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
#### Load-Balancing
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).
@ -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.
#### 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
SAN URI during the peer certificate verification.
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:
description: RootCAsSecrets defines a list of CA secret used to validate
self-signed certificate.
description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items:
type: string
type: array
@ -2373,9 +2397,33 @@ spec:
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.
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:
description: RootCAsSecrets defines a list of CA secret used to
validate self-signed certificates.
description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items:
type: string
type: array

View File

@ -113,9 +113,33 @@ spec:
description: PeerCertURI defines the peer cert URI used to match against
SAN URI during the peer certificate verification.
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:
description: RootCAsSecrets defines a list of CA secret used to validate
self-signed certificate.
description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items:
type: string
type: array

View File

@ -89,9 +89,33 @@ spec:
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.
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:
description: RootCAsSecrets defines a list of CA secret used to
validate self-signed certificates.
description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items:
type: string
type: array

View File

@ -1851,9 +1851,9 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore
spec:
serverName: foobar # [1]
insecureSkipVerify: true # [2]
rootCAsSecrets: # [3]
- foobar
- foobar
rootCAs: # [3]
- configMap: foobar
- secret: foobar
certificatesSecrets: # [4]
- foobar
- foobar
@ -1872,10 +1872,10 @@ Register the `TLSStore` kind in the Kubernetes cluster before creating `TLSStore
```
| Ref | Attribute | Purpose |
|------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|------|-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [1] | `serverName` | ServerName used to contact the server. |
| [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. |
| [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. |
@ -1960,9 +1960,9 @@ The `default@internal` serversTransportTCP is created from the [static configura
serverName: foobar # [5]
insecureSkipVerify: true # [6]
peerCertURI: foobar # [7]
rootCAsSecrets: # [8]
- foobar
- foobar
rootCAs: # [8]
- secret: foobar
- configMap: foobar
certificatesSecrets: # [9]
- foobar
- foobar
@ -1982,7 +1982,7 @@ The `default@internal` serversTransportTCP is created from the [static configura
| [5] | `serverName` | ServerName used to contact the server. |
| [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. |
| [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. |
| [10] | `spiffe` | The SPIFFE configuration. |
| [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
SAN URI during the peer certificate verification.
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:
description: RootCAsSecrets defines a list of CA secret used to validate
self-signed certificate.
description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items:
type: string
type: array
@ -2373,9 +2397,33 @@ spec:
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.
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:
description: RootCAsSecrets defines a list of CA secret used to
validate self-signed certificates.
description: |-
RootCAsSecrets defines a list of CA secret used to validate self-signed certificate.
Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
items:
type: string
type: array

View File

@ -331,8 +331,8 @@ type HealthCheck struct{}
type ServersTransport struct {
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"`
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"`
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"`
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"`
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"`

View File

@ -159,8 +159,8 @@ type TCPServersTransport struct {
type TLSClientConfig struct {
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"`
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"`
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"`
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"`
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"`
}

View File

@ -50,6 +50,7 @@ type Client interface {
GetSecret(namespace, name string) (*corev1.Secret, bool, error)
GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error)
GetNodes() ([]*corev1.Node, bool, error)
GetConfigMap(namespace, name string) (*corev1.ConfigMap, bool, error)
}
// 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 {
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))
_, 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
}
// 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) {
nodes, err := c.clusterScopeFactory.Core().V1().Nodes().Lister().List(labels.Everything())
exist, err := translateNotFoundError(err)

View File

@ -48,6 +48,26 @@ data:
ca.crt: VEVTVFJPT1RDQVM0
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
kind: Secret
@ -82,6 +102,26 @@ data:
tls.crt: VEVTVENFUlQz
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
kind: ServersTransportTCP
@ -101,6 +141,12 @@ spec:
- root-ca3
- root-ca4
- 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:
- mtls1
- mtls2

View File

@ -1,4 +1,10 @@
apiVersion: v1
kind: Namespace
metadata:
name: foo
---
apiVersion: v1
kind: Secret
metadata:
name: root-ca0
@ -48,6 +54,26 @@ data:
ca.crt: VEVTVFJPT1RDQVM0
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
kind: Secret
@ -82,6 +108,26 @@ data:
tls.crt: VEVTVENFUlQz
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
kind: ServersTransport
@ -102,6 +148,12 @@ spec:
- root-ca3
- root-ca4
- 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:
- mtls1
- mtls2

View File

@ -340,19 +340,61 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
}
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
for _, secret := range serversTransport.Spec.RootCAsSecrets {
caSecret, err := loadCASecret(serversTransport.Namespace, secret, client)
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
}
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
for _, secret := range serversTransport.Spec.CertificatesSecrets {
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 len(serversTransportTCP.Spec.TLS.RootCAsSecrets) > 0 {
logger.Warn().Msg("RootCAsSecrets option is deprecated, please use the RootCA option instead.")
}
var rootCAs []types.FileOrContent
for _, secret := range serversTransportTCP.Spec.TLS.RootCAsSecrets {
caSecret, err := loadCASecret(serversTransportTCP.Namespace, secret, client)
if err != nil {
logger.Error().
Err(err).
Str("rootCAs", secret).
Msg("Error while loading rootCAs")
Str("secret", secret).
Msg("Error while loading CA Secret")
continue
}
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
for _, secret := range serversTransportTCP.Spec.TLS.CertificatesSecrets {
tlsCert, tlsKey, err := loadAuthTLSSecret(serversTransportTCP.Namespace, secret, client)
@ -936,7 +1014,7 @@ func loadCASecret(namespace, secretName string, k8sClient Client) (string, error
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 {
// 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 {
@ -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) {
@ -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)
}
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{} {
if throttleDuration == 0 {
return nil

View File

@ -1491,7 +1491,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) {
TLS: &dynamic.TLSClientConfig{
ServerName: "test",
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{
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},
@ -4734,7 +4734,7 @@ func TestLoadIngressRoutes(t *testing.T) {
"foo-test": {
ServerName: "test",
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{
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},

View File

@ -31,7 +31,10 @@ type ServersTransportSpec struct {
ServerName string `json:"serverName,omitempty"`
// InsecureSkipVerify disables SSL certificate verification.
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.
// Deprecated: RootCAsSecrets is deprecated, please use the RootCAs option instead.
RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"`
// CertificatesSecrets defines a list of secret storing client certificates for mTLS.
CertificatesSecrets []string `json:"certificatesSecrets,omitempty"`
@ -74,6 +77,20 @@ type ForwardingTimeouts struct {
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
// ServersTransportList is a collection of ServersTransport resources.

View File

@ -49,7 +49,10 @@ type TLSClientConfig struct {
ServerName string `json:"serverName,omitempty"`
// InsecureSkipVerify disables TLS certificate verification.
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"`
// CertificatesSecrets defines a list of secret storing client certificates for mTLS.
CertificatesSecrets []string `json:"certificatesSecrets,omitempty"`

View File

@ -1162,6 +1162,32 @@ func (in *Retry) DeepCopy() *Retry {
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.
func (in *Route) DeepCopyInto(out *Route) {
*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.
func (in *ServersTransportSpec) DeepCopyInto(out *ServersTransportSpec) {
*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 {
in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
*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.
func (in *TLSClientConfig) DeepCopyInto(out *TLSClientConfig) {
*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 {
in, out := &in.RootCAsSecrets, &out.RootCAsSecrets
*out = make([]string, len(*in))