Improve CA certificate loading from kubernetes secret
This commit is contained in:
parent
a758d18e51
commit
0a3e40332a
7 changed files with 98 additions and 30 deletions
|
@ -447,7 +447,7 @@ metadata:
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
clientAuth:
|
clientAuth:
|
||||||
# the CA certificate is extracted from key `tls.ca` of the given secrets.
|
# the CA certificate is extracted from key `tls.ca` or `ca.crt` of the given secrets.
|
||||||
secretNames:
|
secretNames:
|
||||||
- secretCA
|
- secretCA
|
||||||
clientAuthType: RequireAndVerifyClientCert
|
clientAuthType: RequireAndVerifyClientCert
|
||||||
|
|
|
@ -373,7 +373,8 @@ metadata:
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
data:
|
data:
|
||||||
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
# Must contain a certificate under either a `tls.ca` or a `ca.crt` key.
|
||||||
|
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
|
|
|
@ -1482,16 +1482,20 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre
|
||||||
sniStrict: true # [8]
|
sniStrict: true # [8]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Ref | Attribute | Purpose |
|
| Ref | Attribute | Purpose |
|
||||||
|-----|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----|-----------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [1] | `minVersion` | Defines the [minimum TLS version](../../https/tls.md#minimum-tls-version) that is acceptable |
|
| [1] | `minVersion` | Defines the [minimum TLS version](../../https/tls.md#minimum-tls-version) that is acceptable |
|
||||||
| [2] | `maxVersion` | Defines the [maximum TLS version](../../https/tls.md#maximum-tls-version) that is acceptable |
|
| [2] | `maxVersion` | Defines the [maximum TLS version](../../https/tls.md#maximum-tls-version) that is acceptable |
|
||||||
| [3] | `cipherSuites` | list of supported [cipher suites](../../https/tls.md#cipher-suites) for TLS versions up to TLS 1.2 |
|
| [3] | `cipherSuites` | list of supported [cipher suites](../../https/tls.md#cipher-suites) for TLS versions up to TLS 1.2 |
|
||||||
| [4] | `curvePreferences` | List of the [elliptic curves references](../../https/tls.md#curve-preferences) that will be used in an ECDHE handshake, in preference order |
|
| [4] | `curvePreferences` | List of the [elliptic curves references](../../https/tls.md#curve-preferences) that will be used in an ECDHE handshake, in preference order |
|
||||||
| [5] | `clientAuth` | determines the server's policy for TLS [Client Authentication](../../https/tls.md#client-authentication-mtls) |
|
| [5] | `clientAuth` | determines the server's policy for TLS [Client Authentication](../../https/tls.md#client-authentication-mtls) |
|
||||||
| [6] | `clientAuth.secretNames` | list of names of the referenced Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) (in TLSOption namespace) |
|
| [6] | `clientAuth.secretNames` | list of names of the referenced Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) (in TLSOption namespace). The secret must contain a certificate under either a `tls.ca` or a `ca.crt` key. |
|
||||||
| [7] | `clientAuth.clientAuthType` | defines the client authentication type to apply. The available values are: `NoClientCert`, `RequestClientCert`, `VerifyClientCertIfGiven` and `RequireAndVerifyClientCert` |
|
| [7] | `clientAuth.clientAuthType` | defines the client authentication type to apply. The available values are: `NoClientCert`, `RequestClientCert`, `VerifyClientCertIfGiven` and `RequireAndVerifyClientCert` |
|
||||||
| [8] | `sniStrict` | if `true`, Traefik won't allow connections from clients connections that do not specify a server_name extension |
|
| [8] | `sniStrict` | if `true`, Traefik won't allow connections from clients connections that do not specify a server_name extension |
|
||||||
|
|
||||||
|
!!! info "CA Secret"
|
||||||
|
|
||||||
|
The CA secret must contain a base64 encoded certificate under either a `tls.ca` or a `ca.crt` key.
|
||||||
|
|
||||||
??? example "Declaring and referencing a TLSOption"
|
??? example "Declaring and referencing a TLSOption"
|
||||||
|
|
||||||
|
@ -1544,6 +1548,7 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
data:
|
data:
|
||||||
|
# Must contain a certificate under either a `tls.ca` or a `ca.crt` key.
|
||||||
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
|
|
||||||
---
|
---
|
||||||
|
@ -1554,6 +1559,7 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
data:
|
data:
|
||||||
|
# Must contain a certificate under either a `tls.ca` or a `ca.crt` key.
|
||||||
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -1679,7 +1685,7 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
|
||||||
|-----|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [1] | `serverName` | ServerName used to contact the server. |
|
| [1] | `serverName` | ServerName used to contact the server. |
|
||||||
| [2] | `insecureSkipVerify` | Disable SSL certificate verification. |
|
| [2] | `insecureSkipVerify` | Disable SSL certificate verification. |
|
||||||
| [3] | `rootCAsSecrets` | Add cert file for self-signed certificate. |
|
| [3] | `rootCAsSecrets` | Add cert file for self-signed certificate. The secret must contain a certificate under either a tls.ca or a ca.crt key. |
|
||||||
| [4] | `certificatesSecrets` | Certificates for mTLS. |
|
| [4] | `certificatesSecrets` | Certificates for mTLS. |
|
||||||
| [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. |
|
| [5] | `maxIdleConnsPerHost` | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, `defaultMaxIdleConnsPerHost` is used. |
|
||||||
| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. |
|
| [6] | `forwardingTimeouts` | Timeouts for requests forwarded to the backend servers. |
|
||||||
|
@ -1687,6 +1693,10 @@ or referencing TLS stores in the [`IngressRoute`](#kind-ingressroute) / [`Ingres
|
||||||
| [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 period for which an idle HTTP keep-alive connection will remain open before closing itself. |
|
| [9] | `idleConnTimeout` | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself. |
|
||||||
|
|
||||||
|
!!! info "CA Secret"
|
||||||
|
|
||||||
|
The CA secret must contain a base64 encoded certificate under either a `tls.ca` or a `ca.crt` key.
|
||||||
|
|
||||||
??? example "Declaring and referencing a ServersTransport"
|
??? example "Declaring and referencing a ServersTransport"
|
||||||
|
|
||||||
```yaml tab="ServersTransport"
|
```yaml tab="ServersTransport"
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: rootCas0
|
||||||
|
namespace: foo
|
||||||
|
|
||||||
|
data:
|
||||||
|
foobar: VEVTVFJPT1RDQVMw
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
metadata:
|
metadata:
|
||||||
name: rootCas1
|
name: rootCas1
|
||||||
namespace: foo
|
namespace: foo
|
||||||
|
|
||||||
data:
|
data:
|
||||||
tls.ca: VEVTVFJPT1RDQVM=
|
tls.ca: VEVTVFJPT1RDQVMx
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
|
@ -17,6 +27,27 @@ metadata:
|
||||||
data:
|
data:
|
||||||
tls.ca: VEVTVFJPT1RDQVMy
|
tls.ca: VEVTVFJPT1RDQVMy
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: rootCas3
|
||||||
|
namespace: foo
|
||||||
|
|
||||||
|
data:
|
||||||
|
ca.crt: VEVTVFJPT1RDQVMz
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: rootCas4
|
||||||
|
namespace: foo
|
||||||
|
|
||||||
|
data:
|
||||||
|
ca.crt: VEVTVFJPT1RDQVM0
|
||||||
|
tls.ca: VEVTVFJPT1RDQVM1 # <-- This should be the prefered one.
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Secret
|
kind: Secret
|
||||||
|
@ -39,6 +70,18 @@ data:
|
||||||
tls.crt: VEVTVENFUlQy
|
tls.crt: VEVTVENFUlQy
|
||||||
tls.key: VEVTVEtFWTI=
|
tls.key: VEVTVEtFWTI=
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: allcerts
|
||||||
|
namespace: foo
|
||||||
|
|
||||||
|
data:
|
||||||
|
ca.crt: VEVTVEFMTENFUlRT
|
||||||
|
tls.crt: VEVTVENFUlQz
|
||||||
|
tls.key: VEVTVEtFWTM=
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: ServersTransport
|
kind: ServersTransport
|
||||||
|
@ -51,11 +94,16 @@ spec:
|
||||||
insecureSkipVerify: true
|
insecureSkipVerify: true
|
||||||
maxIdleConnsPerHost: 42
|
maxIdleConnsPerHost: 42
|
||||||
rootCAsSecrets:
|
rootCAsSecrets:
|
||||||
|
- rootCas0
|
||||||
- rootCas1
|
- rootCas1
|
||||||
- rootCas2
|
- rootCas2
|
||||||
|
- rootCas3
|
||||||
|
- rootCas4
|
||||||
|
- allcerts
|
||||||
certificatesSecrets:
|
certificatesSecrets:
|
||||||
- mtls1
|
- mtls1
|
||||||
- mtls2
|
- mtls2
|
||||||
|
- allcerts
|
||||||
forwardingTimeouts:
|
forwardingTimeouts:
|
||||||
dialTimeout: 42
|
dialTimeout: 42
|
||||||
responseHeaderTimeout: 42s
|
responseHeaderTimeout: 42s
|
||||||
|
|
|
@ -15,7 +15,7 @@ metadata:
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
data:
|
data:
|
||||||
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
|
|
@ -508,20 +508,29 @@ func loadCASecret(namespace, secretName string, k8sClient Client) (string, error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to fetch secret '%s/%s': %w", namespace, secretName, err)
|
return "", fmt.Errorf("failed to fetch secret '%s/%s': %w", namespace, secretName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", fmt.Errorf("secret '%s/%s' not found", namespace, secretName)
|
return "", fmt.Errorf("secret '%s/%s' not found", namespace, secretName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if secret == nil {
|
if secret == nil {
|
||||||
return "", fmt.Errorf("data for secret '%s/%s' must not be nil", namespace, secretName)
|
return "", fmt.Errorf("data for secret '%s/%s' must not be nil", namespace, secretName)
|
||||||
}
|
}
|
||||||
if len(secret.Data) != 1 {
|
|
||||||
return "", fmt.Errorf("found %d elements for secret '%s/%s', must be single element exactly", len(secret.Data), namespace, secretName)
|
tlsCAData, err := getCABlocks(secret, namespace, secretName)
|
||||||
|
if err == nil {
|
||||||
|
return tlsCAData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range secret.Data {
|
// TODO: remove this behavior in the next major version (v3)
|
||||||
return string(v), nil
|
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 {
|
||||||
|
return string(v), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return "", nil
|
|
||||||
|
return "", fmt.Errorf("could not find CA block: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, string, error) {
|
func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, string, error) {
|
||||||
|
@ -529,15 +538,14 @@ func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("failed to fetch secret '%s/%s': %w", namespace, secretName, err)
|
return "", "", fmt.Errorf("failed to fetch secret '%s/%s': %w", namespace, secretName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
return "", "", fmt.Errorf("secret '%s/%s' does not exist", namespace, secretName)
|
return "", "", fmt.Errorf("secret '%s/%s' does not exist", namespace, secretName)
|
||||||
}
|
}
|
||||||
|
|
||||||
if secret == nil {
|
if secret == nil {
|
||||||
return "", "", fmt.Errorf("data for secret '%s/%s' must not be nil", namespace, secretName)
|
return "", "", fmt.Errorf("data for secret '%s/%s' must not be nil", namespace, secretName)
|
||||||
}
|
}
|
||||||
if len(secret.Data) != 2 {
|
|
||||||
return "", "", fmt.Errorf("found %d elements for secret '%s/%s', must be two elements exactly", len(secret.Data), namespace, secretName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return getCertificateBlocks(secret, namespace, secretName)
|
return getCertificateBlocks(secret, namespace, secretName)
|
||||||
}
|
}
|
||||||
|
@ -869,16 +877,16 @@ func getCertificateBlocks(secret *corev1.Secret, namespace, secretName string) (
|
||||||
|
|
||||||
func getCABlocks(secret *corev1.Secret, namespace, secretName string) (string, error) {
|
func getCABlocks(secret *corev1.Secret, namespace, secretName string) (string, error) {
|
||||||
tlsCrtData, tlsCrtExists := secret.Data["tls.ca"]
|
tlsCrtData, tlsCrtExists := secret.Data["tls.ca"]
|
||||||
if !tlsCrtExists {
|
if tlsCrtExists {
|
||||||
return "", fmt.Errorf("the tls.ca entry is missing from secret %s/%s", namespace, secretName)
|
return string(tlsCrtData), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cert := string(tlsCrtData)
|
tlsCrtData, tlsCrtExists = secret.Data["ca.crt"]
|
||||||
if cert == "" {
|
if tlsCrtExists {
|
||||||
return "", fmt.Errorf("the tls.ca entry in secret %s/%s is empty", namespace, secretName)
|
return string(tlsCrtData), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return cert, nil
|
return "", fmt.Errorf("secret %s/%s contains neither tls.ca nor ca.crt", namespace, secretName)
|
||||||
}
|
}
|
||||||
|
|
||||||
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{} {
|
||||||
|
|
|
@ -3444,10 +3444,11 @@ func TestLoadIngressRoutes(t *testing.T) {
|
||||||
"test": {
|
"test": {
|
||||||
ServerName: "test",
|
ServerName: "test",
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
RootCAs: []tls.FileOrContent{"TESTROOTCAS", "TESTROOTCAS2"},
|
RootCAs: []tls.FileOrContent{"TESTROOTCAS0", "TESTROOTCAS1", "TESTROOTCAS2", "TESTROOTCAS3", "TESTROOTCAS5", "TESTALLCERTS"},
|
||||||
Certificates: tls.Certificates{
|
Certificates: tls.Certificates{
|
||||||
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
|
{CertFile: "TESTCERT1", KeyFile: "TESTKEY1"},
|
||||||
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},
|
{CertFile: "TESTCERT2", KeyFile: "TESTKEY2"},
|
||||||
|
{CertFile: "TESTCERT3", KeyFile: "TESTKEY3"},
|
||||||
},
|
},
|
||||||
MaxIdleConnsPerHost: 42,
|
MaxIdleConnsPerHost: 42,
|
||||||
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
ForwardingTimeouts: &dynamic.ForwardingTimeouts{
|
||||||
|
|
Loading…
Reference in a new issue