blob: 3ec4e19357de01c13446a20b72dad6c1e710b105 [file] [log] [blame]
Matteo Scandoloa4285862020-12-01 18:10:10 -08001/*
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 "fmt"
21 "net"
22 "net/http"
23 "strings"
24 "sync"
25 "time"
26
27 utilnet "k8s.io/apimachinery/pkg/util/net"
28 "k8s.io/apimachinery/pkg/util/wait"
29)
30
31// TlsTransportCache caches TLS http.RoundTrippers different configurations. The
32// same RoundTripper will be returned for configs with identical TLS options If
33// the config has no custom TLS options, http.DefaultTransport is returned.
34type tlsTransportCache struct {
35 mu sync.Mutex
36 transports map[tlsCacheKey]*http.Transport
37}
38
39const idleConnsPerHost = 25
40
41var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)}
42
43type tlsCacheKey struct {
44 insecure bool
45 caData string
46 certData string
47 keyData string
48 certFile string
49 keyFile string
50 getCert string
51 serverName string
52 nextProtos string
53 dial string
54 disableCompression bool
55 proxy string
56}
57
58func (t tlsCacheKey) String() string {
59 keyText := "<none>"
60 if len(t.keyData) > 0 {
61 keyText = "<redacted>"
62 }
63 return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, getCert: %s, serverName:%s, dial:%s disableCompression:%t, proxy: %s", t.insecure, t.caData, t.certData, keyText, t.getCert, t.serverName, t.dial, t.disableCompression, t.proxy)
64}
65
66func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
67 key, err := tlsConfigKey(config)
68 if err != nil {
69 return nil, err
70 }
71
72 // Ensure we only create a single transport for the given TLS options
73 c.mu.Lock()
74 defer c.mu.Unlock()
75
76 // See if we already have a custom transport for this config
77 if t, ok := c.transports[key]; ok {
78 return t, nil
79 }
80
81 // Get the TLS options for this client config
82 tlsConfig, err := TLSConfigFor(config)
83 if err != nil {
84 return nil, err
85 }
86 // The options didn't require a custom TLS config
87 if tlsConfig == nil && config.Dial == nil && config.Proxy == nil {
88 return http.DefaultTransport, nil
89 }
90
91 dial := config.Dial
92 if dial == nil {
93 dial = (&net.Dialer{
94 Timeout: 30 * time.Second,
95 KeepAlive: 30 * time.Second,
96 }).DialContext
97 }
98
99 // If we use are reloading files, we need to handle certificate rotation properly
100 // TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true
101 if config.TLS.ReloadTLSFiles {
102 dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial)
103 tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate
104 dial = dynamicCertDialer.connDialer.DialContext
105 go dynamicCertDialer.Run(wait.NeverStop)
106 }
107
108 proxy := http.ProxyFromEnvironment
109 if config.Proxy != nil {
110 proxy = config.Proxy
111 }
112
113 // Cache a single transport for these options
114 c.transports[key] = utilnet.SetTransportDefaults(&http.Transport{
115 Proxy: proxy,
116 TLSHandshakeTimeout: 10 * time.Second,
117 TLSClientConfig: tlsConfig,
118 MaxIdleConnsPerHost: idleConnsPerHost,
119 DialContext: dial,
120 DisableCompression: config.DisableCompression,
121 })
122 return c.transports[key], nil
123}
124
125// tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor
126func tlsConfigKey(c *Config) (tlsCacheKey, error) {
127 // Make sure ca/key/cert content is loaded
128 if err := loadTLSFiles(c); err != nil {
129 return tlsCacheKey{}, err
130 }
131 k := tlsCacheKey{
132 insecure: c.TLS.Insecure,
133 caData: string(c.TLS.CAData),
134 getCert: fmt.Sprintf("%p", c.TLS.GetCert),
135 serverName: c.TLS.ServerName,
136 nextProtos: strings.Join(c.TLS.NextProtos, ","),
137 dial: fmt.Sprintf("%p", c.Dial),
138 disableCompression: c.DisableCompression,
139 proxy: fmt.Sprintf("%p", c.Proxy),
140 }
141
142 if c.TLS.ReloadTLSFiles {
143 k.certFile = c.TLS.CertFile
144 k.keyFile = c.TLS.KeyFile
145 } else {
146 k.certData = string(c.TLS.CertData)
147 k.keyData = string(c.TLS.KeyData)
148 }
149
150 return k, nil
151}