Skip to content

Commit 550a678

Browse files
committed
Warn when insecure TLS ciphers are selected.
1 parent 4e975b9 commit 550a678

File tree

7 files changed

+97
-18
lines changed

7 files changed

+97
-18
lines changed

cmd/kubelet/app/options/options.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,11 +464,13 @@ func AddKubeletConfigFlags(mainfs *pflag.FlagSet, c *kubeletconfig.KubeletConfig
464464
fs.StringVar(&c.TLSPrivateKeyFile, "tls-private-key-file", c.TLSPrivateKeyFile, "File containing x509 private key matching --tls-cert-file.")
465465
fs.BoolVar(&c.ServerTLSBootstrap, "rotate-server-certificates", c.ServerTLSBootstrap, "Auto-request and rotate the kubelet serving certificates by requesting new certificates from the kube-apiserver when the certificate expiration approaches. Requires the RotateKubeletServerCertificate feature gate to be enabled, and approval of the submitted CertificateSigningRequest objects.")
466466

467-
tlsCipherPossibleValues := cliflag.TLSCipherPossibleValues()
467+
tlsCipherPreferredValues := cliflag.PreferredTLSCipherNames()
468+
tlsCipherInsecureValues := cliflag.InsecureTLSCipherNames()
468469
fs.StringSliceVar(&c.TLSCipherSuites, "tls-cipher-suites", c.TLSCipherSuites,
469470
"Comma-separated list of cipher suites for the server. "+
470-
"If omitted, the default Go cipher suites will be used. "+
471-
"Possible values: "+strings.Join(tlsCipherPossibleValues, ","))
471+
"If omitted, the default Go cipher suites will be used. \n"+
472+
"Preferred values: "+strings.Join(tlsCipherPreferredValues, ", ")+". \n"+
473+
"Insecure values: "+strings.Join(tlsCipherInsecureValues, ", ")+".")
472474
tlsPossibleVersions := cliflag.TLSPossibleVersions()
473475
fs.StringVar(&c.TLSMinVersion, "tls-min-version", c.TLSMinVersion,
474476
"Minimum TLS version supported. "+

cmd/kubelet/app/server.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import (
6060
"k8s.io/client-go/util/connrotation"
6161
"k8s.io/client-go/util/keyutil"
6262
cloudprovider "k8s.io/cloud-provider"
63+
"k8s.io/component-base/cli/flag"
6364
cliflag "k8s.io/component-base/cli/flag"
6465
"k8s.io/component-base/configz"
6566
"k8s.io/component-base/featuregate"
@@ -1010,6 +1011,17 @@ func InitializeTLS(kf *options.KubeletFlags, kc *kubeletconfiginternal.KubeletCo
10101011
return nil, err
10111012
}
10121013

1014+
if len(tlsCipherSuites) > 0 {
1015+
insecureCiphers := flag.InsecureTLSCiphers()
1016+
for i := 0; i < len(tlsCipherSuites); i++ {
1017+
for cipherName, cipherID := range insecureCiphers {
1018+
if tlsCipherSuites[i] == cipherID {
1019+
klog.Warningf("Use of insecure cipher '%s' detected.", cipherName)
1020+
}
1021+
}
1022+
}
1023+
}
1024+
10131025
minTLSVersion, err := cliflag.TLSVersion(kc.TLSMinVersion)
10141026
if err != nil {
10151027
return nil, err

staging/src/k8s.io/apiserver/pkg/server/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ go_library(
117117
"//staging/src/k8s.io/apiserver/pkg/util/openapi:go_default_library",
118118
"//staging/src/k8s.io/client-go/informers:go_default_library",
119119
"//staging/src/k8s.io/client-go/rest:go_default_library",
120+
"//staging/src/k8s.io/component-base/cli/flag:go_default_library",
120121
"//staging/src/k8s.io/component-base/logs:go_default_library",
121122
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
122123
"//vendor/github.com/emicklei/go-restful:go_default_library",

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,13 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
171171
fs.StringVar(&s.ServerCert.CertKey.KeyFile, "tls-private-key-file", s.ServerCert.CertKey.KeyFile,
172172
"File containing the default x509 private key matching --tls-cert-file.")
173173

174-
tlsCipherPossibleValues := cliflag.TLSCipherPossibleValues()
174+
tlsCipherPreferredValues := cliflag.PreferredTLSCipherNames()
175+
tlsCipherInsecureValues := cliflag.InsecureTLSCipherNames()
175176
fs.StringSliceVar(&s.CipherSuites, "tls-cipher-suites", s.CipherSuites,
176177
"Comma-separated list of cipher suites for the server. "+
177-
"If omitted, the default Go cipher suites will be use. "+
178-
"Possible values: "+strings.Join(tlsCipherPossibleValues, ","))
178+
"If omitted, the default Go cipher suites will be used. \n"+
179+
"Preferred values: "+strings.Join(tlsCipherPreferredValues, ", ")+". \n"+
180+
"Insecure values: "+strings.Join(tlsCipherInsecureValues, ", ")+".")
179181

180182
tlsPossibleVersions := cliflag.TLSPossibleVersions()
181183
fs.StringVar(&s.MinTLSVersion, "tls-min-version", s.MinTLSVersion,

staging/src/k8s.io/apiserver/pkg/server/secure_serving.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"time"
2626

2727
"golang.org/x/net/http2"
28+
"k8s.io/component-base/cli/flag"
2829
"k8s.io/klog/v2"
2930

3031
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -56,6 +57,14 @@ func (s *SecureServingInfo) tlsConfig(stopCh <-chan struct{}) (*tls.Config, erro
5657
}
5758
if len(s.CipherSuites) > 0 {
5859
tlsConfig.CipherSuites = s.CipherSuites
60+
insecureCiphers := flag.InsecureTLSCiphers()
61+
for i := 0; i < len(s.CipherSuites); i++ {
62+
for cipherName, cipherID := range insecureCiphers {
63+
if s.CipherSuites[i] == cipherID {
64+
klog.Warningf("Use of insecure cipher '%s' detected.", cipherName)
65+
}
66+
}
67+
}
5968
}
6069

6170
if s.ClientCA != nil {

staging/src/k8s.io/component-base/cli/flag/ciphersuites_flag.go

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,18 @@ import (
2525

2626
// ciphers maps strings into tls package cipher constants in
2727
// https://golang.org/pkg/crypto/tls/#pkg-constants
28+
// to be replaced by tls.CipherSuites() when the project migrates to go1.14.
2829
var ciphers = map[string]uint16{
29-
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
3030
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
3131
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
3232
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
33-
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
3433
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
3534
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
36-
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
3735
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
3836
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
39-
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
4037
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
4138
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
4239
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
43-
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
44-
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
4540
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
4641
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
4742
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
@@ -53,21 +48,76 @@ var ciphers = map[string]uint16{
5348
"TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384,
5449
}
5550

56-
func TLSCipherPossibleValues() []string {
51+
// to be replaced by tls.InsecureCipherSuites() when the project migrates to go1.14.
52+
var insecureCiphers = map[string]uint16{
53+
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
54+
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
55+
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
56+
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
57+
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
58+
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
59+
}
60+
61+
// InsecureTLSCiphers returns the cipher suites implemented by crypto/tls which have
62+
// security issues.
63+
func InsecureTLSCiphers() map[string]uint16 {
64+
cipherKeys := make(map[string]uint16, len(insecureCiphers))
65+
for k, v := range insecureCiphers {
66+
cipherKeys[k] = v
67+
}
68+
return cipherKeys
69+
}
70+
71+
// InsecureTLSCipherNames returns a list of cipher suite names implemented by crypto/tls
72+
// which have security issues.
73+
func InsecureTLSCipherNames() []string {
74+
cipherKeys := sets.NewString()
75+
for key := range insecureCiphers {
76+
cipherKeys.Insert(key)
77+
}
78+
return cipherKeys.List()
79+
}
80+
81+
// PreferredTLSCipherNames returns a list of cipher suite names implemented by crypto/tls.
82+
func PreferredTLSCipherNames() []string {
5783
cipherKeys := sets.NewString()
5884
for key := range ciphers {
5985
cipherKeys.Insert(key)
6086
}
6187
return cipherKeys.List()
6288
}
6389

90+
func allCiphers() map[string]uint16 {
91+
acceptedCiphers := make(map[string]uint16, len(ciphers)+len(insecureCiphers))
92+
for k, v := range ciphers {
93+
acceptedCiphers[k] = v
94+
}
95+
for k, v := range insecureCiphers {
96+
acceptedCiphers[k] = v
97+
}
98+
return acceptedCiphers
99+
}
100+
101+
// TLSCipherPossibleValues returns all acceptable cipher suite names.
102+
// This is a combination of both InsecureTLSCipherNames() and PreferredTLSCipherNames().
103+
func TLSCipherPossibleValues() []string {
104+
cipherKeys := sets.NewString()
105+
acceptedCiphers := allCiphers()
106+
for key := range acceptedCiphers {
107+
cipherKeys.Insert(key)
108+
}
109+
return cipherKeys.List()
110+
}
111+
112+
// TLSCipherSuites returns a list of cipher suite IDs from the cipher suite names passed.
64113
func TLSCipherSuites(cipherNames []string) ([]uint16, error) {
65114
if len(cipherNames) == 0 {
66115
return nil, nil
67116
}
68117
ciphersIntSlice := make([]uint16, 0)
118+
possibleCiphers := allCiphers()
69119
for _, cipher := range cipherNames {
70-
intValue, ok := ciphers[cipher]
120+
intValue, ok := possibleCiphers[cipher]
71121
if !ok {
72122
return nil, fmt.Errorf("Cipher suite %s not supported or doesn't exist", cipher)
73123
}
@@ -83,6 +133,7 @@ var versions = map[string]uint16{
83133
"VersionTLS13": tls.VersionTLS13,
84134
}
85135

136+
// TLSPossibleVersions returns all acceptable values for TLS Version.
86137
func TLSPossibleVersions() []string {
87138
versionsKeys := sets.NewString()
88139
for key := range versions {
@@ -91,6 +142,7 @@ func TLSPossibleVersions() []string {
91142
return versionsKeys.List()
92143
}
93144

145+
// TLSVersion returns the TLS Version ID for the version name passed.
94146
func TLSVersion(versionName string) (uint16, error) {
95147
if len(versionName) == 0 {
96148
return DefaultTLSVersion(), nil
@@ -101,6 +153,7 @@ func TLSVersion(versionName string) (uint16, error) {
101153
return 0, fmt.Errorf("unknown tls version %q", versionName)
102154
}
103155

156+
// DefaultTLSVersion defines the default TLS Version.
104157
func DefaultTLSVersion() uint16 {
105158
// Can't use SSLv3 because of POODLE and BEAST
106159
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher

staging/src/k8s.io/component-base/cli/flag/ciphersuites_flag_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,18 @@ func TestConstantMaps(t *testing.T) {
8686
if strings.HasPrefix(declName, "VersionTLS") {
8787
discoveredVersions[declName] = true
8888
}
89-
if strings.HasPrefix(declName, "TLS_RSA_") || strings.HasPrefix(declName, "TLS_ECDHE_") ||
90-
strings.HasPrefix(declName, "TLS_AES_") || strings.HasPrefix(declName, "TLS_CHACHA20_") {
89+
if strings.HasPrefix(declName, "TLS_") && !strings.HasPrefix(declName, "TLS_FALLBACK_") {
9190
discoveredCiphers[declName] = true
9291
}
9392
}
9493

94+
acceptedCiphers := allCiphers()
9595
for k := range discoveredCiphers {
96-
if _, ok := ciphers[k]; !ok {
96+
if _, ok := acceptedCiphers[k]; !ok {
9797
t.Errorf("discovered cipher tls.%s not in ciphers map", k)
9898
}
9999
}
100-
for k := range ciphers {
100+
for k := range acceptedCiphers {
101101
if _, ok := discoveredCiphers[k]; !ok {
102102
t.Errorf("ciphers map has %s not in tls package", k)
103103
}

0 commit comments

Comments
 (0)