k3s/vendor/k8s.io/apiserver/pkg/server/secure_serving.go

360 lines
13 KiB
Go
Raw Normal View History

2019-01-12 04:58:27 +00:00
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"context"
"crypto/tls"
"fmt"
2020-08-10 17:43:49 +00:00
"io"
"log"
2019-01-12 04:58:27 +00:00
"net"
"net/http"
2020-08-10 17:43:49 +00:00
"os"
"strings"
2019-01-12 04:58:27 +00:00
"time"
"golang.org/x/net/http2"
2020-08-10 17:43:49 +00:00
"k8s.io/component-base/cli/flag"
"k8s.io/klog/v2"
2019-01-12 04:58:27 +00:00
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2020-08-10 17:43:49 +00:00
"k8s.io/apiserver/pkg/endpoints/metrics"
2019-12-12 01:27:03 +00:00
"k8s.io/apiserver/pkg/server/dynamiccertificates"
2019-01-12 04:58:27 +00:00
)
const (
defaultKeepAlivePeriod = 3 * time.Minute
)
2019-12-12 01:27:03 +00:00
// tlsConfig produces the tls.Config to serve with.
func (s *SecureServingInfo) tlsConfig(stopCh <-chan struct{}) (*tls.Config, error) {
tlsConfig := &tls.Config{
// Can't use SSLv3 because of POODLE and BEAST
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
// Can't use TLSv1.1 because of RC4 cipher usage
MinVersion: tls.VersionTLS12,
// enable HTTP2 for go's 1.7 HTTP Server
NextProtos: []string{"h2", "http/1.1"},
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
// these are static aspects of the tls.Config
2019-09-27 21:51:53 +00:00
if s.DisableHTTP2 {
klog.Info("Forcing use of http/1.1 only")
2019-12-12 01:27:03 +00:00
tlsConfig.NextProtos = []string{"http/1.1"}
2019-09-27 21:51:53 +00:00
}
2019-01-12 04:58:27 +00:00
if s.MinTLSVersion > 0 {
2019-12-12 01:27:03 +00:00
tlsConfig.MinVersion = s.MinTLSVersion
2019-01-12 04:58:27 +00:00
}
if len(s.CipherSuites) > 0 {
2019-12-12 01:27:03 +00:00
tlsConfig.CipherSuites = s.CipherSuites
2020-08-10 17:43:49 +00:00
insecureCiphers := flag.InsecureTLSCiphers()
for i := 0; i < len(s.CipherSuites); i++ {
for cipherName, cipherID := range insecureCiphers {
if s.CipherSuites[i] == cipherID {
klog.Warningf("Use of insecure cipher '%s' detected.", cipherName)
}
}
}
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
if s.ClientCA != nil {
// Populate PeerCertificates in requests, but don't reject connections without certificates
// This allows certificates to be validated by authenticators, while still allowing other auth types
tlsConfig.ClientAuth = tls.RequestClientCert
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
if s.ClientCA != nil || s.Cert != nil || len(s.SNICerts) > 0 {
dynamicCertificateController := dynamiccertificates.NewDynamicServingCertificateController(
2020-03-26 21:07:15 +00:00
tlsConfig,
2019-12-12 01:27:03 +00:00
s.ClientCA,
s.Cert,
s.SNICerts,
nil, // TODO see how to plumb an event recorder down in here. For now this results in simply klog messages.
)
if s.ClientCA != nil {
s.ClientCA.AddListener(dynamicCertificateController)
2019-12-12 01:27:03 +00:00
}
if s.Cert != nil {
s.Cert.AddListener(dynamicCertificateController)
2019-12-12 01:27:03 +00:00
}
2019-12-12 01:27:03 +00:00
// start controllers if possible
if controller, ok := s.ClientCA.(dynamiccertificates.ControllerRunner); ok {
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := controller.RunOnce(); err != nil {
klog.Warningf("Initial population of client CA failed: %v", err)
}
go controller.Run(1, stopCh)
}
if controller, ok := s.Cert.(dynamiccertificates.ControllerRunner); ok {
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := controller.RunOnce(); err != nil {
klog.Warningf("Initial population of default serving certificate failed: %v", err)
}
go controller.Run(1, stopCh)
}
for _, sniCert := range s.SNICerts {
sniCert.AddListener(dynamicCertificateController)
2019-12-12 01:27:03 +00:00
if controller, ok := sniCert.(dynamiccertificates.ControllerRunner); ok {
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := controller.RunOnce(); err != nil {
klog.Warningf("Initial population of SNI serving certificate failed: %v", err)
}
go controller.Run(1, stopCh)
}
}
// runonce to try to prime data. If this fails, it's ok because we fail closed.
// Files are required to be populated already, so this is for convenience.
if err := dynamicCertificateController.RunOnce(); err != nil {
klog.Warningf("Initial population of dynamic certificates failed: %v", err)
}
go dynamicCertificateController.Run(1, stopCh)
tlsConfig.GetConfigForClient = dynamicCertificateController.GetConfigForClient
2019-01-12 04:58:27 +00:00
}
2019-12-12 01:27:03 +00:00
return tlsConfig, nil
}
// Serve runs the secure http server. It fails only if certificates cannot be loaded or the initial listen call fails.
// The actual server loop (stoppable by closing stopCh) runs in a go routine, i.e. Serve does not block.
// It returns a stoppedCh that is closed when all non-hijacked active requests have been processed.
func (s *SecureServingInfo) Serve(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) (<-chan struct{}, error) {
if s.Listener == nil {
return nil, fmt.Errorf("listener must not be nil")
}
tlsConfig, err := s.tlsConfig(stopCh)
if err != nil {
return nil, err
}
secureServer := &http.Server{
Addr: s.Listener.Addr().String(),
Handler: handler,
MaxHeaderBytes: 1 << 20,
TLSConfig: tlsConfig,
IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
ReadHeaderTimeout: 32 * time.Second, // just shy of requestTimeoutUpperBound
2019-01-12 04:58:27 +00:00
}
// At least 99% of serialized resources in surveyed clusters were smaller than 256kb.
// This should be big enough to accommodate most API POST requests in a single frame,
// and small enough to allow a per connection buffer of this size multiplied by `MaxConcurrentStreams`.
const resourceBody99Percentile = 256 * 1024
http2Options := &http2.Server{
IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
}
2019-01-12 04:58:27 +00:00
// shrink the per-stream buffer and max framesize from the 1MB default while still accommodating most API POST requests in a single frame
http2Options.MaxUploadBufferPerStream = resourceBody99Percentile
http2Options.MaxReadFrameSize = resourceBody99Percentile
// use the overridden concurrent streams setting or make the default of 250 explicit so we can size MaxUploadBufferPerConnection appropriately
if s.HTTP2MaxStreamsPerConnection > 0 {
http2Options.MaxConcurrentStreams = uint32(s.HTTP2MaxStreamsPerConnection)
} else {
http2Options.MaxConcurrentStreams = 250
}
// increase the connection buffer size from the 1MB default to handle the specified number of concurrent streams
http2Options.MaxUploadBufferPerConnection = http2Options.MaxUploadBufferPerStream * int32(http2Options.MaxConcurrentStreams)
2019-09-27 21:51:53 +00:00
if !s.DisableHTTP2 {
// apply settings to the server
if err := http2.ConfigureServer(secureServer, http2Options); err != nil {
return nil, fmt.Errorf("error configuring http2: %v", err)
}
2019-01-12 04:58:27 +00:00
}
2020-08-10 17:43:49 +00:00
// use tlsHandshakeErrorWriter to handle messages of tls handshake error
tlsErrorWriter := &tlsHandshakeErrorWriter{os.Stderr}
tlsErrorLogger := log.New(tlsErrorWriter, "", 0)
secureServer.ErrorLog = tlsErrorLogger
klog.Infof("Serving securely on %s", secureServer.Addr)
stoppedCh, _, err := RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)
return stoppedCh, err
}
// ServeWithListenerStopped runs the secure http server. It fails only if certificates cannot be loaded or the initial listen call fails.
// The actual server loop (stoppable by closing stopCh) runs in a go routine, i.e. ServeWithListenerStopped does not block.
// It returns a stoppedCh that is closed when all non-hijacked active requests have been processed.
// It returns a listenerStoppedCh that is closed when the underlying http Server has stopped listening.
// TODO: do a follow up PR to remove this function, change 'Serve' to return listenerStoppedCh
// and update all components that call 'Serve'
func (s *SecureServingInfo) ServeWithListenerStopped(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) (<-chan struct{}, <-chan struct{}, error) {
if s.Listener == nil {
return nil, nil, fmt.Errorf("listener must not be nil")
}
tlsConfig, err := s.tlsConfig(stopCh)
if err != nil {
return nil, nil, err
}
secureServer := &http.Server{
Addr: s.Listener.Addr().String(),
Handler: handler,
MaxHeaderBytes: 1 << 20,
TLSConfig: tlsConfig,
IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
ReadHeaderTimeout: 32 * time.Second, // just shy of requestTimeoutUpperBound
}
// At least 99% of serialized resources in surveyed clusters were smaller than 256kb.
// This should be big enough to accommodate most API POST requests in a single frame,
// and small enough to allow a per connection buffer of this size multiplied by `MaxConcurrentStreams`.
const resourceBody99Percentile = 256 * 1024
http2Options := &http2.Server{
IdleTimeout: 90 * time.Second, // matches http.DefaultTransport keep-alive timeout
}
// shrink the per-stream buffer and max framesize from the 1MB default while still accommodating most API POST requests in a single frame
http2Options.MaxUploadBufferPerStream = resourceBody99Percentile
http2Options.MaxReadFrameSize = resourceBody99Percentile
// use the overridden concurrent streams setting or make the default of 250 explicit so we can size MaxUploadBufferPerConnection appropriately
if s.HTTP2MaxStreamsPerConnection > 0 {
http2Options.MaxConcurrentStreams = uint32(s.HTTP2MaxStreamsPerConnection)
} else {
http2Options.MaxConcurrentStreams = 250
}
// increase the connection buffer size from the 1MB default to handle the specified number of concurrent streams
http2Options.MaxUploadBufferPerConnection = http2Options.MaxUploadBufferPerStream * int32(http2Options.MaxConcurrentStreams)
if !s.DisableHTTP2 {
// apply settings to the server
if err := http2.ConfigureServer(secureServer, http2Options); err != nil {
return nil, nil, fmt.Errorf("error configuring http2: %v", err)
}
}
// use tlsHandshakeErrorWriter to handle messages of tls handshake error
tlsErrorWriter := &tlsHandshakeErrorWriter{os.Stderr}
tlsErrorLogger := log.New(tlsErrorWriter, "", 0)
secureServer.ErrorLog = tlsErrorLogger
2019-01-12 04:58:27 +00:00
klog.Infof("Serving securely on %s", secureServer.Addr)
return RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)
}
2019-09-27 21:51:53 +00:00
// RunServer spawns a go-routine continuously serving until the stopCh is
// closed.
2019-04-07 17:07:55 +00:00
// It returns a stoppedCh that is closed when all non-hijacked active requests
// have been processed.
// This function does not block
2019-01-12 04:58:27 +00:00
// TODO: make private when insecure serving is gone from the kube-apiserver
func RunServer(
server *http.Server,
ln net.Listener,
shutDownTimeout time.Duration,
stopCh <-chan struct{},
) (<-chan struct{}, <-chan struct{}, error) {
2019-01-12 04:58:27 +00:00
if ln == nil {
return nil, nil, fmt.Errorf("listener must not be nil")
2019-01-12 04:58:27 +00:00
}
// Shutdown server gracefully.
serverShutdownCh, listenerStoppedCh := make(chan struct{}), make(chan struct{})
2019-01-12 04:58:27 +00:00
go func() {
defer close(serverShutdownCh)
2019-01-12 04:58:27 +00:00
<-stopCh
ctx, cancel := context.WithTimeout(context.Background(), shutDownTimeout)
server.Shutdown(ctx)
cancel()
}()
go func() {
defer utilruntime.HandleCrash()
defer close(listenerStoppedCh)
2019-01-12 04:58:27 +00:00
var listener net.Listener
2020-08-10 17:43:49 +00:00
listener = tcpKeepAliveListener{ln}
2019-01-12 04:58:27 +00:00
if server.TLSConfig != nil {
listener = tls.NewListener(listener, server.TLSConfig)
}
err := server.Serve(listener)
msg := fmt.Sprintf("Stopped listening on %s", ln.Addr().String())
select {
case <-stopCh:
klog.Info(msg)
default:
panic(fmt.Sprintf("%s due to error: %v", msg, err))
}
}()
return serverShutdownCh, listenerStoppedCh, nil
2019-01-12 04:58:27 +00:00
}
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
//
// Copied from Go 1.7.2 net/http/server.go
type tcpKeepAliveListener struct {
2020-08-10 17:43:49 +00:00
net.Listener
2019-01-12 04:58:27 +00:00
}
func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
2020-08-10 17:43:49 +00:00
c, err := ln.Listener.Accept()
2019-01-12 04:58:27 +00:00
if err != nil {
return nil, err
}
2020-08-10 17:43:49 +00:00
if tc, ok := c.(*net.TCPConn); ok {
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(defaultKeepAlivePeriod)
}
return c, nil
}
// tlsHandshakeErrorWriter writes TLS handshake errors to klog with
// trace level - V(5), to avoid flooding of tls handshake errors.
type tlsHandshakeErrorWriter struct {
out io.Writer
}
const tlsHandshakeErrorPrefix = "http: TLS handshake error"
func (w *tlsHandshakeErrorWriter) Write(p []byte) (int, error) {
if strings.Contains(string(p), tlsHandshakeErrorPrefix) {
klog.V(5).Info(string(p))
metrics.TLSHandshakeErrors.Inc()
return len(p), nil
}
// for non tls handshake error, log it as usual
return w.out.Write(p)
2019-01-12 04:58:27 +00:00
}