Photo by Philipp Katzenberger on Unsplash
Configure Cert Manager and Let's Encrypt in a Kubernetes Cluster with Traefik and Cloudflare
Table of contents
Introduction
If your DNS is managed by Cloudflare and you want to use DNS01 challenge with Cloudflare and Cert Manager in a Kubernetes Cluster, you can follow this guide.
Configuring Cloudflare:
Assuming you have created a Cloudflare account and added your domain, go to your Cloudflare Dashboard and point your domain to your Load Balancer IP. If you have used the Kubernetes on Hetzner guide, point your Hetzner Load Balancer IP address that you have just created. Under DNS add two A Record. One is for the root and one is for subdomains:
Type Name Content Proxy status TTL
A @ Your-LB-IP DNS Only Auto
A * Your-LB-IP DNS Only Auto
Do not forget to disable proxy:
Deploying Cert Manager:
First add the helm repo:
helm repo add jetstack https://charts.jetstack.io
helm repo update
create a values.yaml and add Cloudflare's dns as well:
installCRDs: true
podDnsConfig:
nameservers:
- "1.1.1.1"
- "9.9.9.9"
then install Cert manager:
helm install cert-manager jetstack/cert-manager -f values.yaml --namespace cert-manager --create-namespace --set installCRDs=true
Generate an Cloudflare API token like this:
Add your Cloudflare Api Token as a secret in Kubernetes. If you are using GitOps, consider encrypting your secrets or use some kind of secret managing solution:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-secret
namespace: cert-manager
type: Opaque
stringData:
cloudflare-token: yourtoken
Create your cluster issuers. One for staging and one for production.
Staging:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: your@email.com
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- dns01:
cloudflare:
email: your-cloudflare@email.com
apiTokenSecretRef:
name: cloudflare-secret
key: cloudflare-token
selector:
dnsZones:
- "your-domain.com"
Production:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-production
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your@email.com
privateKeySecretRef:
name: letsencrypt-production
solvers:
- dns01:
cloudflare:
email: your-cloudflare@email.com
apiTokenSecretRef:
name: cloudflare-secret
key: cloudflare-token
selector:
dnsZones:
- "your-domain.com"
You can now test it with an application. Below you will find an example app exposed with Traefik:
kubectl create deployment nginx --image=nginx
kubectl expose deploy nginx --type=ClusterIP --port=80
create a Certificate for your application:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: nginx.yourdomain.com
namespace: default
spec:
dnsNames:
- nginx.yourdomain.com
secretName: nginx-tls
issuerRef:
name: letsencrypt-staging
kind: ClusterIssuer
then create an Ingressroute:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nginx
namespace: default
annotations:
kubernetes.io/ingress.class: traefik-external
spec:
entryPoints:
- websecure
- web
routes:
- match: Host(`nginx.yourdomain.com`)
kind: Rule
services:
- name: nginx
port: 80
- match: Host(`www.nginx.yourdomain.com`)
kind: Rule
services:
- name: nginx
port: 80
tls:
secretName: nginx-tls
You can now access your application:
You will see a warning about an unsecure connection. This is normal with staging. When you switch to the production, the error will disappear: