k8s.io/apiserver@v0.31.1/pkg/server/secure_serving.go (about)

     1  /*
     2  Copyright 2016 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 server
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"fmt"
    23  	"io"
    24  	"log"
    25  	"net"
    26  	"net/http"
    27  	"os"
    28  	"strings"
    29  	"time"
    30  
    31  	"golang.org/x/net/http2"
    32  	"k8s.io/component-base/cli/flag"
    33  	"k8s.io/klog/v2"
    34  
    35  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    36  	"k8s.io/apiserver/pkg/endpoints/metrics"
    37  	"k8s.io/apiserver/pkg/server/dynamiccertificates"
    38  )
    39  
    40  const (
    41  	defaultKeepAlivePeriod = 3 * time.Minute
    42  )
    43  
    44  // tlsConfig produces the tls.Config to serve with.
    45  func (s *SecureServingInfo) tlsConfig(stopCh <-chan struct{}) (*tls.Config, error) {
    46  	tlsConfig := &tls.Config{
    47  		// Can't use SSLv3 because of POODLE and BEAST
    48  		// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
    49  		// Can't use TLSv1.1 because of RC4 cipher usage
    50  		MinVersion: tls.VersionTLS12,
    51  		// enable HTTP2 for go's 1.7 HTTP Server
    52  		NextProtos: []string{"h2", "http/1.1"},
    53  	}
    54  
    55  	// these are static aspects of the tls.Config
    56  	if s.DisableHTTP2 {
    57  		klog.Info("Forcing use of http/1.1 only")
    58  		tlsConfig.NextProtos = []string{"http/1.1"}
    59  	}
    60  	if s.MinTLSVersion > 0 {
    61  		tlsConfig.MinVersion = s.MinTLSVersion
    62  	}
    63  	if len(s.CipherSuites) > 0 {
    64  		tlsConfig.CipherSuites = s.CipherSuites
    65  		insecureCiphers := flag.InsecureTLSCiphers()
    66  		for i := 0; i < len(s.CipherSuites); i++ {
    67  			for cipherName, cipherID := range insecureCiphers {
    68  				if s.CipherSuites[i] == cipherID {
    69  					klog.Warningf("Use of insecure cipher '%s' detected.", cipherName)
    70  				}
    71  			}
    72  		}
    73  	}
    74  
    75  	if s.ClientCA != nil {
    76  		// Populate PeerCertificates in requests, but don't reject connections without certificates
    77  		// This allows certificates to be validated by authenticators, while still allowing other auth types
    78  		tlsConfig.ClientAuth = tls.RequestClientCert
    79  	}
    80  
    81  	if s.ClientCA != nil || s.Cert != nil || len(s.SNICerts) > 0 {
    82  		dynamicCertificateController := dynamiccertificates.NewDynamicServingCertificateController(
    83  			tlsConfig,
    84  			s.ClientCA,
    85  			s.Cert,
    86  			s.SNICerts,
    87  			nil, // TODO see how to plumb an event recorder down in here. For now this results in simply klog messages.
    88  		)
    89  
    90  		if s.ClientCA != nil {
    91  			s.ClientCA.AddListener(dynamicCertificateController)
    92  		}
    93  		if s.Cert != nil {
    94  			s.Cert.AddListener(dynamicCertificateController)
    95  		}
    96  		// generate a context from stopCh. This is to avoid modifying files which are relying on apiserver
    97  		// TODO: See if we can pass ctx to the current method
    98  		ctx, cancel := context.WithCancel(context.Background())
    99  		go func() {
   100  			select {
   101  			case <-stopCh:
   102  				cancel() // stopCh closed, so cancel our context
   103  			case <-ctx.Done():
   104  			}
   105  		}()
   106  		// start controllers if possible
   107  		if controller, ok := s.ClientCA.(dynamiccertificates.ControllerRunner); ok {
   108  			// runonce to try to prime data.  If this fails, it's ok because we fail closed.
   109  			// Files are required to be populated already, so this is for convenience.
   110  			if err := controller.RunOnce(ctx); err != nil {
   111  				klog.Warningf("Initial population of client CA failed: %v", err)
   112  			}
   113  
   114  			go controller.Run(ctx, 1)
   115  		}
   116  		if controller, ok := s.Cert.(dynamiccertificates.ControllerRunner); ok {
   117  			// runonce to try to prime data.  If this fails, it's ok because we fail closed.
   118  			// Files are required to be populated already, so this is for convenience.
   119  			if err := controller.RunOnce(ctx); err != nil {
   120  				klog.Warningf("Initial population of default serving certificate failed: %v", err)
   121  			}
   122  
   123  			go controller.Run(ctx, 1)
   124  		}
   125  		for _, sniCert := range s.SNICerts {
   126  			sniCert.AddListener(dynamicCertificateController)
   127  			if controller, ok := sniCert.(dynamiccertificates.ControllerRunner); ok {
   128  				// runonce to try to prime data.  If this fails, it's ok because we fail closed.
   129  				// Files are required to be populated already, so this is for convenience.
   130  				if err := controller.RunOnce(ctx); err != nil {
   131  					klog.Warningf("Initial population of SNI serving certificate failed: %v", err)
   132  				}
   133  
   134  				go controller.Run(ctx, 1)
   135  			}
   136  		}
   137  
   138  		// runonce to try to prime data.  If this fails, it's ok because we fail closed.
   139  		// Files are required to be populated already, so this is for convenience.
   140  		if err := dynamicCertificateController.RunOnce(); err != nil {
   141  			klog.Warningf("Initial population of dynamic certificates failed: %v", err)
   142  		}
   143  		go dynamicCertificateController.Run(1, stopCh)
   144  
   145  		tlsConfig.GetConfigForClient = dynamicCertificateController.GetConfigForClient
   146  	}
   147  
   148  	return tlsConfig, nil
   149  }
   150  
   151  // Serve runs the secure http server. It fails only if certificates cannot be loaded or the initial listen call fails.
   152  // The actual server loop (stoppable by closing stopCh) runs in a go routine, i.e. Serve does not block.
   153  // It returns a stoppedCh that is closed when all non-hijacked active requests have been processed.
   154  // It returns a listenerStoppedCh that is closed when the underlying http Server has stopped listening.
   155  func (s *SecureServingInfo) Serve(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) (<-chan struct{}, <-chan struct{}, error) {
   156  	if s.Listener == nil {
   157  		return nil, nil, fmt.Errorf("listener must not be nil")
   158  	}
   159  
   160  	tlsConfig, err := s.tlsConfig(stopCh)
   161  	if err != nil {
   162  		return nil, nil, err
   163  	}
   164  
   165  	secureServer := &http.Server{
   166  		Addr:           s.Listener.Addr().String(),
   167  		Handler:        handler,
   168  		MaxHeaderBytes: 1 << 20,
   169  		TLSConfig:      tlsConfig,
   170  
   171  		IdleTimeout:       90 * time.Second, // matches http.DefaultTransport keep-alive timeout
   172  		ReadHeaderTimeout: 32 * time.Second, // just shy of requestTimeoutUpperBound
   173  	}
   174  
   175  	// At least 99% of serialized resources in surveyed clusters were smaller than 256kb.
   176  	// This should be big enough to accommodate most API POST requests in a single frame,
   177  	// and small enough to allow a per connection buffer of this size multiplied by `MaxConcurrentStreams`.
   178  	const resourceBody99Percentile = 256 * 1024
   179  
   180  	http2Options := &http2.Server{
   181  		IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
   182  	}
   183  
   184  	// shrink the per-stream buffer and max framesize from the 1MB default while still accommodating most API POST requests in a single frame
   185  	http2Options.MaxUploadBufferPerStream = resourceBody99Percentile
   186  	http2Options.MaxReadFrameSize = resourceBody99Percentile
   187  
   188  	// use the overridden concurrent streams setting or make the default of 250 explicit so we can size MaxUploadBufferPerConnection appropriately
   189  	if s.HTTP2MaxStreamsPerConnection > 0 {
   190  		http2Options.MaxConcurrentStreams = uint32(s.HTTP2MaxStreamsPerConnection)
   191  	} else {
   192  		// match http2.initialMaxConcurrentStreams used by clients
   193  		// this makes it so that a malicious client can only open 400 streams before we forcibly close the connection
   194  		// https://github.com/golang/net/commit/b225e7ca6dde1ef5a5ae5ce922861bda011cfabd
   195  		http2Options.MaxConcurrentStreams = 100
   196  	}
   197  
   198  	// increase the connection buffer size from the 1MB default to handle the specified number of concurrent streams
   199  	http2Options.MaxUploadBufferPerConnection = http2Options.MaxUploadBufferPerStream * int32(http2Options.MaxConcurrentStreams)
   200  
   201  	if !s.DisableHTTP2 {
   202  		// apply settings to the server
   203  		if err := http2.ConfigureServer(secureServer, http2Options); err != nil {
   204  			return nil, nil, fmt.Errorf("error configuring http2: %v", err)
   205  		}
   206  	}
   207  
   208  	// use tlsHandshakeErrorWriter to handle messages of tls handshake error
   209  	tlsErrorWriter := &tlsHandshakeErrorWriter{os.Stderr}
   210  	tlsErrorLogger := log.New(tlsErrorWriter, "", 0)
   211  	secureServer.ErrorLog = tlsErrorLogger
   212  
   213  	klog.Infof("Serving securely on %s", secureServer.Addr)
   214  	return RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)
   215  }
   216  
   217  // RunServer spawns a go-routine continuously serving until the stopCh is
   218  // closed.
   219  // It returns a stoppedCh that is closed when all non-hijacked active requests
   220  // have been processed.
   221  // This function does not block
   222  // TODO: make private when insecure serving is gone from the kube-apiserver
   223  func RunServer(
   224  	server *http.Server,
   225  	ln net.Listener,
   226  	shutDownTimeout time.Duration,
   227  	stopCh <-chan struct{},
   228  ) (<-chan struct{}, <-chan struct{}, error) {
   229  	if ln == nil {
   230  		return nil, nil, fmt.Errorf("listener must not be nil")
   231  	}
   232  
   233  	// Shutdown server gracefully.
   234  	serverShutdownCh, listenerStoppedCh := make(chan struct{}), make(chan struct{})
   235  	go func() {
   236  		defer close(serverShutdownCh)
   237  		<-stopCh
   238  		ctx, cancel := context.WithTimeout(context.Background(), shutDownTimeout)
   239  		server.Shutdown(ctx)
   240  		cancel()
   241  	}()
   242  
   243  	go func() {
   244  		defer utilruntime.HandleCrash()
   245  		defer close(listenerStoppedCh)
   246  
   247  		var listener net.Listener
   248  		listener = tcpKeepAliveListener{ln}
   249  		if server.TLSConfig != nil {
   250  			listener = tls.NewListener(listener, server.TLSConfig)
   251  		}
   252  
   253  		err := server.Serve(listener)
   254  
   255  		msg := fmt.Sprintf("Stopped listening on %s", ln.Addr().String())
   256  		select {
   257  		case <-stopCh:
   258  			klog.Info(msg)
   259  		default:
   260  			panic(fmt.Sprintf("%s due to error: %v", msg, err))
   261  		}
   262  	}()
   263  
   264  	return serverShutdownCh, listenerStoppedCh, nil
   265  }
   266  
   267  // tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
   268  // connections. It's used by ListenAndServe and ListenAndServeTLS so
   269  // dead TCP connections (e.g. closing laptop mid-download) eventually
   270  // go away.
   271  //
   272  // Copied from Go 1.7.2 net/http/server.go
   273  type tcpKeepAliveListener struct {
   274  	net.Listener
   275  }
   276  
   277  func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
   278  	c, err := ln.Listener.Accept()
   279  	if err != nil {
   280  		return nil, err
   281  	}
   282  	if tc, ok := c.(*net.TCPConn); ok {
   283  		tc.SetKeepAlive(true)
   284  		tc.SetKeepAlivePeriod(defaultKeepAlivePeriod)
   285  	}
   286  	return c, nil
   287  }
   288  
   289  // tlsHandshakeErrorWriter writes TLS handshake errors to klog with
   290  // trace level - V(5), to avoid flooding of tls handshake errors.
   291  type tlsHandshakeErrorWriter struct {
   292  	out io.Writer
   293  }
   294  
   295  const tlsHandshakeErrorPrefix = "http: TLS handshake error"
   296  
   297  func (w *tlsHandshakeErrorWriter) Write(p []byte) (int, error) {
   298  	if strings.Contains(string(p), tlsHandshakeErrorPrefix) {
   299  		klog.V(5).Info(string(p))
   300  		metrics.TLSHandshakeErrors.Inc()
   301  		return len(p), nil
   302  	}
   303  
   304  	// for non tls handshake error, log it as usual
   305  	return w.out.Write(p)
   306  }