1
16
17 package transport
18
19 import (
20 "context"
21 "fmt"
22 "net"
23 "net/http"
24 "strings"
25 "sync"
26 "time"
27
28 utilnet "k8s.io/apimachinery/pkg/util/net"
29 "k8s.io/apimachinery/pkg/util/wait"
30 "k8s.io/client-go/tools/metrics"
31 )
32
33
34
35
36 type tlsTransportCache struct {
37 mu sync.Mutex
38 transports map[tlsCacheKey]*http.Transport
39 }
40
41
42
43
44 var DialerStopCh = wait.NeverStop
45
46 const idleConnsPerHost = 25
47
48 var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)}
49
50 type tlsCacheKey struct {
51 insecure bool
52 caData string
53 certData string
54 keyData string `datapolicy:"security-key"`
55 certFile string
56 keyFile string
57 serverName string
58 nextProtos string
59 disableCompression bool
60
61 getCert *GetCertHolder
62 dial *DialHolder
63 }
64
65 func (t tlsCacheKey) String() string {
66 keyText := "<none>"
67 if len(t.keyData) > 0 {
68 keyText = "<redacted>"
69 }
70 return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t, getCert:%p, dial:%p",
71 t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression, t.getCert, t.dial)
72 }
73
74 func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
75 key, canCache, err := tlsConfigKey(config)
76 if err != nil {
77 return nil, err
78 }
79
80 if canCache {
81
82 c.mu.Lock()
83 defer c.mu.Unlock()
84 defer metrics.TransportCacheEntries.Observe(len(c.transports))
85
86
87 if t, ok := c.transports[key]; ok {
88 metrics.TransportCreateCalls.Increment("hit")
89 return t, nil
90 }
91 metrics.TransportCreateCalls.Increment("miss")
92 } else {
93 metrics.TransportCreateCalls.Increment("uncacheable")
94 }
95
96
97 tlsConfig, err := TLSConfigFor(config)
98 if err != nil {
99 return nil, err
100 }
101
102 if tlsConfig == nil && config.DialHolder == nil && config.Proxy == nil {
103 return http.DefaultTransport, nil
104 }
105
106 var dial func(ctx context.Context, network, address string) (net.Conn, error)
107 if config.DialHolder != nil {
108 dial = config.DialHolder.Dial
109 } else {
110 dial = (&net.Dialer{
111 Timeout: 30 * time.Second,
112 KeepAlive: 30 * time.Second,
113 }).DialContext
114 }
115
116
117
118 if config.TLS.ReloadTLSFiles && tlsConfig != nil && tlsConfig.GetClientCertificate != nil {
119 dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial)
120 tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate
121 dial = dynamicCertDialer.connDialer.DialContext
122 go dynamicCertDialer.Run(DialerStopCh)
123 }
124
125 proxy := http.ProxyFromEnvironment
126 if config.Proxy != nil {
127 proxy = config.Proxy
128 }
129
130 transport := utilnet.SetTransportDefaults(&http.Transport{
131 Proxy: proxy,
132 TLSHandshakeTimeout: 10 * time.Second,
133 TLSClientConfig: tlsConfig,
134 MaxIdleConnsPerHost: idleConnsPerHost,
135 DialContext: dial,
136 DisableCompression: config.DisableCompression,
137 })
138
139 if canCache {
140
141 c.transports[key] = transport
142 }
143
144 return transport, nil
145 }
146
147
148 func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) {
149
150 if err := loadTLSFiles(c); err != nil {
151 return tlsCacheKey{}, false, err
152 }
153
154 if c.Proxy != nil {
155
156 return tlsCacheKey{}, false, nil
157 }
158
159 k := tlsCacheKey{
160 insecure: c.TLS.Insecure,
161 caData: string(c.TLS.CAData),
162 serverName: c.TLS.ServerName,
163 nextProtos: strings.Join(c.TLS.NextProtos, ","),
164 disableCompression: c.DisableCompression,
165 getCert: c.TLS.GetCertHolder,
166 dial: c.DialHolder,
167 }
168
169 if c.TLS.ReloadTLSFiles {
170 k.certFile = c.TLS.CertFile
171 k.keyFile = c.TLS.KeyFile
172 } else {
173 k.certData = string(c.TLS.CertData)
174 k.keyData = string(c.TLS.KeyData)
175 }
176
177 return k, true, nil
178 }
179
View as plain text