Allow root CA to be added through config maps

This commit is contained in:
Nelson Isioma 2025-03-11 15:38:05 +01:00 committed by GitHub
parent 30fe11eccf
commit ae4a00b4bc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 516 additions and 48 deletions

View File

@ -192,6 +192,8 @@ To be consistent with the naming and other metrics providers, the metric now rep
### Kubernetes CRD Provider ### Kubernetes CRD Provider
#### Load-Balancing
In `v3.4`, the HTTP service definition has been updated. In `v3.4`, the HTTP service definition has been updated.
The strategy field now supports two new values: `wrr` and `p2c` (please refer to the [HTTP Services Load Balancing documentation](../../routing/services/#load-balancing-strategy) for more details). The strategy field now supports two new values: `wrr` and `p2c` (please refer to the [HTTP Services Load Balancing documentation](../../routing/services/#load-balancing-strategy) for more details).
@ -202,3 +204,37 @@ kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/con
``` ```
Please note that the `RoundRobin` strategy value is now deprecated, but still supported and equivalent to `wrr`, and will be removed in the next major release. Please note that the `RoundRobin` strategy value is now deprecated, but still supported and equivalent to `wrr`, and will be removed in the next major release.
#### ServersTransport CA Certificate
In `v3.4`, a new `rootCAs` option has been added to the `ServersTransport` and `ServersTransportTCP` CRDs.
It allows the configuration of CA certificates from both `ConfigMaps` and `Secrets`,
and replaces the `rootCAsSecrets` option, as shown below:
```yaml
---
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
name: foo
namespace: bar
spec:
rootCAs:
- configMap: ca-config-map
- secret: ca-secret
---
apiVersion: traefik.io/v1alpha1
kind: ServersTransportTCP
metadata:
name: foo
namespace: bar
spec:
rootCAs:
- configMap: ca-config-map
- secret: ca-secret
```
The `rootCAsSecrets` option, which allows only `Secrets` references,
is still supported, but is now deprecated,
and will be removed in the next major release.

View File

@ -2252,9 +2252,33 @@ spec:
description: PeerCertURI defines the peer cert URI used to match against description: PeerCertURI defines the peer cert URI used to match against
SAN URI during the peer certificate verification. SAN URI during the peer certificate verification.
type: string type: string
rootCAs:
description: RootCAs defines a list of CA certificate Secrets or ConfigMaps
used to validate server certificates.
items:
description: |-
RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
properties:
configMap:
description: |-
ConfigMap defines the name of a ConfigMap that holds a CA certificate.
The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
type: string
secret:
description: |-
Secret defines the name of a Secret that holds a CA certificate.
The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
type: string
type: object
x-kubernetes-validations:
- message: RootCA cannot have both Secret and ConfigMap defined.
rule: has(self.secret) && has(self.configMap)
type: array
rootCAsSecrets: rootCAsSecrets:
description: RootCAsSecrets defines a list of CA secret used to validate description: |-
self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
type: array type: array
@ -2373,9 +2397,33 @@ spec:
MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host.
PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification.
type: string type: string
rootCAs:
description: RootCAs defines a list of CA certificate Secrets
or ConfigMaps used to validate server certificates.
items:
description: |-
RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
properties:
configMap:
description: |-
ConfigMap defines the name of a ConfigMap that holds a CA certificate.
The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
type: string
secret:
description: |-
Secret defines the name of a Secret that holds a CA certificate.
The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
type: string
type: object
x-kubernetes-validations:
- message: RootCA cannot have both Secret and ConfigMap defined.
rule: has(self.secret) && has(self.configMap)
type: array
rootCAsSecrets: rootCAsSecrets:
description: RootCAsSecrets defines a list of CA secret used to description: |-
validate self-signed certificates. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
type: array type: array

View File

@ -113,9 +113,33 @@ spec:
description: PeerCertURI defines the peer cert URI used to match against description: PeerCertURI defines the peer cert URI used to match against
SAN URI during the peer certificate verification. SAN URI during the peer certificate verification.
type: string type: string
rootCAs:
description: RootCAs defines a list of CA certificate Secrets or ConfigMaps
used to validate server certificates.
items:
description: |-
RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
properties:
configMap:
description: |-
ConfigMap defines the name of a ConfigMap that holds a CA certificate.
The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
type: string
secret:
description: |-
Secret defines the name of a Secret that holds a CA certificate.
The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
type: string
type: object
x-kubernetes-validations:
- message: RootCA cannot have both Secret and ConfigMap defined.
rule: has(self.secret) && has(self.configMap)
type: array
rootCAsSecrets: rootCAsSecrets:
description: RootCAsSecrets defines a list of CA secret used to validate description: |-
self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
type: array type: array

View File

@ -89,9 +89,33 @@ spec:
MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host.
PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification.
type: string type: string
rootCAs:
description: RootCAs defines a list of CA certificate Secrets
or ConfigMaps used to validate server certificates.
items:
description: |-
RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
properties:
configMap:
description: |-
ConfigMap defines the name of a ConfigMap that holds a CA certificate.
The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
type: string
secret:
description: |-
Secret defines the name of a Secret that holds a CA certificate.
The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
type: string
type: object
x-kubernetes-validations:
- message: RootCA cannot have both Secret and ConfigMap defined.
rule: has(self.secret) && has(self.configMap)
type: array
rootCAsSecrets: rootCAsSecrets:
description: RootCAsSecrets defines a list of CA secret used to description: |-
validate self-signed certificates. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
type: array type: array

View File

@ -1851,9 +1851,9 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore
spec: spec:
serverName: foobar # [1] serverName: foobar # [1]
insecureSkipVerify: true # [2] insecureSkipVerify: true # [2]
rootCAsSecrets: # [3] rootCAs: # [3]
- foobar - configMap: foobar
- foobar - secret: foobar
certificatesSecrets: # [4] certificatesSecrets: # [4]
- foobar - foobar
- foobar - foobar
@ -1871,22 +1871,22 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore
trustDomain: "spiffe://trust-domain" # [14] trustDomain: "spiffe://trust-domain" # [14]
``` ```
| Ref | Attribute | Purpose | | Ref | Attribute | Purpose |
|------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [1] | `serverName` | ServerName used to contact the server. | | [1] | `serverName` | ServerName used to contact the server. |
| [2] | `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | | [2] | `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. |
| [3] | `rootCAsSecrets` | Defines the set of root certificate authorities to use when verifying server certificates. The secret must contain a certificate under either a tls.ca or a ca.crt key. | | [3] | `rootCAs` | Defines the set of root certificate authorities to use when verifying server certificates. The referenced Secret or ConfigMap must contain a certificate under either a tls.ca or a ca.crt key. |
| [4] | `certificatesSecrets` | Certificates to present to the server for mTLS. | | [4] | `certificatesSecrets` | Certificates to present to the server for mTLS. |
| [5] | `maxIdleConnsPerHost` | Controls the maximum idle (keep-alive) connections to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. | | [5] | `maxIdleConnsPerHost` | Controls the maximum idle (keep-alive) connections to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. |
| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the servers. | | [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the servers. |
| [7] | `dialTimeout` | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists. | | [7] | `dialTimeout` | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists. |
| [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | | [8] | `responseHeaderTimeout` | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. |
| [9] | `idleConnTimeout` | The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout exists. | | [9] | `idleConnTimeout` | The maximum amount of time an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout exists. |
| [10] | `peerCertURI` | URI used to match against SAN URIs during the server's certificate verification. | | [10] | `peerCertURI` | URI used to match against SAN URIs during the server's certificate verification. |
| [11] | `disableHTTP2` | Disables HTTP/2 for connections with servers. | | [11] | `disableHTTP2` | Disables HTTP/2 for connections with servers. |
| [12] | `spiffe` | The spiffe configuration. | | [12] | `spiffe` | The spiffe configuration. |
| [13] | `ids` | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). | | [13] | `ids` | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). |
| [14] | `trustDomain` | Defines the allowed SPIFFE trust domain. | | [14] | `trustDomain` | Defines the allowed SPIFFE trust domain. |
!!! info "CA Secret" !!! info "CA Secret"
@ -1960,9 +1960,9 @@ The `default@internal` serversTransportTCP is created from the [static configura
serverName: foobar # [5] serverName: foobar # [5]
insecureSkipVerify: true # [6] insecureSkipVerify: true # [6]
peerCertURI: foobar # [7] peerCertURI: foobar # [7]
rootCAsSecrets: # [8] rootCAs: # [8]
- foobar - secret: foobar
- foobar - configMap: foobar
certificatesSecrets: # [9] certificatesSecrets: # [9]
- foobar - foobar
- foobar - foobar
@ -1982,7 +1982,7 @@ The `default@internal` serversTransportTCP is created from the [static configura
| [5] | `serverName` | ServerName used to contact the server. | | [5] | `serverName` | ServerName used to contact the server. |
| [6] | `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | | [6] | `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. |
| [7] | `peerCertURI` | URI used to match against SAN URIs during the server's certificate verification. | | [7] | `peerCertURI` | URI used to match against SAN URIs during the server's certificate verification. |
| [8] | `rootCAsSecrets` | Defines the set of root certificate authorities to use when verifying server certificates. The secret must contain a certificate under either a tls.ca or a ca.crt key. | | [8] | `rootCAs` | Defines the set of root certificate authorities to use when verifying server certificates. The referenced Secret or ConfigMap must contain a certificate under either a tls.ca or a ca.crt key. |
| [9] | `certificatesSecrets` | Certificates to present to the server for mTLS. | | [9] | `certificatesSecrets` | Certificates to present to the server for mTLS. |
| [10] | `spiffe` | The SPIFFE configuration. | | [10] | `spiffe` | The SPIFFE configuration. |
| [11] | `ids` | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). | | [11] | `ids` | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). |

View File

@ -2252,9 +2252,33 @@ spec:
description: PeerCertURI defines the peer cert URI used to match against description: PeerCertURI defines the peer cert URI used to match against
SAN URI during the peer certificate verification. SAN URI during the peer certificate verification.
type: string type: string
rootCAs:
description: RootCAs defines a list of CA certificate Secrets or ConfigMaps
used to validate server certificates.
items:
description: |-
RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
properties:
configMap:
description: |-
ConfigMap defines the name of a ConfigMap that holds a CA certificate.
The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
type: string
secret:
description: |-
Secret defines the name of a Secret that holds a CA certificate.
The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
type: string
type: object
x-kubernetes-validations:
- message: RootCA cannot have both Secret and ConfigMap defined.
rule: has(self.secret) && has(self.configMap)
type: array
rootCAsSecrets: rootCAsSecrets:
description: RootCAsSecrets defines a list of CA secret used to validate description: |-
self-signed certificate. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
type: array type: array
@ -2373,9 +2397,33 @@ spec:
MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host.
PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification. PeerCertURI defines the peer cert URI used to match against SAN URI during the peer certificate verification.
type: string type: string
rootCAs:
description: RootCAs defines a list of CA certificate Secrets
or ConfigMaps used to validate server certificates.
items:
description: |-
RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
properties:
configMap:
description: |-
ConfigMap defines the name of a ConfigMap that holds a CA certificate.
The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
type: string
secret:
description: |-
Secret defines the name of a Secret that holds a CA certificate.
The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
type: string
type: object
x-kubernetes-validations:
- message: RootCA cannot have both Secret and ConfigMap defined.
rule: has(self.secret) && has(self.configMap)
type: array
rootCAsSecrets: rootCAsSecrets:
description: RootCAsSecrets defines a list of CA secret used to description: |-
validate self-signed certificates. RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items: items:
type: string type: string
type: array type: array

View File

@ -331,8 +331,8 @@ type HealthCheck struct{}
type ServersTransport struct { type ServersTransport struct {
ServerName string `description:"Defines the serverName used to contact the server." json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"` ServerName string `description:"Defines the serverName used to contact the server." json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"`
InsecureSkipVerify bool `description:"Disables SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` 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 secret used to validate self-signed certificate" json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` 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 secret storing client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` Certificates traefiktls.Certificates `description:"Defines a list of client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,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"` 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"` 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"` DisableHTTP2 bool `description:"Disables HTTP/2 for connections with backend servers." json:"disableHTTP2,omitempty" toml:"disableHTTP2,omitempty" yaml:"disableHTTP2,omitempty" export:"true"`

View File

@ -159,8 +159,8 @@ type TCPServersTransport struct {
type TLSClientConfig struct { type TLSClientConfig struct {
ServerName string `description:"Defines the serverName used to contact the server." json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"` ServerName string `description:"Defines the serverName used to contact the server." json:"serverName,omitempty" toml:"serverName,omitempty" yaml:"serverName,omitempty"`
InsecureSkipVerify bool `description:"Disables SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` 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 secret used to validate self-signed certificate" json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` 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 secret storing client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` Certificates traefiktls.Certificates `description:"Defines a list of client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"`
PeerCertURI string `description:"Defines the URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"` PeerCertURI string `description:"Defines the URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"`
Spiffe *Spiffe `description:"Defines the SPIFFE TLS configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` Spiffe *Spiffe `description:"Defines the SPIFFE TLS configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
} }

View File

@ -50,6 +50,7 @@ type Client interface {
GetSecret(namespace, name string) (*corev1.Secret, bool, error) GetSecret(namespace, name string) (*corev1.Secret, bool, error)
GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error) GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error)
GetNodes() ([]*corev1.Node, bool, error) GetNodes() ([]*corev1.Node, bool, error)
GetConfigMap(namespace, name string) (*corev1.ConfigMap, bool, error)
} }
// TODO: add tests for the clientWrapper (and its methods) itself. // TODO: add tests for the clientWrapper (and its methods) itself.
@ -227,6 +228,10 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
if err != nil { if err != nil {
return nil, err return nil, err
} }
_, err = factoryKube.Core().V1().ConfigMaps().Informer().AddEventHandler(eventHandler)
if err != nil {
return nil, err
}
factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm))
_, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) _, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
@ -478,6 +483,17 @@ func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool,
return secret, exist, err return secret, exist, err
} }
// GetConfigMap returns the named config map from the given namespace.
func (c *clientWrapper) GetConfigMap(namespace, name string) (*corev1.ConfigMap, bool, error) {
if !c.isWatchedNamespace(namespace) {
return nil, false, fmt.Errorf("failed to get config map %s/%s: namespace is not within watched namespaces", namespace, name)
}
configMap, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name)
exist, err := translateNotFoundError(err)
return configMap, exist, err
}
func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) { func (c *clientWrapper) GetNodes() ([]*corev1.Node, bool, error) {
nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything()) nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
exist, err := translateNotFoundError(err) exist, err := translateNotFoundError(err)

View File

@ -48,6 +48,26 @@ data:
ca.crt: VEVTVFJPT1RDQVM0 ca.crt: VEVTVFJPT1RDQVM0
tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the preferred one. tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the preferred one.
---
apiVersion: v1
kind: Secret
metadata:
name: root-ca5
namespace: foo
data:
ca.crt: VEVTVFJPT1RDQVM2
---
apiVersion: v1
kind: Secret
metadata:
name: root-ca6
namespace: foo
data:
ca.crt: VEVTVFJPT1RDQVM3
--- ---
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
@ -82,6 +102,26 @@ data:
tls.crt: VEVTVENFUlQz tls.crt: VEVTVENFUlQz
tls.key: VEVTVEtFWTM= tls.key: VEVTVEtFWTM=
---
apiVersion: v1
kind: ConfigMap
metadata:
name: root-ca-as-config-map
namespace: foo
data:
ca.crt: "TESTROOTCASFROMCONFIGMAP"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: root-ca-as-config-map-2
namespace: foo
data:
ca.crt: "TESTROOTCASFROMCONFIGMAP2"
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: ServersTransportTCP kind: ServersTransportTCP
@ -101,6 +141,12 @@ spec:
- root-ca3 - root-ca3
- root-ca4 - root-ca4
- allcerts - allcerts
rootCAs:
- configMap: root-ca-as-config-map
- secret: root-ca5
# referencing both a ConfigMap and a Secret should fail.
- configMap: root-ca-as-config-map-2
secret: root-ca6
certificatesSecrets: certificatesSecrets:
- mtls1 - mtls1
- mtls2 - mtls2

View File

@ -1,4 +1,10 @@
apiVersion: v1 apiVersion: v1
kind: Namespace
metadata:
name: foo
---
apiVersion: v1
kind: Secret kind: Secret
metadata: metadata:
name: root-ca0 name: root-ca0
@ -48,6 +54,26 @@ data:
ca.crt: VEVTVFJPT1RDQVM0 ca.crt: VEVTVFJPT1RDQVM0
tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the preferred one. tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the preferred one.
---
apiVersion: v1
kind: Secret
metadata:
name: root-ca5
namespace: foo
data:
ca.crt: VEVTVFJPT1RDQVM2
---
apiVersion: v1
kind: Secret
metadata:
name: root-ca6
namespace: foo
data:
ca.crt: VEVTVFJPT1RDQVM3
--- ---
apiVersion: v1 apiVersion: v1
kind: Secret kind: Secret
@ -82,6 +108,26 @@ data:
tls.crt: VEVTVENFUlQz tls.crt: VEVTVENFUlQz
tls.key: VEVTVEtFWTM= tls.key: VEVTVEtFWTM=
---
apiVersion: v1
kind: ConfigMap
metadata:
name: root-ca-as-config-map
namespace: foo
data:
ca.crt: "TESTROOTCASFROMCONFIGMAP"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: root-ca-as-config-map-2
namespace: foo
data:
ca.crt: "TESTROOTCASFROMCONFIGMAP2"
--- ---
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: ServersTransport kind: ServersTransport
@ -102,6 +148,12 @@ spec:
- root-ca3 - root-ca3
- root-ca4 - root-ca4
- allcerts - allcerts
rootCAs:
- configMap: root-ca-as-config-map
- secret: root-ca5
# referencing both a ConfigMap and a Secret should fail.
- configMap: root-ca-as-config-map-2
secret: root-ca6
certificatesSecrets: certificatesSecrets:
- mtls1 - mtls1
- mtls2 - mtls2

View File

@ -340,19 +340,61 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
} }
for _, serversTransport := range client.GetServersTransports() { for _, serversTransport := range client.GetServersTransports() {
logger := log.Ctx(ctx).With().Str(logs.ServersTransportName, serversTransport.Name).Logger() logger := log.Ctx(ctx).With().
Str(logs.ServersTransportName, serversTransport.Name).
Str("namespace", serversTransport.Namespace).
Logger()
if len(serversTransport.Spec.RootCAsSecrets) > 0 {
logger.Warn().Msg("RootCAsSecrets option is deprecated, please use the RootCA option instead.")
}
var rootCAs []types.FileOrContent var rootCAs []types.FileOrContent
for _, secret := range serversTransport.Spec.RootCAsSecrets { for _, secret := range serversTransport.Spec.RootCAsSecrets {
caSecret, err := loadCASecret(serversTransport.Namespace, secret, client) caSecret, err := loadCASecret(serversTransport.Namespace, secret, client)
if err != nil { if err != nil {
logger.Error().Err(err).Msgf("Error while loading rootCAs %s", secret) logger.Error().
Err(err).
Str("secret", secret).
Msg("Error while loading CA Secret")
continue continue
} }
rootCAs = append(rootCAs, types.FileOrContent(caSecret)) rootCAs = append(rootCAs, types.FileOrContent(caSecret))
} }
for _, rootCA := range serversTransport.Spec.RootCAs {
if rootCA.Secret != nil && rootCA.ConfigMap != nil {
logger.Error().Msg("Error while loading CA: both Secret and ConfigMap are defined")
continue
}
if rootCA.Secret != nil {
ca, err := loadCASecret(serversTransport.Namespace, *rootCA.Secret, client)
if err != nil {
logger.Error().
Err(err).
Str("secret", *rootCA.Secret).
Msg("Error while loading CA Secret")
continue
}
rootCAs = append(rootCAs, types.FileOrContent(ca))
continue
}
ca, err := loadCAConfigMap(serversTransport.Namespace, *rootCA.ConfigMap, client)
if err != nil {
logger.Error().
Err(err).
Str("configMap", *rootCA.ConfigMap).
Msg("Error while loading CA ConfigMap")
continue
}
rootCAs = append(rootCAs, types.FileOrContent(ca))
}
var certs tls.Certificates var certs tls.Certificates
for _, secret := range serversTransport.Spec.CertificatesSecrets { for _, secret := range serversTransport.Spec.CertificatesSecrets {
tlsSecret, tlsKey, err := loadAuthTLSSecret(serversTransport.Namespace, secret, client) tlsSecret, tlsKey, err := loadAuthTLSSecret(serversTransport.Namespace, secret, client)
@ -449,20 +491,56 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
} }
if serversTransportTCP.Spec.TLS != nil { if serversTransportTCP.Spec.TLS != nil {
if len(serversTransportTCP.Spec.TLS.RootCAsSecrets) > 0 {
logger.Warn().Msg("RootCAsSecrets option is deprecated, please use the RootCA option instead.")
}
var rootCAs []types.FileOrContent var rootCAs []types.FileOrContent
for _, secret := range serversTransportTCP.Spec.TLS.RootCAsSecrets { for _, secret := range serversTransportTCP.Spec.TLS.RootCAsSecrets {
caSecret, err := loadCASecret(serversTransportTCP.Namespace, secret, client) caSecret, err := loadCASecret(serversTransportTCP.Namespace, secret, client)
if err != nil { if err != nil {
logger.Error(). logger.Error().
Err(err). Err(err).
Str("rootCAs", secret). Str("secret", secret).
Msg("Error while loading rootCAs") Msg("Error while loading CA Secret")
continue continue
} }
rootCAs = append(rootCAs, types.FileOrContent(caSecret)) rootCAs = append(rootCAs, types.FileOrContent(caSecret))
} }
for _, rootCA := range serversTransportTCP.Spec.TLS.RootCAs {
if rootCA.Secret != nil && rootCA.ConfigMap != nil {
logger.Error().Msg("Error while loading CA: both Secret and ConfigMap are defined")
continue
}
if rootCA.Secret != nil {
ca, err := loadCASecret(serversTransportTCP.Namespace, *rootCA.Secret, client)
if err != nil {
logger.Error().
Err(err).
Str("secret", *rootCA.Secret).
Msg("Error while loading CA Secret")
continue
}
rootCAs = append(rootCAs, types.FileOrContent(ca))
continue
}
ca, err := loadCAConfigMap(serversTransportTCP.Namespace, *rootCA.ConfigMap, client)
if err != nil {
logger.Error().
Err(err).
Str("configMap", *rootCA.ConfigMap).
Msg("Error while loading CA ConfigMap")
continue
}
rootCAs = append(rootCAs, types.FileOrContent(ca))
}
var certs tls.Certificates var certs tls.Certificates
for _, secret := range serversTransportTCP.Spec.TLS.CertificatesSecrets { for _, secret := range serversTransportTCP.Spec.TLS.CertificatesSecrets {
tlsCert, tlsKey, err := loadAuthTLSSecret(serversTransportTCP.Namespace, secret, client) tlsCert, tlsKey, err := loadAuthTLSSecret(serversTransportTCP.Namespace, secret, client)
@ -936,7 +1014,7 @@ func loadCASecret(namespace, secretName string, k8sClient Client) (string, error
return tlsCAData, nil return tlsCAData, nil
} }
// TODO: remove this behavior in the next major version (v3) // TODO: remove this behavior in the next major version (v4)
if len(secret.Data) == 1 { if len(secret.Data) == 1 {
// For backwards compatibility, use the only available secret data as CA if both 'ca.crt' and 'tls.ca' are missing. // For backwards compatibility, use the only available secret data as CA if both 'ca.crt' and 'tls.ca' are missing.
for _, v := range secret.Data { for _, v := range secret.Data {
@ -944,7 +1022,29 @@ func loadCASecret(namespace, secretName string, k8sClient Client) (string, error
} }
} }
return "", fmt.Errorf("could not find CA block: %w", err) return "", fmt.Errorf("secret '%s/%s' has no CA block: %w", namespace, secretName, err)
}
func loadCAConfigMap(namespace, name string, k8sClient Client) (string, error) {
configMap, ok, err := k8sClient.GetConfigMap(namespace, name)
if err != nil {
return "", fmt.Errorf("failed to fetch configMap '%s/%s': %w", namespace, name, err)
}
if !ok {
return "", fmt.Errorf("configMap '%s/%s' not found", namespace, name)
}
if configMap == nil {
return "", fmt.Errorf("data for configMap '%s/%s' must not be nil", namespace, name)
}
tlsCAData, err := getCABlocksFromConfigMap(configMap, namespace, name)
if err == nil {
return tlsCAData, nil
}
return "", fmt.Errorf("configMap '%s/%s' has no CA block: %w", namespace, name, err)
} }
func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, string, error) { func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, string, error) {
@ -1384,6 +1484,20 @@ func getCABlocks(secret *corev1.Secret, namespace, secretName string) (string, e
return "", fmt.Errorf("secret %s/%s contains neither tls.ca nor ca.crt", namespace, secretName) return "", fmt.Errorf("secret %s/%s contains neither tls.ca nor ca.crt", namespace, secretName)
} }
func getCABlocksFromConfigMap(configMap *corev1.ConfigMap, namespace, name string) (string, error) {
tlsCrtData, tlsCrtExists := configMap.Data["tls.ca"]
if tlsCrtExists {
return tlsCrtData, nil
}
tlsCrtData, tlsCrtExists = configMap.Data["ca.crt"]
if tlsCrtExists {
return tlsCrtData, nil
}
return "", fmt.Errorf("config map %s/%s contains neither tls.ca nor ca.crt", namespace, name)
}
func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} { func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} {
if throttleDuration == 0 { if throttleDuration == 0 {
return nil return nil

View File

@ -1491,7 +1491,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
TLS: &dynamic.TLSClientConfig{ TLS: &dynamic.TLSClientConfig{
ServerName: "test", ServerName: "test",
InsecureSkipVerify: true, InsecureSkipVerify: true,
RootCAs: []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"}, RootCAs: []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS", "TESTROOTCASFROMCONFIGMAP", "TESTROOTCAS6"},
Certificates: tls.Certificates{ Certificates: tls.Certificates{
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"}, {CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"}, {CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},
@ -4734,7 +4734,7 @@ func TestLoadIngressRoutes(t *testing.T) {
"foo-test": { "foo-test": {
ServerName: "test", ServerName: "test",
InsecureSkipVerify: true, InsecureSkipVerify: true,
RootCAs: []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"}, RootCAs: []types.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS", "TESTROOTCASFROMCONFIGMAP", "TESTROOTCAS6"},
Certificates: tls.Certificates{ Certificates: tls.Certificates{
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"}, {CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"}, {CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},

View File

@ -31,7 +31,10 @@ type ServersTransportSpec struct {
ServerName string `json:"serverName,omitempty"` ServerName string `json:"serverName,omitempty"`
// InsecureSkipVerify disables SSL certificate verification. // InsecureSkipVerify disables SSL certificate verification.
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
// RootCAs defines a list of CA certificate Secrets or ConfigMaps used to validate server certificates.
RootCAs []RootCA `json:"rootCAs,omitempty"`
// RootCAsSecrets defines a list of CA secret used to validate self-signed certificate. // RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
// Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"` RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"`
// CertificatesSecrets defines a list of secret storing client certificates for mTLS. // CertificatesSecrets defines a list of secret storing client certificates for mTLS.
CertificatesSecrets []string `json:"certificatesSecrets,omitempty"` CertificatesSecrets []string `json:"certificatesSecrets,omitempty"`
@ -74,6 +77,20 @@ type ForwardingTimeouts struct {
PingTimeout *intstr.IntOrString `json:"pingTimeout,omitempty"` PingTimeout *intstr.IntOrString `json:"pingTimeout,omitempty"`
} }
// +k8s:deepcopy-gen=true
// RootCA defines a reference to a Secret or a ConfigMap that holds a CA certificate.
// If both a Secret and a ConfigMap reference are defined, the Secret reference takes precedence.
// +kubebuilder:validation:XValidation:rule="has(self.secret) && has(self.configMap)",message="RootCA cannot have both Secret and ConfigMap defined."
type RootCA struct {
// Secret defines the name of a Secret that holds a CA certificate.
// The referenced Secret must contain a certificate under either a tls.ca or a ca.crt key.
Secret *string `json:"secret,omitempty"`
// ConfigMap defines the name of a ConfigMap that holds a CA certificate.
// The referenced ConfigMap must contain a certificate under either a tls.ca or a ca.crt key.
ConfigMap *string `json:"configMap,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ServersTransportList is a collection of ServersTransport resources. // ServersTransportList is a collection of ServersTransport resources.

View File

@ -49,7 +49,10 @@ type TLSClientConfig struct {
ServerName string `json:"serverName,omitempty"` ServerName string `json:"serverName,omitempty"`
// InsecureSkipVerify disables TLS certificate verification. // InsecureSkipVerify disables TLS certificate verification.
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"` InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
// RootCAsSecrets defines a list of CA secret used to validate self-signed certificates. // RootCAs defines a list of CA certificate Secrets or ConfigMaps used to validate server certificates.
RootCAs []RootCA `json:"rootCAs,omitempty"`
// RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
// Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"` RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"`
// CertificatesSecrets defines a list of secret storing client certificates for mTLS. // CertificatesSecrets defines a list of secret storing client certificates for mTLS.
CertificatesSecrets []string `json:"certificatesSecrets,omitempty"` CertificatesSecrets []string `json:"certificatesSecrets,omitempty"`

View File

@ -1162,6 +1162,32 @@ func (in *Retry) DeepCopy() *Retry {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RootCA) DeepCopyInto(out *RootCA) {
*out = *in
if in.Secret != nil {
in, out := &in.Secret, &out.Secret
*out = new(string)
**out = **in
}
if in.ConfigMap != nil {
in, out := &in.ConfigMap, &out.ConfigMap
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RootCA.
func (in *RootCA) DeepCopy() *RootCA {
if in == nil {
return nil
}
out := new(RootCA)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Route) DeepCopyInto(out *Route) { func (in *Route) DeepCopyInto(out *Route) {
*out = *in *out = *in
@ -1347,6 +1373,13 @@ func (in *ServersTransportList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ServersTransportSpec) DeepCopyInto(out *ServersTransportSpec) { func (in *ServersTransportSpec) DeepCopyInto(out *ServersTransportSpec) {
*out = *in *out = *in
if in.RootCAs != nil {
in, out := &in.RootCAs, &out.RootCAs
*out = make([]RootCA, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.RootCAsSecrets != nil { if in.RootCAsSecrets != nil {
in, out := &in.RootCAsSecrets, &out.RootCAsSecrets in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
*out = make([]string, len(*in)) *out = make([]string, len(*in))
@ -1593,6 +1626,13 @@ func (in *TLS) DeepCopy() *TLS {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) { func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) {
*out = *in *out = *in
if in.RootCAs != nil {
in, out := &in.RootCAs, &out.RootCAs
*out = make([]RootCA, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.RootCAsSecrets != nil { if in.RootCAsSecrets != nil {
in, out := &in.RootCAsSecrets, &out.RootCAsSecrets in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
*out = make([]string, len(*in)) *out = make([]string, len(*in))