traefik-helm-chart
traefik-helm-chart copied to clipboard
Provide a "default" Certificate for TLS
If a user provides us his certificate data, we could create a TLSStore and attach the cert as a default certificate.
Partially fixes #185
I was searching a lot how I could swap Traefik auto generated certificates out and provide the default ones. Since there is a lot of confusion around this, I'll make an informational post here.
Specifically I use Kubernetes and Ingresses with Cloudflare as DNS and Firewall provider. Cloudflare provides their own Edge certificates (for connection between CF and host server) that I need to upload and serve for all the hosts.
I created secret of type tls. This is necessary.
kubectl create secret tls tls-secret --cert tls.crt --key tls.key
A) And tried using TLSStore (src: https://github.com/traefik/traefik/issues/6057#issuecomment-615903064)
apiVersion: traefik.containo.us/v1alpha1
kind: TLSStore
metadata:
name: cloudflare-tls
namespace: traefik
spec:
defaultCertificate:
secretName: tls-secret
but seems to be ignored by Kubernetes Ingress resources. This may work if you are using IngressRoute (Kubernetes CRD provided by Traefik)
B) Then I went the dynamic config way: Created ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: traefik-config
labels:
name: traefik-config
namespace: traefik
data:
dyn.yaml: |
# https://doc.traefik.io/traefik/https/tls/
tls:
stores:
default:
defaultCertificate:
certFile: '/certs/tls.crt'
keyFile: '/certs/tls.key'
and used these values when deploying Traefik chart
additionalArguments:
- '--providers.file.filename=/config/dyn.yaml'
volumes:
- name: tls-secret
mountPath: '/certs'
type: secret
- name: traefik-config
mountPath: '/config'
type: configMap
and it works!
It can be challenging to understand Traefik, especially when Kubernetes are involved, hope this will shed some light.
Relevant: https://github.com/traefik/traefik/issues/5468#issuecomment-590067174
I would like to see the ConfigMap and therefore my custom configuration to be managed within this HelmChart as well.
So in values.yaml
additionalConfig: |
# https://doc.traefik.io/traefik/https/tls/
tls:
stores:
default:
defaultCertificate:
certFile: '/certs/tls.crt'
keyFile: '/certs/tls.key'
and a ConfigMap like
{{ if .Values.additionalConfig }}
apiVersion: v1
kind: ConfigMap
metadata:
name: traefik-config
labels:
name: traefik-config
data:
dyn.yaml: |
{{ .Values.additionalConfig }}
{{ end }}
Maybe it could be possible to do that with the defaultCert as well, at least for the standard Ingress type.
What do you think @SantoDE ?
Please add the additional configuration section to the helm chart !
i have been spinning for days until i found this issue ..
+1 wishing for an easier / more flexible way to configure this, as it feels like a very common use case when using wildcard certificates with LetsEncrypt: we want to be able to reuse this same certificate as the default at the cluster-level without needing to sync, copy, or reissue the TLS secret to each namespace where it is needed.
@flexchar mentions a workaround, but I believe that this assumes that you are in the same namespace in which the secret was created by cert-manager, since we can't mount a secret from a different namespace. This secret would typically be in the same namespace as the Ingress rules that it secures, but not necessarily where Traefik itself is running.
For example:
- CertManager Helm chart running in namespace
cert-manager - Traefik Helm chart running in namespace
traefik - Our application running in namespace
example, where our Ingress rules are created
What happens:
- CertManager sees a new Ingress rule requiring TLS in
example, so it creates a new secret in theexamplenamespace containing the neededtls.key/tls.crtfiles - We cannot mount in the
tls.key/tls.crtfiles that are needed by Traefik to set the default certificates for the cluster, because they are not in thetraefiknamespace - To properly support this case, we would need to make sure that the correct TLS secret exists in both namespaces, which seems odd
Syncing/copying to every needed namespace adds complexity to automating the renewal of these certs, and it can be difficult to debug or even know if the sync/copy ever fails. Reissuing the same cert for every namespace can lead to being rate-limited by LetsEncrypt if your application scales out too far or too quickly.
For our use case, we may need to switch back to NGINX, where this is a simple command-line flag that we can add to extraArgs in the Helm chart: --set controller.extraArgs.default-ssl-certificate=<namespace>/<secretname>. In this case, they do not assume that the secret will actually live in the same namespace, but are somehow able to read it anyways.
I'm going to quickly clarify that I have manually uploaded a certificate files (we are using Cloudflare ones) as a secret to Traefik namespace. Works great.
Each of the actual applications incl. the Ingress manifest are in their own respective namespace zone - that works fine.
I was able to achieve this by just creating a TLSStore
apiVersion: traefik.containo.us/v1alpha1
kind: TLSStore
metadata:
name: default
spec:
defaultCertificate:
secretName: custom-default-cert
Note that i did need to enable the feature for the IngressRoute custom resources, even though i am not using them. The default cert defined in the TLSStore works for Ingress objects. I also named the TLSStore "default". not sure if that is required, but the docs do mention that the only valid store is "default"
@gmorse81 's solution works. It would be great if we could configure it beforehand at the helm chart installation.
I can also confirm that the @gmorse81's solution works, but with the caveat that the TLSStore needs to exist when Traefik starts, otherwise traefik generates and uses a self signed certificate until someone restarts the pod.
This seems strange, because the [tls.stores.default.defaultCertificate] must be declared inside a "dynamic configuration" instead of a "static configuration", so I would normally assume that traefik reloads this certificate "on the fly" when the config changes.
So, to use a default tls certificate using the custom resource TLSStore you currently have to deploy traefik in this order:
- Deploy the custom resource definitions (CRDs)
- Deploy your
TLSStorecustom resource as shown by @gmorse81 - Deploy the actual pods using the helm chart.
Edit: Actually, this is false. The default TLSStore can be deployed at runtime. It just took a minute or so for traefik to pick up my changes.
@bodom0015 I have exactly the same issue. I've got
- cert-manager running in namespace
cert-managerand - Traefik (deployed using k3s that internally uses this helm chart) in namespace
kube-system.
Therefore using the solution proposed by @gmorse81 with a TLSStore does not work as this would require referencing a secret across namespaces. I also tried using a namespace prefix (cert-manager/name-of-the-secret) but this does not work, too. So I guess I need to revert to an nginx ingress.
@pfisterer, the answer provided by @gmorse81 does work, but you need to use a ClusterIssuer for cert-manager instead of an Issuer.
My setup:
- k3s cluster with 3 nodes (
--disable=traefik) - cert-manager helm chart deployed to the namespace
cert-manager - Traefik helm chart deployed to the namespace
traefik
First I create my ClusterIssuer which doesn't belong to a namespace.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- dns01:
cloudflare:
email: [email protected]
apiTokenSecretRef:
name: super-secret-secret-name
key: api-token
Then I create a Certificate and assign it as default in the TLSStore. Note, the namespace for both the Certificate and TLSStore should match where you deployed Traefik. Generally kube-system or traefik, either will work.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: wildcard-example-com
namespace: `YOUR_TRAEFIK_NAMESPACE`
spec:
secretName: wildcard-example-com-tls
dnsNames:
- "example.com"
- "*.example.com"
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
---
apiVersion: traefik.containo.us/v1alpha1
kind: TLSStore
metadata:
name: default
namespace: `YOUR_TRAEFIK_NAMESPACE`
spec:
defaultCertificate:
secretName: wildcard-example-com-tls
Once the certificate creation process has completed, Traefik will reload using the new default certificate.
@kraihn Great, thank you!
@kraihn, I'm trying to do the same thing, and I can't seem to get it to work.
I have:
- A TLSStore in the kube-system namespace with a default secret
- That secret in the kube-system namespace
- the traefik entrypoint configured as:
websecure:
address: :8443
http:
tls: {}
- I have tried configuring the IngressRoute with
- no tls: entry
- tls: {}
- and
tls:
store:
name: default
All with no success. Traefik debug level logging just keeps claiming there is "No certificate found for domain:..."
Could you share your entrypoint and ingress tls configuration?
Hello,
Now that PR #601 has been merged, as @gmorse81 said, you can use the TLSStore to achieve this.
For anyone reading this and trying to get @kraihn's solution to work, I had to add the certificates array to the TLSStore for it to work:
apiVersion: traefik.containo.us/v1alpha1
kind: TLSStore
metadata:
name: default
namespace: `YOUR_TRAEFIK_NAMESPACE`
spec:
certificates:
- secretName: wildcard-example-com-tls
defaultCertificate:
secretName: wildcard-example-com-tls
@jpjonte I checked and I was able to make it work without this with current version of Traefik and this Helm Chart.
I provided a complete example in PR #754, does this work for you ?