k8s.io/client-go@v0.22.2/transport/cache.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package transport
    18  
    19  import (
    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.
    34  type tlsTransportCache struct {
    35  	mu         sync.Mutex
    36  	transports map[tlsCacheKey]*http.Transport
    37  }
    38  
    39  const idleConnsPerHost = 25
    40  
    41  var tlsCache = &tlsTransportCache{transports: make(map[tlsCacheKey]*http.Transport)}
    42  
    43  type tlsCacheKey struct {
    44  	insecure           bool
    45  	caData             string
    46  	certData           string
    47  	keyData            string `datapolicy:"security-key"`
    48  	certFile           string
    49  	keyFile            string
    50  	serverName         string
    51  	nextProtos         string
    52  	disableCompression bool
    53  }
    54  
    55  func (t tlsCacheKey) String() string {
    56  	keyText := "<none>"
    57  	if len(t.keyData) > 0 {
    58  		keyText = "<redacted>"
    59  	}
    60  	return fmt.Sprintf("insecure:%v, caData:%#v, certData:%#v, keyData:%s, serverName:%s, disableCompression:%t", t.insecure, t.caData, t.certData, keyText, t.serverName, t.disableCompression)
    61  }
    62  
    63  func (c *tlsTransportCache) get(config *Config) (http.RoundTripper, error) {
    64  	key, canCache, err := tlsConfigKey(config)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	if canCache {
    70  		// Ensure we only create a single transport for the given TLS options
    71  		c.mu.Lock()
    72  		defer c.mu.Unlock()
    73  
    74  		// See if we already have a custom transport for this config
    75  		if t, ok := c.transports[key]; ok {
    76  			return t, nil
    77  		}
    78  	}
    79  
    80  	// Get the TLS options for this client config
    81  	tlsConfig, err := TLSConfigFor(config)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  	// The options didn't require a custom TLS config
    86  	if tlsConfig == nil && config.Dial == nil && config.Proxy == nil {
    87  		return http.DefaultTransport, nil
    88  	}
    89  
    90  	dial := config.Dial
    91  	if dial == nil {
    92  		dial = (&net.Dialer{
    93  			Timeout:   30 * time.Second,
    94  			KeepAlive: 30 * time.Second,
    95  		}).DialContext
    96  	}
    97  
    98  	// If we use are reloading files, we need to handle certificate rotation properly
    99  	// TODO(jackkleeman): We can also add rotation here when config.HasCertCallback() is true
   100  	if config.TLS.ReloadTLSFiles {
   101  		dynamicCertDialer := certRotatingDialer(tlsConfig.GetClientCertificate, dial)
   102  		tlsConfig.GetClientCertificate = dynamicCertDialer.GetClientCertificate
   103  		dial = dynamicCertDialer.connDialer.DialContext
   104  		go dynamicCertDialer.Run(wait.NeverStop)
   105  	}
   106  
   107  	proxy := http.ProxyFromEnvironment
   108  	if config.Proxy != nil {
   109  		proxy = config.Proxy
   110  	}
   111  
   112  	transport := utilnet.SetTransportDefaults(&http.Transport{
   113  		Proxy:               proxy,
   114  		TLSHandshakeTimeout: 10 * time.Second,
   115  		TLSClientConfig:     tlsConfig,
   116  		MaxIdleConnsPerHost: idleConnsPerHost,
   117  		DialContext:         dial,
   118  		DisableCompression:  config.DisableCompression,
   119  	})
   120  
   121  	if canCache {
   122  		// Cache a single transport for these options
   123  		c.transports[key] = transport
   124  	}
   125  
   126  	return transport, nil
   127  }
   128  
   129  // tlsConfigKey returns a unique key for tls.Config objects returned from TLSConfigFor
   130  func tlsConfigKey(c *Config) (tlsCacheKey, bool, error) {
   131  	// Make sure ca/key/cert content is loaded
   132  	if err := loadTLSFiles(c); err != nil {
   133  		return tlsCacheKey{}, false, err
   134  	}
   135  
   136  	if c.TLS.GetCert != nil || c.Dial != nil || c.Proxy != nil {
   137  		// cannot determine equality for functions
   138  		return tlsCacheKey{}, false, nil
   139  	}
   140  
   141  	k := tlsCacheKey{
   142  		insecure:           c.TLS.Insecure,
   143  		caData:             string(c.TLS.CAData),
   144  		serverName:         c.TLS.ServerName,
   145  		nextProtos:         strings.Join(c.TLS.NextProtos, ","),
   146  		disableCompression: c.DisableCompression,
   147  	}
   148  
   149  	if c.TLS.ReloadTLSFiles {
   150  		k.certFile = c.TLS.CertFile
   151  		k.keyFile = c.TLS.KeyFile
   152  	} else {
   153  		k.certData = string(c.TLS.CertData)
   154  		k.keyData = string(c.TLS.KeyData)
   155  	}
   156  
   157  	return k, true, nil
   158  }