From 70dd7cdc7195ef98b42934693e9a4973852f368c Mon Sep 17 00:00:00 2001 From: Romain Date: Tue, 23 Jul 2024 16:30:05 +0200 Subject: [PATCH] Enforce default cipher suites list Co-authored-by: Kevin Pollet --- pkg/provider/kubernetes/crd/kubernetes.go | 61 +++++---- .../kubernetes/crd/kubernetes_test.go | 120 ++++++++++++++++++ pkg/tls/tls.go | 1 + 3 files changed, 154 insertions(+), 28 deletions(-) diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 0ef4d78ad..83f0b4846 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -876,64 +876,69 @@ func createChainMiddleware(ctx context.Context, namespace string, chain *traefik } func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options { - tlsOptionsCRD := client.GetTLSOptions() + tlsOptionsCRDs := client.GetTLSOptions() var tlsOptions map[string]tls.Options - if len(tlsOptionsCRD) == 0 { + if len(tlsOptionsCRDs) == 0 { return tlsOptions } tlsOptions = make(map[string]tls.Options) var nsDefault []string - for _, tlsOption := range tlsOptionsCRD { - logger := log.FromContext(log.With(ctx, log.Str("tlsOption", tlsOption.Name), log.Str("namespace", tlsOption.Namespace))) + for _, tlsOptionsCRD := range tlsOptionsCRDs { + logger := log.FromContext(log.With(ctx, log.Str("tlsOption", tlsOptionsCRD.Name), log.Str("namespace", tlsOptionsCRD.Namespace))) var clientCAs []tls.FileOrContent - for _, secretName := range tlsOption.Spec.ClientAuth.SecretNames { - secret, exists, err := client.GetSecret(tlsOption.Namespace, secretName) + for _, secretName := range tlsOptionsCRD.Spec.ClientAuth.SecretNames { + secret, exists, err := client.GetSecret(tlsOptionsCRD.Namespace, secretName) if err != nil { - logger.Errorf("Failed to fetch secret %s/%s: %v", tlsOption.Namespace, secretName, err) + logger.Errorf("Failed to fetch secret %s/%s: %v", tlsOptionsCRD.Namespace, secretName, err) continue } if !exists { - logger.Warnf("Secret %s/%s does not exist", tlsOption.Namespace, secretName) + logger.Warnf("Secret %s/%s does not exist", tlsOptionsCRD.Namespace, secretName) continue } - cert, err := getCABlocks(secret, tlsOption.Namespace, secretName) + cert, err := getCABlocks(secret, tlsOptionsCRD.Namespace, secretName) if err != nil { - logger.Errorf("Failed to extract CA from secret %s/%s: %v", tlsOption.Namespace, secretName, err) + logger.Errorf("Failed to extract CA from secret %s/%s: %v", tlsOptionsCRD.Namespace, secretName, err) continue } clientCAs = append(clientCAs, tls.FileOrContent(cert)) } - id := makeID(tlsOption.Namespace, tlsOption.Name) + id := makeID(tlsOptionsCRD.Namespace, tlsOptionsCRD.Name) // If the name is default, we override the default config. - if tlsOption.Name == tls.DefaultTLSConfigName { - id = tlsOption.Name - nsDefault = append(nsDefault, tlsOption.Namespace) + if tlsOptionsCRD.Name == tls.DefaultTLSConfigName { + id = tlsOptionsCRD.Name + nsDefault = append(nsDefault, tlsOptionsCRD.Namespace) } - alpnProtocols := tls.DefaultTLSOptions.ALPNProtocols - if len(tlsOption.Spec.ALPNProtocols) > 0 { - alpnProtocols = tlsOption.Spec.ALPNProtocols + tlsOption := tls.Options{} + tlsOption.SetDefaults() + + tlsOption.MinVersion = tlsOptionsCRD.Spec.MinVersion + tlsOption.MaxVersion = tlsOptionsCRD.Spec.MaxVersion + + if tlsOptionsCRD.Spec.CipherSuites != nil { + tlsOption.CipherSuites = tlsOptionsCRD.Spec.CipherSuites } - tlsOptions[id] = tls.Options{ - MinVersion: tlsOption.Spec.MinVersion, - MaxVersion: tlsOption.Spec.MaxVersion, - CipherSuites: tlsOption.Spec.CipherSuites, - CurvePreferences: tlsOption.Spec.CurvePreferences, - ClientAuth: tls.ClientAuth{ - CAFiles: clientCAs, - ClientAuthType: tlsOption.Spec.ClientAuth.ClientAuthType, - }, - SniStrict: tlsOption.Spec.SniStrict, - ALPNProtocols: alpnProtocols, + tlsOption.CurvePreferences = tlsOptionsCRD.Spec.CurvePreferences + tlsOption.ClientAuth = tls.ClientAuth{ + CAFiles: clientCAs, + ClientAuthType: tlsOptionsCRD.Spec.ClientAuth.ClientAuthType, } + tlsOption.SniStrict = tlsOptionsCRD.Spec.SniStrict + + if tlsOptionsCRD.Spec.ALPNProtocols != nil { + tlsOption.ALPNProtocols = tlsOptionsCRD.Spec.ALPNProtocols + } + + tlsOptions[id] = tlsOption } if len(nsDefault) > 1 { diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index 6724e5c63..1d9869d61 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -860,6 +860,21 @@ func TestLoadIngressRouteTCPs(t *testing.T) { "http/1.1", "acme-tls/1", }, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -915,6 +930,21 @@ func TestLoadIngressRouteTCPs(t *testing.T) { "http/1.1", "acme-tls/1", }, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -3063,6 +3093,21 @@ func TestLoadIngressRoutes(t *testing.T) { "http/1.1", "acme-tls/1", }, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -3121,6 +3166,21 @@ func TestLoadIngressRoutes(t *testing.T) { "http/1.1", "acme-tls/1", }, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -5511,6 +5571,21 @@ func TestCrossNamespace(t *testing.T) { "cross-ns-tls-options-cn": { MinVersion: "VersionTLS12", ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -5555,6 +5630,21 @@ func TestCrossNamespace(t *testing.T) { "cross-ns-tls-options-cn": { MinVersion: "VersionTLS12", ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -5803,6 +5893,21 @@ func TestCrossNamespace(t *testing.T) { "cross-ns-tls-options-cn": { MinVersion: "VersionTLS12", ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, @@ -5846,6 +5951,21 @@ func TestCrossNamespace(t *testing.T) { "cross-ns-tls-options-cn": { MinVersion: "VersionTLS12", ALPNProtocols: []string{"h2", "http/1.1", "acme-tls/1"}, + CipherSuites: []string{ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "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", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + }, }, }, }, diff --git a/pkg/tls/tls.go b/pkg/tls/tls.go index bacc3e337..3bf366805 100644 --- a/pkg/tls/tls.go +++ b/pkg/tls/tls.go @@ -32,6 +32,7 @@ type Options struct { func (o *Options) SetDefaults() { // ensure http2 enabled o.ALPNProtocols = DefaultTLSOptions.ALPNProtocols + o.CipherSuites = DefaultTLSOptions.CipherSuites } // +k8s:deepcopy-gen=true