blob: c19739fdfe8636bb0118c2368e1375ec396610bf [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -07001/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package transport
18
19import (
20 "crypto/tls"
21 "crypto/x509"
22 "fmt"
23 "io/ioutil"
24 "net/http"
25)
26
27// New returns an http.RoundTripper that will provide the authentication
28// or transport level security defined by the provided Config.
29func New(config *Config) (http.RoundTripper, error) {
30 // Set transport level security
31 if config.Transport != nil && (config.HasCA() || config.HasCertAuth() || config.HasCertCallback() || config.TLS.Insecure) {
32 return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
33 }
34
35 var (
36 rt http.RoundTripper
37 err error
38 )
39
40 if config.Transport != nil {
41 rt = config.Transport
42 } else {
43 rt, err = tlsCache.get(config)
44 if err != nil {
45 return nil, err
46 }
47 }
48
49 return HTTPWrappersForConfig(config, rt)
50}
51
52// TLSConfigFor returns a tls.Config that will provide the transport level security defined
53// by the provided Config. Will return nil if no transport level security is requested.
54func TLSConfigFor(c *Config) (*tls.Config, error) {
55 if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0) {
56 return nil, nil
57 }
58 if c.HasCA() && c.TLS.Insecure {
59 return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
60 }
61 if err := loadTLSFiles(c); err != nil {
62 return nil, err
63 }
64
65 tlsConfig := &tls.Config{
66 // Can't use SSLv3 because of POODLE and BEAST
67 // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
68 // Can't use TLSv1.1 because of RC4 cipher usage
69 MinVersion: tls.VersionTLS12,
70 InsecureSkipVerify: c.TLS.Insecure,
71 ServerName: c.TLS.ServerName,
72 }
73
74 if c.HasCA() {
75 tlsConfig.RootCAs = rootCertPool(c.TLS.CAData)
76 }
77
78 var staticCert *tls.Certificate
79 if c.HasCertAuth() {
80 // If key/cert were provided, verify them before setting up
81 // tlsConfig.GetClientCertificate.
82 cert, err := tls.X509KeyPair(c.TLS.CertData, c.TLS.KeyData)
83 if err != nil {
84 return nil, err
85 }
86 staticCert = &cert
87 }
88
89 if c.HasCertAuth() || c.HasCertCallback() {
90 tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
91 // Note: static key/cert data always take precedence over cert
92 // callback.
93 if staticCert != nil {
94 return staticCert, nil
95 }
96 if c.HasCertCallback() {
97 cert, err := c.TLS.GetCert()
98 if err != nil {
99 return nil, err
100 }
101 // GetCert may return empty value, meaning no cert.
102 if cert != nil {
103 return cert, nil
104 }
105 }
106
107 // Both c.TLS.CertData/KeyData were unset and GetCert didn't return
108 // anything. Return an empty tls.Certificate, no client cert will
109 // be sent to the server.
110 return &tls.Certificate{}, nil
111 }
112 }
113
114 return tlsConfig, nil
115}
116
117// loadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
118// KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
119// either populated or were empty to start.
120func loadTLSFiles(c *Config) error {
121 var err error
122 c.TLS.CAData, err = dataFromSliceOrFile(c.TLS.CAData, c.TLS.CAFile)
123 if err != nil {
124 return err
125 }
126
127 c.TLS.CertData, err = dataFromSliceOrFile(c.TLS.CertData, c.TLS.CertFile)
128 if err != nil {
129 return err
130 }
131
132 c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
133 if err != nil {
134 return err
135 }
136 return nil
137}
138
139// dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
140// or an error if an error occurred reading the file
141func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
142 if len(data) > 0 {
143 return data, nil
144 }
145 if len(file) > 0 {
146 fileData, err := ioutil.ReadFile(file)
147 if err != nil {
148 return []byte{}, err
149 }
150 return fileData, nil
151 }
152 return nil, nil
153}
154
155// rootCertPool returns nil if caData is empty. When passed along, this will mean "use system CAs".
156// When caData is not empty, it will be the ONLY information used in the CertPool.
157func rootCertPool(caData []byte) *x509.CertPool {
158 // What we really want is a copy of x509.systemRootsPool, but that isn't exposed. It's difficult to build (see the go
159 // code for a look at the platform specific insanity), so we'll use the fact that RootCAs == nil gives us the system values
160 // It doesn't allow trusting either/or, but hopefully that won't be an issue
161 if len(caData) == 0 {
162 return nil
163 }
164
165 // if we have caData, use it
166 certPool := x509.NewCertPool()
167 certPool.AppendCertsFromPEM(caData)
168 return certPool
169}