Skip to content

Commit

Permalink
UPSTREAM: <carry>: verify required http2 cipher suites
Browse files Browse the repository at this point in the history
In the Apiserver admission, we need to return an error if the required
http2 cipher suites are missing from a custom tlsSecurityProfile.
Currently, custom cipher suites missing ECDHE_RSA_WITH_AES_128_GCM_SHA256 or
ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 result in invalid http2 Server
configuration causing the apiservers to crash.
See: go/x/net/http2.ConfigureServer for futher information.

Signed-off-by: Damien Grisonnet <dgrisonn@redhat.com>
  • Loading branch information
dgrisonnet authored and damemi committed Dec 6, 2021
1 parent 06fe2eb commit 636a920
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func validateTLSSecurityProfile(fieldPath *field.Path, profile *configv1.TLSSecu
errs = append(errs, validateTLSSecurityProfileType(fieldPath, profile)...)

if profile.Type == configv1.TLSProfileCustomType && profile.Custom != nil {
errs = append(errs, validateCipherSuites(fieldPath.Child("custom", "ciphers"), profile.Custom.Ciphers)...)
errs = append(errs, validateCipherSuites(fieldPath.Child("custom", "ciphers"), profile.Custom.Ciphers, profile.Custom.MinTLSVersion)...)
errs = append(errs, validateMinTLSVersion(fieldPath.Child("custom", "minTLSVersion"), profile.Custom.MinTLSVersion)...)
}

Expand Down Expand Up @@ -212,16 +212,38 @@ func validateTLSSecurityProfileType(fieldPath *field.Path, profile *configv1.TLS
return errs
}

func validateCipherSuites(fieldPath *field.Path, suites []string) field.ErrorList {
func validateCipherSuites(fieldPath *field.Path, suites []string, version configv1.TLSProtocolVersion) field.ErrorList {
errs := field.ErrorList{}

if ianaSuites := libgocrypto.OpenSSLToIANACipherSuites(suites); len(ianaSuites) == 0 {
errs = append(errs, field.Invalid(fieldPath, suites, "no supported cipher suite found"))
}

// Return an error if it is missing ECDHE_RSA_WITH_AES_128_GCM_SHA256 or
// ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 to prevent the http2 Server
// configuration to return an error when http2 required cipher suites aren't
// provided.
// See: go/x/net/http2.ConfigureServer for futher information.
if version < configv1.VersionTLS13 && !haveRequiredHTTP2CipherSuites(suites) {
errs = append(errs, field.Invalid(fieldPath, suites, "http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of ECDHE-RSA-AES128-GCM-SHA256 or ECDHE-ECDSA-AES128-GCM-SHA256)"))
}

return errs
}

func haveRequiredHTTP2CipherSuites(suites []string) bool {
for _, s := range suites {
switch s {
case "ECDHE-RSA-AES128-GCM-SHA256",
// Alternative MTI cipher to not discourage ECDSA-only servers.
// See http://golang.org/cl/30721 for further information.
"ECDHE-ECDSA-AES128-GCM-SHA256":
return true
}
}
return false
}

func validateMinTLSVersion(fieldPath *field.Path, version configv1.TLSProtocolVersion) field.ErrorList {
errs := field.ErrorList{}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func Test_validateTLSSecurityProfile(t *testing.T) {
},
want: field.ErrorList{
field.Invalid(rootFieldPath.Child("custom", "ciphers"), []string{"UNKNOWN_CIPHER"}, "no supported cipher suite found"),
field.Invalid(rootFieldPath.Child("custom", "ciphers"), []string{"UNKNOWN_CIPHER"}, "http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of ECDHE-RSA-AES128-GCM-SHA256 or ECDHE-ECDSA-AES128-GCM-SHA256)"),
},
},
{
Expand All @@ -190,7 +191,7 @@ func Test_validateTLSSecurityProfile(t *testing.T) {
Custom: &configv1.CustomTLSProfile{
TLSProfileSpec: configv1.TLSProfileSpec{
Ciphers: []string{
"UNKNOWN_CIPHER", "ECDHE-ECDSA-CHACHA20-POLY1305",
"UNKNOWN_CIPHER", "ECDHE-RSA-AES128-GCM-SHA256",
},
},
},
Expand All @@ -207,6 +208,7 @@ func Test_validateTLSSecurityProfile(t *testing.T) {
},
want: field.ErrorList{
field.Invalid(rootFieldPath.Child("custom", "ciphers"), []string(nil), "no supported cipher suite found"),
field.Invalid(rootFieldPath.Child("custom", "ciphers"), []string(nil), "http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of ECDHE-RSA-AES128-GCM-SHA256 or ECDHE-ECDSA-AES128-GCM-SHA256)"),
},
},
{
Expand All @@ -224,6 +226,45 @@ func Test_validateTLSSecurityProfile(t *testing.T) {
field.NotSupported(rootFieldPath.Child("custom", "minTLSVersion"), configv1.VersionTLS13, []string{string(configv1.VersionTLS10), string(configv1.VersionTLS11), string(configv1.VersionTLS12)}),
},
},
{
name: "custom profile missing required http2 ciphers",
profile: &configv1.TLSSecurityProfile{
Type: "Custom",
Custom: &configv1.CustomTLSProfile{
TLSProfileSpec: configv1.TLSProfileSpec{
Ciphers: []string{
"ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-CHACHA20-POLY1305",
"ECDHE-RSA-CHACHA20-POLY1305",
},
MinTLSVersion: configv1.VersionTLS12,
},
},
},
want: field.ErrorList{
field.Invalid(rootFieldPath.Child("custom", "ciphers"), []string{"ECDSA-AES256-GCM-SHA384", "ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-CHACHA20-POLY1305", "ECDHE-RSA-CHACHA20-POLY1305"}, "http2: TLSConfig.CipherSuites is missing an HTTP/2-required AES_128_GCM_SHA256 cipher (need at least one of ECDHE-RSA-AES128-GCM-SHA256 or ECDHE-ECDSA-AES128-GCM-SHA256)"),
},
},
{
name: "custom profile with one required http2 ciphers",
profile: &configv1.TLSSecurityProfile{
Type: "Custom",
Custom: &configv1.CustomTLSProfile{
TLSProfileSpec: configv1.TLSProfileSpec{
Ciphers: []string{
"ECDSA-AES256-GCM-SHA384",
"ECDHE-RSA-AES256-GCM-SHA384",
"ECDHE-ECDSA-CHACHA20-POLY1305",
"ECDHE-RSA-CHACHA20-POLY1305",
"ECDHE-RSA-AES128-GCM-SHA256",
},
MinTLSVersion: configv1.VersionTLS12,
},
},
},
want: field.ErrorList{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 636a920

Please sign in to comment.