Skip to content
This repository was archived by the owner on Apr 3, 2023. It is now read-only.

Commit b7100f1

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#48859 from victorgp/master
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Support for custom tls cipher suites in api server and kubelet **What this PR does / why we need it**: This pull request aims to solve the problem of users not able to set custom cipher suites in the api server. Several users have requested this given that some default ciphers are vulnerable. There is a discussion in kubernetes#41038 of how to implement this. The options are: - Setting a fixed list of ciphers, but users will have different requirements so a fixed list would be problematic. - Letting the user set them by parameter, this requires adding a new parameter that could be pretty long with the list of all the ciphers. I implemented the second option, if the ciphers are not passed by parameter, the Go default ones will be used (same behavior as now). **Which issue this PR fixes** fixes kubernetes#41038 **Special notes for your reviewer**: The ciphers in Go tls config are constants and the ones passed by parameters are a comma-separated list. I needed to create the `type CipherSuitesFlag` to support that conversion/mapping, because i couldn't find any way to do this type of reflection in Go. If you think there is another way to implement this, let me know. If you want to test it out, this is a ciphers combination i tested without the weak ones: ``` TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA ``` If this is merged i will implement the same for the Kubelet. **Release note**: ```release-note kube-apiserver and kubelet now support customizing TLS ciphers via a `--tls-cipher-suites` flag ```
2 parents df27ac0 + d7dbc96 commit b7100f1

File tree

16 files changed

+444
-1
lines changed

16 files changed

+444
-1
lines changed

cmd/kubelet/app/options/options.go

+4
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ func AddKubeletConfigFlags(fs *pflag.FlagSet, c *kubeletconfig.KubeletConfigurat
442442
"If --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key "+
443443
"are generated for the public address and saved to the directory passed to --cert-dir.")
444444
fs.StringVar(&c.TLSPrivateKeyFile, "tls-private-key-file", c.TLSPrivateKeyFile, "File containing x509 private key matching --tls-cert-file.")
445+
fs.StringSliceVar(&c.TLSCipherSuites, "tls-cipher-suites", c.TLSCipherSuites,
446+
"Comma-separated list of cipher suites for the server. "+
447+
"Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+
448+
"If omitted, the default Go cipher suites will be used")
445449

446450
fs.Int32Var(&c.RegistryPullQPS, "registry-qps", c.RegistryPullQPS, "If > 0, limit registry pull QPS to this value. If 0, unlimited.")
447451
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")

cmd/kubelet/app/server.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -567,12 +567,19 @@ func InitializeTLS(kf *options.KubeletFlags, kc *kubeletconfiginternal.KubeletCo
567567
glog.V(4).Infof("Using self-signed cert (%s, %s)", kc.TLSCertFile, kc.TLSPrivateKeyFile)
568568
}
569569
}
570+
571+
tlsCipherSuites, err := flag.TLSCipherSuites(kc.TLSCipherSuites)
572+
if err != nil {
573+
return nil, err
574+
}
575+
570576
tlsOptions := &server.TLSOptions{
571577
Config: &tls.Config{
572578
// Can't use SSLv3 because of POODLE and BEAST
573579
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
574580
// Can't use TLSv1.1 because of RC4 cipher usage
575-
MinVersion: tls.VersionTLS12,
581+
MinVersion: tls.VersionTLS12,
582+
CipherSuites: tlsCipherSuites,
576583
},
577584
CertFile: kc.TLSCertFile,
578585
KeyFile: kc.TLSPrivateKeyFile,

pkg/kubelet/apis/kubeletconfig/helpers_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ var (
175175
"HairpinMode",
176176
"HealthzBindAddress",
177177
"HealthzPort",
178+
"TLSCipherSuites[*]",
178179
"IPTablesDropBit",
179180
"IPTablesMasqueradeBit",
180181
"ImageGCHighThresholdPercent",

pkg/kubelet/apis/kubeletconfig/types.go

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ type KubeletConfiguration struct {
8585
// tlsPrivateKeyFile is the ile containing x509 private key matching
8686
// tlsCertFile.
8787
TLSPrivateKeyFile string
88+
// TLSCipherSuites is the list of allowed cipher suites for the server.
89+
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
90+
TLSCipherSuites []string
8891
// authentication specifies how requests to the Kubelet's server are authenticated
8992
Authentication KubeletAuthentication
9093
// authorization specifies how requests to the Kubelet's server are authorized

pkg/kubelet/apis/kubeletconfig/v1alpha1/types.go

+3
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ type KubeletConfiguration struct {
8585
// tlsPrivateKeyFile is the ile containing x509 private key matching
8686
// tlsCertFile.
8787
TLSPrivateKeyFile string `json:"tlsPrivateKeyFile"`
88+
// TLSCipherSuites is the list of allowed cipher suites for the server.
89+
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
90+
TLSCipherSuites []string `json:"tlsCipherSuites"`
8891
// authentication specifies how requests to the Kubelet's server are authenticated
8992
Authentication KubeletAuthentication `json:"authentication"`
9093
// authorization specifies how requests to the Kubelet's server are authorized

pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.conversion.go

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/kubelet/apis/kubeletconfig/v1alpha1/zz_generated.deepcopy.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/kubelet/apis/kubeletconfig/zz_generated.deepcopy.go

+5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/apiserver/pkg/server/options/serving.go

+16
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ type SecureServingOptions struct {
5151
ServerCert GeneratableKeyCert
5252
// SNICertKeys are named CertKeys for serving secure traffic with SNI support.
5353
SNICertKeys []utilflag.NamedCertKey
54+
// CipherSuites is the list of allowed cipher suites for the server.
55+
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
56+
CipherSuites []string
5457
}
5558

5659
type CertKey struct {
@@ -134,6 +137,11 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
134137
"Controllers. This must be a valid PEM-encoded CA bundle. Altneratively, the certificate authority "+
135138
"can be appended to the certificate provided by --tls-cert-file.")
136139

140+
fs.StringSliceVar(&s.CipherSuites, "tls-cipher-suites", s.CipherSuites,
141+
"Comma-separated list of cipher suites for the server. "+
142+
"Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). "+
143+
"If omitted, the default Go cipher suites will be used")
144+
137145
fs.Var(utilflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+
138146
"A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+
139147
"domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+
@@ -233,6 +241,14 @@ func (s *SecureServingOptions) applyServingInfoTo(c *server.Config) error {
233241
}
234242
}
235243

244+
if len(s.CipherSuites) != 0 {
245+
cipherSuites, err := utilflag.TLSCipherSuites(s.CipherSuites)
246+
if err != nil {
247+
return err
248+
}
249+
secureServingInfo.CipherSuites = cipherSuites
250+
}
251+
236252
// load SNI certs
237253
namedTLSCerts := make([]server.NamedTLSCert, 0, len(s.SNICertKeys))
238254
for _, nck := range s.SNICertKeys {

staging/src/k8s.io/apiserver/pkg/util/flag/BUILD

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ load(
99
go_test(
1010
name = "go_default_test",
1111
srcs = [
12+
"ciphersuites_flag_test.go",
1213
"colon_separated_multimap_string_string_test.go",
1314
"langle_separated_map_string_string_test.go",
1415
"map_string_bool_test.go",
@@ -23,6 +24,7 @@ go_test(
2324
go_library(
2425
name = "go_default_library",
2526
srcs = [
27+
"ciphersuites_flag.go",
2628
"colon_separated_multimap_string_string.go",
2729
"configuration_map.go",
2830
"flags.go",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
Copyright 2017 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 flag
18+
19+
import (
20+
"crypto/tls"
21+
"fmt"
22+
)
23+
24+
// ciphers maps strings into tls package cipher constants in
25+
// https://golang.org/pkg/crypto/tls/#pkg-constants
26+
var ciphers = map[string]uint16{
27+
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
28+
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
29+
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
30+
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
31+
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
32+
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
33+
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
34+
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
35+
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
36+
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
37+
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
38+
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
39+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
40+
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
41+
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
42+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
43+
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
44+
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
45+
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
46+
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
47+
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
48+
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
49+
}
50+
51+
func TLSCipherSuites(cipherNames []string) ([]uint16, error) {
52+
if len(cipherNames) == 0 {
53+
return nil, nil
54+
}
55+
ciphersIntSlice := make([]uint16, 0)
56+
for _, cipher := range cipherNames {
57+
intValue, ok := ciphers[cipher]
58+
if !ok {
59+
return nil, fmt.Errorf("Cipher suite %s not supported or doesn't exist", cipher)
60+
}
61+
ciphersIntSlice = append(ciphersIntSlice, intValue)
62+
}
63+
return ciphersIntSlice, nil
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
Copyright 2017 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 flag
18+
19+
import (
20+
"crypto/tls"
21+
"fmt"
22+
"go/importer"
23+
"reflect"
24+
"strings"
25+
"testing"
26+
)
27+
28+
func TestStrToUInt16(t *testing.T) {
29+
tests := []struct {
30+
flag []string
31+
expected []uint16
32+
expected_error bool
33+
}{
34+
{
35+
// Happy case
36+
flag: []string{"TLS_RSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"},
37+
expected: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
38+
expected_error: false,
39+
},
40+
{
41+
// One flag only
42+
flag: []string{"TLS_RSA_WITH_RC4_128_SHA"},
43+
expected: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA},
44+
expected_error: false,
45+
},
46+
{
47+
// Empty flag
48+
flag: []string{},
49+
expected: nil,
50+
expected_error: false,
51+
},
52+
{
53+
// Duplicated flag
54+
flag: []string{"TLS_RSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_RC4_128_SHA"},
55+
expected: []uint16{tls.TLS_RSA_WITH_RC4_128_SHA, tls.TLS_RSA_WITH_AES_128_CBC_SHA, tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, tls.TLS_RSA_WITH_RC4_128_SHA},
56+
expected_error: false,
57+
},
58+
{
59+
// Invalid flag
60+
flag: []string{"foo"},
61+
expected: nil,
62+
expected_error: true,
63+
},
64+
}
65+
66+
for i, test := range tests {
67+
uIntFlags, err := TLSCipherSuites(test.flag)
68+
if reflect.DeepEqual(uIntFlags, test.expected) == false {
69+
t.Errorf("%d: expected %+v, got %+v", i, test.expected, uIntFlags)
70+
}
71+
if test.expected_error && err == nil {
72+
t.Errorf("%d: expecting error, got %+v", i, err)
73+
}
74+
}
75+
}
76+
77+
func TestConstantMaps(t *testing.T) {
78+
pkg, err := importer.Default().Import("crypto/tls")
79+
if err != nil {
80+
fmt.Printf("error: %s\n", err.Error())
81+
return
82+
}
83+
discoveredCiphers := map[string]bool{}
84+
for _, declName := range pkg.Scope().Names() {
85+
if strings.HasPrefix(declName, "TLS_RSA_") || strings.HasPrefix(declName, "TLS_ECDHE_") {
86+
discoveredCiphers[declName] = true
87+
}
88+
}
89+
90+
for k := range discoveredCiphers {
91+
if _, ok := ciphers[k]; !ok {
92+
t.Errorf("discovered cipher tls.%s not in ciphers map", k)
93+
}
94+
}
95+
for k := range ciphers {
96+
if _, ok := discoveredCiphers[k]; !ok {
97+
t.Errorf("ciphers map has %s not in tls package", k)
98+
}
99+
}
100+
}

test/integration/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ filegroup(
5858
"//test/integration/secrets:all-srcs",
5959
"//test/integration/serviceaccount:all-srcs",
6060
"//test/integration/storageclasses:all-srcs",
61+
"//test/integration/tls:all-srcs",
6162
"//test/integration/ttlcontroller:all-srcs",
6263
"//test/integration/volume:all-srcs",
6364
],

test/integration/tls/BUILD

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
load("@io_bazel_rules_go//go:def.bzl", "go_test")
2+
3+
go_test(
4+
name = "go_default_test",
5+
size = "large",
6+
srcs = [
7+
"ciphers_test.go",
8+
"main_test.go",
9+
],
10+
importpath = "k8s.io/kubernetes/test/integration/tls",
11+
tags = ["integration"],
12+
deps = [
13+
"//cmd/kube-apiserver/app:go_default_library",
14+
"//cmd/kube-apiserver/app/options:go_default_library",
15+
"//test/integration/framework:go_default_library",
16+
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
17+
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
18+
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
19+
"//vendor/k8s.io/client-go/rest:go_default_library",
20+
],
21+
)
22+
23+
filegroup(
24+
name = "package-srcs",
25+
srcs = glob(["**"]),
26+
tags = ["automanaged"],
27+
visibility = ["//visibility:private"],
28+
)
29+
30+
filegroup(
31+
name = "all-srcs",
32+
srcs = [":package-srcs"],
33+
tags = ["automanaged"],
34+
visibility = ["//visibility:public"],
35+
)

0 commit comments

Comments
 (0)