diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index b7f30c649..da700b25f 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -364,6 +364,9 @@ serverName = "foobar" insecureSkipVerify = true rootCAs = ["foobar", "foobar"] + cipherSuites = ["foobar", "foobar"] + minVersion = "foobar" + maxVersion = "foobar" maxIdleConnsPerHost = 42 disableHTTP2 = true peerCertURI = "foobar" @@ -388,6 +391,9 @@ serverName = "foobar" insecureSkipVerify = true rootCAs = ["foobar", "foobar"] + cipherSuites = ["foobar", "foobar"] + minVersion = "foobar" + maxVersion = "foobar" maxIdleConnsPerHost = 42 disableHTTP2 = true peerCertURI = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 2ac73fd85..578423252 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -426,6 +426,11 @@ http: keyFile: foobar - certFile: foobar keyFile: foobar + cipherSuites: + - foobar + - foobar + minVersion: foobar + maxVersion: foobar maxIdleConnsPerHost: 42 forwardingTimeouts: dialTimeout: 42s @@ -451,6 +456,11 @@ http: keyFile: foobar - certFile: foobar keyFile: foobar + cipherSuites: + - foobar + - foobar + minVersion: foobar + maxVersion: foobar maxIdleConnsPerHost: 42 forwardingTimeouts: dialTimeout: 42s diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index c4f6dc076..f5e68381b 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -2217,6 +2217,12 @@ spec: items: type: string type: array + cipherSuites: + description: CipherSuites defines a list of cipher to use to contact + the backend servers. + items: + type: string + type: array disableHTTP2: description: DisableHTTP2 disables HTTP/2 for connections with backend servers. @@ -2277,6 +2283,14 @@ spec: to keep per-host. minimum: 0 type: integer + maxVersion: + description: MaxVersion defines TLS maximum version to use to contact + the backend servers. + type: string + minVersion: + description: MinVersion defines TLS minimum version to use to contact + the backend servers. + type: string peerCertURI: description: PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index a7c1b217f..c579be286 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -237,6 +237,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/serversTransports/ServersTransport0/certificates/0/keyFile` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/certificates/1/certFile` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/certificates/1/keyFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/cipherSuites/0` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/cipherSuites/1` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/disableHTTP2` | `true` | | `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/dialTimeout` | `42s` | | `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/idleConnTimeout` | `42s` | @@ -245,6 +247,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/responseHeaderTimeout` | `42s` | | `traefik/http/serversTransports/ServersTransport0/insecureSkipVerify` | `true` | | `traefik/http/serversTransports/ServersTransport0/maxIdleConnsPerHost` | `42` | +| `traefik/http/serversTransports/ServersTransport0/maxVersion` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/minVersion` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/peerCertURI` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/rootCAs/0` | `foobar` | | `traefik/http/serversTransports/ServersTransport0/rootCAs/1` | `foobar` | @@ -256,6 +260,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/serversTransports/ServersTransport1/certificates/0/keyFile` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/certificates/1/certFile` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/certificates/1/keyFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/cipherSuites/0` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/cipherSuites/1` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/disableHTTP2` | `true` | | `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/dialTimeout` | `42s` | | `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/idleConnTimeout` | `42s` | @@ -264,6 +270,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/responseHeaderTimeout` | `42s` | | `traefik/http/serversTransports/ServersTransport1/insecureSkipVerify` | `true` | | `traefik/http/serversTransports/ServersTransport1/maxIdleConnsPerHost` | `42` | +| `traefik/http/serversTransports/ServersTransport1/maxVersion` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/minVersion` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/peerCertURI` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/rootCAs/0` | `foobar` | | `traefik/http/serversTransports/ServersTransport1/rootCAs/1` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml index 132f71685..7a2eceedd 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml @@ -49,6 +49,12 @@ spec: items: type: string type: array + cipherSuites: + description: CipherSuites defines a list of cipher to use to contact + the backend servers. + items: + type: string + type: array disableHTTP2: description: DisableHTTP2 disables HTTP/2 for connections with backend servers. @@ -109,6 +115,14 @@ spec: to keep per-host. minimum: 0 type: integer + maxVersion: + description: MaxVersion defines TLS maximum version to use to contact + the backend servers. + type: string + minVersion: + description: MinVersion defines TLS minimum version to use to contact + the backend servers. + type: string peerCertURI: description: PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. diff --git a/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md b/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md index 89d7b31c4..451f23be3 100644 --- a/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md +++ b/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md @@ -35,6 +35,11 @@ http: - "spiffe://example.org/id1" - "spiffe://example.org/id2" trustDomain: "example.org" + cipherSuites: + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + minVersion: VersionTLS12 + maxVersion: VersionTLS12 ``` ```toml tab="Structured (TOML)" @@ -46,6 +51,9 @@ http: maxIdleConnsPerHost = 100 disableHTTP2 = true peerCertURI = "spiffe://example.org/peer" + cipherSuites = ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"] + minVersion = "VersionTLS12" + maxVersion = "VersionTLS12" [http.serversTransports.mytransport.forwardingTimeouts] dialTimeout = "30s" @@ -100,6 +108,9 @@ labels: | `certificates` | Defines the list of certificates (as file paths, or data bytes) that will be set as client certificates for mTLS. | [] | No | | `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | | `rootcas` | Set of root certificate authorities to use when verifying server certificates. (for mTLS connections). | [] | No | +| `cipherSuites` | Defines a list of cipher to use to contact the backend servers. | [] | No | +| `minVersion` | Defines a TLS minimum version to use to contact the backend servers. | "" | No | +| `maxVersion` | Defines a TLS maximum version to use to contact the backend servers. | "" | No | | `maxIdleConnsPerHost` | Maximum idle (keep-alive) connections to keep per-host. | 200 | No | | `disableHTTP2` | Disables HTTP/2 for connections with servers. | false | No | | `peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md index 7c364f685..5d697d9fc 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md @@ -59,6 +59,9 @@ spec: | `serverstransport.`
`insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | | `serverstransport.`
`rootcas` | Set of root certificate authorities to use when verifying server certificates. (for mTLS connections). | | No | | `serverstransport.`
`certificatesSecrets` | Certificates to present to the server for mTLS. | | No | +| `serverstransport.`
`cipherSuites` | Defines a list of cipher to use to contact the backend servers. | [] | No | +| `serverstransport.`
`minVersion` | Defines a TLS minimum version to use to contact the backend servers. | "" | No | +| `serverstransport.`
`maxVersion` | Defines a TLS maximum version to use to contact the backend servers. | "" | No | | `serverstransport.`
`maxIdleConnsPerHost` | Maximum idle (keep-alive) connections to keep per-host. | 200 | No | | `serverstransport.`
`disableHTTP2` | Disables HTTP/2 for connections with servers. | false | No | | `serverstransport.`
`peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 66da0fae3..df509be3f 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -1869,6 +1869,11 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore - spiffe://trust-domain/id1 - spiffe://trust-domain/id2 trustDomain: "spiffe://trust-domain" # [14] + cipherSuites: # [15] + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + minVersion: VersionTLS11 # [16] + maxVersion: VersionTLS12 # [17] ``` | Ref | Attribute | Purpose | @@ -1887,6 +1892,9 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore | [12] | `spiffe` | The spiffe configuration. | | [13] | `ids` | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). | | [14] | `trustDomain` | Defines the allowed SPIFFE trust domain. | +| [15] | `cipherSuites` | Defines a list of cipher to use to contact the backend servers. | +| [16] | `minVersion` | Defines TLS minimum version to use to contact the backend servers. | +| [17] | `maxVersion` | Defines TLS maximum version to use to contact the backend servers. | !!! info "CA Secret" diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index fddf2c953..6d0fed286 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -771,6 +771,100 @@ data: ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= ``` +#### `cipherSuites` + +_Optional_ + +`cipherSuites` defines a list of cipher to use to contact the backend servers. + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + serversTransports: + mytransport: + cipherSuites: + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +``` + +```toml tab="File (TOML)" +## Dynamic configuration +[http.serversTransports.mytransport] + cipherSuites = ["TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"] +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: mytransport + namespace: default +spec: + cipherSuites: + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +``` + +#### `minVersion` + +_Optional_ + +`minVersion` defines TLS minimum version to use to contact the backend servers. + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + serversTransports: + mytransport: + minVersion: VersionTLS12 +``` + +```toml tab="File (TOML)" +## Dynamic configuration +[http.serversTransports.mytransport] + minVersion = "VersionTLS12" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: mytransport + namespace: default +spec: + minVersion: VersionTLS12 +``` + +#### `maxVersion` + +_Optional_ + +`maxVersion` defines TLS maximum version to use to contact the backend servers. + +```yaml tab="File (YAML)" +## Dynamic configuration +http: + serversTransports: + mytransport: + maxVersion: VersionTLS12 +``` + +```toml tab="File (TOML)" +## Dynamic configuration +[http.serversTransports.mytransport] + maxVersion = "VersionTLS12" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: mytransport + namespace: default +spec: + maxVersion: VersionTLS12 +``` + #### `maxIdleConnsPerHost` _Optional, Default=2_ diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index c4f6dc076..f5e68381b 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -2217,6 +2217,12 @@ spec: items: type: string type: array + cipherSuites: + description: CipherSuites defines a list of cipher to use to contact + the backend servers. + items: + type: string + type: array disableHTTP2: description: DisableHTTP2 disables HTTP/2 for connections with backend servers. @@ -2277,6 +2283,14 @@ spec: to keep per-host. minimum: 0 type: integer + maxVersion: + description: MaxVersion defines TLS maximum version to use to contact + the backend servers. + type: string + minVersion: + description: MinVersion defines TLS minimum version to use to contact + the backend servers. + type: string peerCertURI: description: PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index ef6a896f1..447ffd524 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -347,6 +347,9 @@ type ServersTransport struct { InsecureSkipVerify bool `description:"Disables SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` RootCAs []types.FileOrContent `description:"Defines a list of CA certificates used to validate server certificates." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` Certificates traefiktls.Certificates `description:"Defines a list of client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` + CipherSuites []string `description:"Defines a list of cipher to use to contact the backend servers." json:"cipherSuites,omitempty" toml:"cipherSuites,omitempty" yaml:"cipherSuites,omitempty" export:"true"` + MinVersion string `description:"Defines a TLS minimum version to use to contact the backend servers." json:"minVersion,omitempty" toml:"minVersion,omitempty" yaml:"minVersion,omitempty" export:"true"` + MaxVersion string `description:"Defines a TLS maximum version to use to contact the backend servers." json:"maxVersion,omitempty" toml:"maxVersion,omitempty" yaml:"maxVersion,omitempty" export:"true"` MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` ForwardingTimeouts *ForwardingTimeouts `description:"Defines the timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"` DisableHTTP2 bool `description:"Disables HTTP/2 for connections with backend servers." json:"disableHTTP2,omitempty" toml:"disableHTTP2,omitempty" yaml:"disableHTTP2,omitempty" export:"true"` diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index 97f8907e0..59a47804d 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -1514,6 +1514,11 @@ func (in *ServersTransport) DeepCopyInto(out *ServersTransport) { *out = make(tls.Certificates, len(*in)) copy(*out, *in) } + if in.CipherSuites != nil { + in, out := &in.CipherSuites, &out.CipherSuites + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ForwardingTimeouts != nil { in, out := &in.ForwardingTimeouts, &out.ForwardingTimeouts *out = new(ForwardingTimeouts) diff --git a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml index 496c5af30..5b1fee8f0 100644 --- a/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml +++ b/pkg/provider/kubernetes/crd/fixtures/with_servers_transport.yml @@ -169,6 +169,11 @@ spec: - spiffe://foo/buz - spiffe://bar/biz trustDomain: spiffe://lol + cipherSuites: + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + minVersion: VersionTLS11 + maxVersion: VersionTLS12 --- apiVersion: traefik.io/v1alpha1 diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index 2b374eeba..36720e899 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -409,6 +409,39 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) }) } + sTransport := &dynamic.ServersTransport{} + if serversTransport.Spec.CipherSuites != nil { + for _, cipher := range serversTransport.Spec.CipherSuites { + if _, exists := tls.CipherSuites[cipher]; exists { + sTransport.CipherSuites = append(sTransport.CipherSuites, cipher) + } else { + // CipherSuite listed in the configuration does not exist in our list + logger.Error().Msgf("invalid CipherSuite: %s", cipher) + continue + } + } + } + + if serversTransport.Spec.MinVersion != "" { + if _, exists := tls.MinVersion[serversTransport.Spec.MinVersion]; exists { + sTransport.MinVersion = serversTransport.Spec.MinVersion + } else { + // Min TLS version does not exist + logger.Error().Msgf("invalid TLS minimal version: %s", serversTransport.Spec.MinVersion) + continue + } + } + + if serversTransport.Spec.MaxVersion != "" { + if _, exists := tls.MaxVersion[serversTransport.Spec.MaxVersion]; exists { + sTransport.MaxVersion = serversTransport.Spec.MaxVersion + } else { + // Min TLS version does not exist + logger.Error().Msgf("invalid TLS maximal version: %s", serversTransport.Spec.MaxVersion) + continue + } + } + forwardingTimeout := &dynamic.ForwardingTimeouts{} forwardingTimeout.SetDefaults() @@ -455,6 +488,9 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) InsecureSkipVerify: serversTransport.Spec.InsecureSkipVerify, RootCAs: rootCAs, Certificates: certs, + CipherSuites: sTransport.CipherSuites, + MinVersion: sTransport.MinVersion, + MaxVersion: sTransport.MaxVersion, DisableHTTP2: serversTransport.Spec.DisableHTTP2, MaxIdleConnsPerHost: serversTransport.Spec.MaxIdleConnsPerHost, ForwardingTimeouts: forwardingTimeout, diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index ec6651091..b53474f66 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -4745,6 +4745,9 @@ func TestLoadIngressRoutes(t *testing.T) { {CertFile: "TESTCERT2", KeyFile: "TESTKEY2"}, {CertFile: "TESTCERT3", KeyFile: "TESTKEY3"}, }, + CipherSuites: []string{"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}, + MinVersion: "VersionTLS11", + MaxVersion: "VersionTLS12", MaxIdleConnsPerHost: 42, DisableHTTP2: true, ForwardingTimeouts: &dynamic.ForwardingTimeouts{ diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go index 08143ed45..818d7c4ca 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go @@ -38,6 +38,12 @@ type ServersTransportSpec struct { RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"` // CertificatesSecrets defines a list of secret storing client certificates for mTLS. CertificatesSecrets []string `json:"certificatesSecrets,omitempty"` + // CipherSuites defines a list of cipher to use to contact the backend servers. + CipherSuites []string `json:"cipherSuites,omitempty"` + // MinVersion defines TLS minimum version to use to contact the backend servers. + MinVersion string `json:"minVersion,omitempty"` + // MaxVersion defines TLS maximum version to use to contact the backend servers. + MaxVersion string `json:"maxVersion,omitempty"` // MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. // +kubebuilder:validation:Minimum=0 MaxIdleConnsPerHost int `json:"maxIdleConnsPerHost,omitempty"` diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go index 3e53cfae5..4ae91375f 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go @@ -1395,6 +1395,11 @@ func (in *ServersTransportSpec) DeepCopyInto(out *ServersTransportSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.CipherSuites != nil { + in, out := &in.CipherSuites, &out.CipherSuites + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ForwardingTimeouts != nil { in, out := &in.ForwardingTimeouts, &out.ForwardingTimeouts *out = new(ForwardingTimeouts) diff --git a/pkg/server/service/transport.go b/pkg/server/service/transport.go index ecce54010..f7a526fe1 100644 --- a/pkg/server/service/transport.go +++ b/pkg/server/service/transport.go @@ -169,16 +169,54 @@ func (t *TransportManager) createTLSConfig(cfg *dynamic.ServersTransport) (*tls. config = tlsconfig.MTLSClientConfig(t.spiffeX509Source, t.spiffeX509Source, spiffeAuthorizer) } - if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 || cfg.PeerCertURI != "" { + if cfg.InsecureSkipVerify || len(cfg.RootCAs) > 0 || len(cfg.ServerName) > 0 || len(cfg.Certificates) > 0 || cfg.PeerCertURI != "" || len(cfg.CipherSuites) > 0 || cfg.MaxVersion != "" || cfg.MinVersion != "" { if config != nil { return nil, errors.New("TLS and SPIFFE configuration cannot be defined at the same time") } + // map and validate the CipherSuite passed in the configuration + ciphersList := make([]uint16, 0) + if cfg.CipherSuites != nil { + for _, cipher := range cfg.CipherSuites { + if cipherID, exists := traefiktls.CipherSuites[cipher]; exists { + ciphersList = append(ciphersList, cipherID) + } else { + // CipherSuite listed in the configuration does not exist in our list + return nil, fmt.Errorf("invalid CipherSuite: %s", cipher) + } + } + } + + // Set the min TLS version if set in the config + var minVer uint16 + if cfg.MinVersion != "" { + if minConst, exists := traefiktls.MinVersion[cfg.MinVersion]; exists { + minVer = minConst + } else { + // Min TLS version does not exist + return nil, fmt.Errorf("invalid TLS minimal version: %v", minVer) + } + } + + // Set the min TLS version if set in the config + var maxVer uint16 + if cfg.MinVersion != "" { + if maxConst, exists := traefiktls.MaxVersion[cfg.MaxVersion]; exists { + maxVer = maxConst + } else { + // Max TLS version does not exist + return nil, fmt.Errorf("invalid TLS maximal version: %v", maxVer) + } + } + config = &tls.Config{ ServerName: cfg.ServerName, InsecureSkipVerify: cfg.InsecureSkipVerify, RootCAs: createRootCACertPool(cfg.RootCAs), Certificates: cfg.Certificates.GetCertificates(), + CipherSuites: ciphersList, + MinVersion: minVer, + MaxVersion: maxVer, } if cfg.PeerCertURI != "" { diff --git a/pkg/server/service/transport_test.go b/pkg/server/service/transport_test.go index 0fd3a8ab8..89dbd9384 100644 --- a/pkg/server/service/transport_test.go +++ b/pkg/server/service/transport_test.go @@ -183,6 +183,47 @@ func TestKeepConnectionWhenSameConfiguration(t *testing.T) { assert.EqualValues(t, 2, count) } +func TestCipherSuites(t *testing.T) { + srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusOK) + })) + + cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey) + require.NoError(t, err) + + srv.TLS = &tls.Config{ + Certificates: []tls.Certificate{cert}, + MaxVersion: tls.VersionTLS12, + CipherSuites: []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + }, + } + srv.StartTLS() + + transportManager := NewTransportManager(nil) + + dynamicConf := map[string]*dynamic.ServersTransport{ + "test": { + ServerName: "example.com", + RootCAs: []types.FileOrContent{types.FileOrContent(LocalhostCert)}, + CipherSuites: []string{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"}, + MaxVersion: "VersionTLS12", + }, + } + + transportManager.Update(dynamicConf) + + tr, err := transportManager.GetRoundTripper("test") + require.NoError(t, err) + + client := http.Client{Transport: tr} + + resp, err := client.Get(srv.URL) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + func TestMTLS(t *testing.T) { srv := httptest.NewUnstartedServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK)