diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index 9fbf45ea2fe..926bd5a4caa 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -446,6 +446,9 @@ func AddKubeletConfigFlags(fs *pflag.FlagSet, c *kubeletconfig.KubeletConfigurat "Comma-separated list of cipher suites for the server. "+ "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+ "If omitted, the default Go cipher suites will be used") + fs.StringVar(&c.TLSMinVersion, "tls-min-version", c.TLSMinVersion, + "Minimum TLS version supported. "+ + "Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants.") fs.Int32Var(&c.RegistryPullQPS, "registry-qps", c.RegistryPullQPS, "If > 0, limit registry pull QPS to this value. If 0, unlimited.") fs.Int32Var(&c.RegistryBurst, "registry-burst", c.RegistryBurst, "Maximum size of a bursty pulls, temporarily allows pulls to burst to this number, while still not exceeding registry-qps. Only used if --registry-qps > 0") diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index da8fe6fb594..484ef6b78c3 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -627,12 +627,14 @@ func InitializeTLS(kf *options.KubeletFlags, kc *kubeletconfiginternal.KubeletCo return nil, err } + minTLSVersion, err := flag.TLSVersion(kc.TLSMinVersion) + if err != nil { + return nil, err + } + tlsOptions := &server.TLSOptions{ Config: &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, + MinVersion: minTLSVersion, CipherSuites: tlsCipherSuites, }, CertFile: kc.TLSCertFile, diff --git a/pkg/kubelet/apis/kubeletconfig/helpers_test.go b/pkg/kubelet/apis/kubeletconfig/helpers_test.go index b04c43c6736..9af810a84d3 100644 --- a/pkg/kubelet/apis/kubeletconfig/helpers_test.go +++ b/pkg/kubelet/apis/kubeletconfig/helpers_test.go @@ -176,6 +176,7 @@ var ( "HealthzBindAddress", "HealthzPort", "TLSCipherSuites[*]", + "TLSMinVersion", "IPTablesDropBit", "IPTablesMasqueradeBit", "ImageGCHighThresholdPercent", diff --git a/pkg/kubelet/apis/kubeletconfig/types.go b/pkg/kubelet/apis/kubeletconfig/types.go index ac9b1ff9df5..970f3b5907d 100644 --- a/pkg/kubelet/apis/kubeletconfig/types.go +++ b/pkg/kubelet/apis/kubeletconfig/types.go @@ -88,6 +88,9 @@ type KubeletConfiguration struct { // TLSCipherSuites is the list of allowed cipher suites for the server. // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). TLSCipherSuites []string + // TLSMinVersion is the minimum TLS version supported. + // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). + TLSMinVersion string // authentication specifies how requests to the Kubelet's server are authenticated Authentication KubeletAuthentication // authorization specifies how requests to the Kubelet's server are authorized diff --git a/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go b/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go index bedac222648..0f762d0d0d4 100644 --- a/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go +++ b/pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go @@ -88,6 +88,9 @@ type KubeletConfiguration struct { // TLSCipherSuites is the list of allowed cipher suites for the server. // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). TLSCipherSuites []string `json:"tlsCipherSuites"` + // TLSMinVersion is the minimum TLS version supported. + // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). + TLSMinVersion string `json:"tlsMinVersion"` // authentication specifies how requests to the Kubelet's server are authenticated Authentication KubeletAuthentication `json:"authentication"` // authorization specifies how requests to the Kubelet's server are authorized diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/serving.go b/staging/src/k8s.io/apiserver/pkg/server/options/serving.go index 9606fa0cb41..465839597f6 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/serving.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/serving.go @@ -54,6 +54,9 @@ type SecureServingOptions struct { // CipherSuites is the list of allowed cipher suites for the server. // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). CipherSuites []string + // MinTLSVersion is the minimum TLS version supported. + // Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). + MinTLSVersion string } type CertKey struct { @@ -142,6 +145,10 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) { "Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+ "If omitted, the default Go cipher suites will be used") + fs.StringVar(&s.MinTLSVersion, "tls-min-version", s.MinTLSVersion, + "Minimum TLS version supported. "+ + "Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants.") + fs.Var(utilflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+ "A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+ "domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+ @@ -249,6 +256,11 @@ func (s *SecureServingOptions) applyServingInfoTo(c *server.Config) error { secureServingInfo.CipherSuites = cipherSuites } + secureServingInfo.MinTLSVersion, err = utilflag.TLSVersion(s.MinTLSVersion) + if err != nil { + return err + } + // load SNI certs namedTLSCerts := make([]server.NamedTLSCert, 0, len(s.SNICertKeys)) for _, nck := range s.SNICertKeys { diff --git a/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag.go b/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag.go index c2ce311e4f5..73fd62c100a 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag.go +++ b/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag.go @@ -62,3 +62,26 @@ func TLSCipherSuites(cipherNames []string) ([]uint16, error) { } return ciphersIntSlice, nil } + +var versions = map[string]uint16{ + "VersionTLS10": tls.VersionTLS10, + "VersionTLS11": tls.VersionTLS11, + "VersionTLS12": tls.VersionTLS12, +} + +func TLSVersion(versionName string) (uint16, error) { + if len(versionName) == 0 { + return DefaultTLSVersion(), nil + } + if version, ok := versions[versionName]; ok { + return version, nil + } + return 0, fmt.Errorf("unknown tls version %q", versionName) +} + +func DefaultTLSVersion() uint16 { + // 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 + return tls.VersionTLS12 +} diff --git a/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag_test.go b/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag_test.go index b050238a343..4a8c4efebb5 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag_test.go +++ b/staging/src/k8s.io/apiserver/pkg/util/flag/ciphersuites_flag_test.go @@ -80,8 +80,12 @@ func TestConstantMaps(t *testing.T) { fmt.Printf("error: %s\n", err.Error()) return } + discoveredVersions := map[string]bool{} discoveredCiphers := map[string]bool{} for _, declName := range pkg.Scope().Names() { + if strings.HasPrefix(declName, "VersionTLS") { + discoveredVersions[declName] = true + } if strings.HasPrefix(declName, "TLS_RSA_") || strings.HasPrefix(declName, "TLS_ECDHE_") { discoveredCiphers[declName] = true } @@ -97,4 +101,14 @@ func TestConstantMaps(t *testing.T) { t.Errorf("ciphers map has %s not in tls package", k) } } + for k := range discoveredVersions { + if _, ok := versions[k]; !ok { + t.Errorf("discovered version tls.%s not in version map", k) + } + } + for k := range versions { + if _, ok := discoveredVersions[k]; !ok { + t.Errorf("versions map has %s not in tls package", k) + } + } }