k8s.io/client-go@v0.22.2/transport/transport.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  	"context"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"encoding/pem"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"sync"
    28  	"time"
    29  
    30  	utilnet "k8s.io/apimachinery/pkg/util/net"
    31  	"k8s.io/klog/v2"
    32  )
    33  
    34  // New returns an http.RoundTripper that will provide the authentication
    35  // or transport level security defined by the provided Config.
    36  func New(config *Config) (http.RoundTripper, error) {
    37  	// Set transport level security
    38  	if config.Transport != nil && (config.HasCA() || config.HasCertAuth() || config.HasCertCallback() || config.TLS.Insecure) {
    39  		return nil, fmt.Errorf("using a custom transport with TLS certificate options or the insecure flag is not allowed")
    40  	}
    41  
    42  	var (
    43  		rt  http.RoundTripper
    44  		err error
    45  	)
    46  
    47  	if config.Transport != nil {
    48  		rt = config.Transport
    49  	} else {
    50  		rt, err = tlsCache.get(config)
    51  		if err != nil {
    52  			return nil, err
    53  		}
    54  	}
    55  
    56  	return HTTPWrappersForConfig(config, rt)
    57  }
    58  
    59  // TLSConfigFor returns a tls.Config that will provide the transport level security defined
    60  // by the provided Config. Will return nil if no transport level security is requested.
    61  func TLSConfigFor(c *Config) (*tls.Config, error) {
    62  	if !(c.HasCA() || c.HasCertAuth() || c.HasCertCallback() || c.TLS.Insecure || len(c.TLS.ServerName) > 0 || len(c.TLS.NextProtos) > 0) {
    63  		return nil, nil
    64  	}
    65  	if c.HasCA() && c.TLS.Insecure {
    66  		return nil, fmt.Errorf("specifying a root certificates file with the insecure flag is not allowed")
    67  	}
    68  	if err := loadTLSFiles(c); err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	tlsConfig := &tls.Config{
    73  		// Can't use SSLv3 because of POODLE and BEAST
    74  		// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
    75  		// Can't use TLSv1.1 because of RC4 cipher usage
    76  		MinVersion:         tls.VersionTLS12,
    77  		InsecureSkipVerify: c.TLS.Insecure,
    78  		ServerName:         c.TLS.ServerName,
    79  		NextProtos:         c.TLS.NextProtos,
    80  	}
    81  
    82  	if c.HasCA() {
    83  		rootCAs, err := rootCertPool(c.TLS.CAData)
    84  		if err != nil {
    85  			return nil, fmt.Errorf("unable to load root certificates: %w", err)
    86  		}
    87  		tlsConfig.RootCAs = rootCAs
    88  	}
    89  
    90  	var staticCert *tls.Certificate
    91  	// Treat cert as static if either key or cert was data, not a file
    92  	if c.HasCertAuth() && !c.TLS.ReloadTLSFiles {
    93  		// If key/cert were provided, verify them before setting up
    94  		// tlsConfig.GetClientCertificate.
    95  		cert, err := tls.X509KeyPair(c.TLS.CertData, c.TLS.KeyData)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  		staticCert = &cert
   100  	}
   101  
   102  	var dynamicCertLoader func() (*tls.Certificate, error)
   103  	if c.TLS.ReloadTLSFiles {
   104  		dynamicCertLoader = cachingCertificateLoader(c.TLS.CertFile, c.TLS.KeyFile)
   105  	}
   106  
   107  	if c.HasCertAuth() || c.HasCertCallback() {
   108  		tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   109  			// Note: static key/cert data always take precedence over cert
   110  			// callback.
   111  			if staticCert != nil {
   112  				return staticCert, nil
   113  			}
   114  			// key/cert files lead to ReloadTLSFiles being set - takes precedence over cert callback
   115  			if dynamicCertLoader != nil {
   116  				return dynamicCertLoader()
   117  			}
   118  			if c.HasCertCallback() {
   119  				cert, err := c.TLS.GetCert()
   120  				if err != nil {
   121  					return nil, err
   122  				}
   123  				// GetCert may return empty value, meaning no cert.
   124  				if cert != nil {
   125  					return cert, nil
   126  				}
   127  			}
   128  
   129  			// Both c.TLS.CertData/KeyData were unset and GetCert didn't return
   130  			// anything. Return an empty tls.Certificate, no client cert will
   131  			// be sent to the server.
   132  			return &tls.Certificate{}, nil
   133  		}
   134  	}
   135  
   136  	return tlsConfig, nil
   137  }
   138  
   139  // loadTLSFiles copies the data from the CertFile, KeyFile, and CAFile fields into the CertData,
   140  // KeyData, and CAFile fields, or returns an error. If no error is returned, all three fields are
   141  // either populated or were empty to start.
   142  func loadTLSFiles(c *Config) error {
   143  	var err error
   144  	c.TLS.CAData, err = dataFromSliceOrFile(c.TLS.CAData, c.TLS.CAFile)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	// Check that we are purely loading from files
   150  	if len(c.TLS.CertFile) > 0 && len(c.TLS.CertData) == 0 && len(c.TLS.KeyFile) > 0 && len(c.TLS.KeyData) == 0 {
   151  		c.TLS.ReloadTLSFiles = true
   152  	}
   153  
   154  	c.TLS.CertData, err = dataFromSliceOrFile(c.TLS.CertData, c.TLS.CertFile)
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	c.TLS.KeyData, err = dataFromSliceOrFile(c.TLS.KeyData, c.TLS.KeyFile)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	return nil
   164  }
   165  
   166  // dataFromSliceOrFile returns data from the slice (if non-empty), or from the file,
   167  // or an error if an error occurred reading the file
   168  func dataFromSliceOrFile(data []byte, file string) ([]byte, error) {
   169  	if len(data) > 0 {
   170  		return data, nil
   171  	}
   172  	if len(file) > 0 {
   173  		fileData, err := ioutil.ReadFile(file)
   174  		if err != nil {
   175  			return []byte{}, err
   176  		}
   177  		return fileData, nil
   178  	}
   179  	return nil, nil
   180  }
   181  
   182  // rootCertPool returns nil if caData is empty.  When passed along, this will mean "use system CAs".
   183  // When caData is not empty, it will be the ONLY information used in the CertPool.
   184  func rootCertPool(caData []byte) (*x509.CertPool, error) {
   185  	// What we really want is a copy of x509.systemRootsPool, but that isn't exposed.  It's difficult to build (see the go
   186  	// code for a look at the platform specific insanity), so we'll use the fact that RootCAs == nil gives us the system values
   187  	// It doesn't allow trusting either/or, but hopefully that won't be an issue
   188  	if len(caData) == 0 {
   189  		return nil, nil
   190  	}
   191  
   192  	// if we have caData, use it
   193  	certPool := x509.NewCertPool()
   194  	if ok := certPool.AppendCertsFromPEM(caData); !ok {
   195  		return nil, createErrorParsingCAData(caData)
   196  	}
   197  	return certPool, nil
   198  }
   199  
   200  // createErrorParsingCAData ALWAYS returns an error.  We call it because know we failed to AppendCertsFromPEM
   201  // but we don't know the specific error because that API is just true/false
   202  func createErrorParsingCAData(pemCerts []byte) error {
   203  	for len(pemCerts) > 0 {
   204  		var block *pem.Block
   205  		block, pemCerts = pem.Decode(pemCerts)
   206  		if block == nil {
   207  			return fmt.Errorf("unable to parse bytes as PEM block")
   208  		}
   209  
   210  		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
   211  			continue
   212  		}
   213  
   214  		if _, err := x509.ParseCertificate(block.Bytes); err != nil {
   215  			return fmt.Errorf("failed to parse certificate: %w", err)
   216  		}
   217  	}
   218  	return fmt.Errorf("no valid certificate authority data seen")
   219  }
   220  
   221  // WrapperFunc wraps an http.RoundTripper when a new transport
   222  // is created for a client, allowing per connection behavior
   223  // to be injected.
   224  type WrapperFunc func(rt http.RoundTripper) http.RoundTripper
   225  
   226  // Wrappers accepts any number of wrappers and returns a wrapper
   227  // function that is the equivalent of calling each of them in order. Nil
   228  // values are ignored, which makes this function convenient for incrementally
   229  // wrapping a function.
   230  func Wrappers(fns ...WrapperFunc) WrapperFunc {
   231  	if len(fns) == 0 {
   232  		return nil
   233  	}
   234  	// optimize the common case of wrapping a possibly nil transport wrapper
   235  	// with an additional wrapper
   236  	if len(fns) == 2 && fns[0] == nil {
   237  		return fns[1]
   238  	}
   239  	return func(rt http.RoundTripper) http.RoundTripper {
   240  		base := rt
   241  		for _, fn := range fns {
   242  			if fn != nil {
   243  				base = fn(base)
   244  			}
   245  		}
   246  		return base
   247  	}
   248  }
   249  
   250  // ContextCanceller prevents new requests after the provided context is finished.
   251  // err is returned when the context is closed, allowing the caller to provide a context
   252  // appropriate error.
   253  func ContextCanceller(ctx context.Context, err error) WrapperFunc {
   254  	return func(rt http.RoundTripper) http.RoundTripper {
   255  		return &contextCanceller{
   256  			ctx: ctx,
   257  			rt:  rt,
   258  			err: err,
   259  		}
   260  	}
   261  }
   262  
   263  type contextCanceller struct {
   264  	ctx context.Context
   265  	rt  http.RoundTripper
   266  	err error
   267  }
   268  
   269  func (b *contextCanceller) RoundTrip(req *http.Request) (*http.Response, error) {
   270  	select {
   271  	case <-b.ctx.Done():
   272  		return nil, b.err
   273  	default:
   274  		return b.rt.RoundTrip(req)
   275  	}
   276  }
   277  
   278  func tryCancelRequest(rt http.RoundTripper, req *http.Request) {
   279  	type canceler interface {
   280  		CancelRequest(*http.Request)
   281  	}
   282  	switch rt := rt.(type) {
   283  	case canceler:
   284  		rt.CancelRequest(req)
   285  	case utilnet.RoundTripperWrapper:
   286  		tryCancelRequest(rt.WrappedRoundTripper(), req)
   287  	default:
   288  		klog.Warningf("Unable to cancel request for %T", rt)
   289  	}
   290  }
   291  
   292  type certificateCacheEntry struct {
   293  	cert  *tls.Certificate
   294  	err   error
   295  	birth time.Time
   296  }
   297  
   298  // isStale returns true when this cache entry is too old to be usable
   299  func (c *certificateCacheEntry) isStale() bool {
   300  	return time.Since(c.birth) > time.Second
   301  }
   302  
   303  func newCertificateCacheEntry(certFile, keyFile string) certificateCacheEntry {
   304  	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
   305  	return certificateCacheEntry{cert: &cert, err: err, birth: time.Now()}
   306  }
   307  
   308  // cachingCertificateLoader ensures that we don't hammer the filesystem when opening many connections
   309  // the underlying cert files are read at most once every second
   310  func cachingCertificateLoader(certFile, keyFile string) func() (*tls.Certificate, error) {
   311  	current := newCertificateCacheEntry(certFile, keyFile)
   312  	var currentMtx sync.RWMutex
   313  
   314  	return func() (*tls.Certificate, error) {
   315  		currentMtx.RLock()
   316  		if current.isStale() {
   317  			currentMtx.RUnlock()
   318  
   319  			currentMtx.Lock()
   320  			defer currentMtx.Unlock()
   321  
   322  			if current.isStale() {
   323  				current = newCertificateCacheEntry(certFile, keyFile)
   324  			}
   325  		} else {
   326  			defer currentMtx.RUnlock()
   327  		}
   328  
   329  		return current.cert, current.err
   330  	}
   331  }