cert-manager¶
TLS certificate automation via Let's Encrypt, using DNS-01 challenges through
AWS Route53. DNS-01 because these hostnames resolve to RFC1918 addresses that
Let's Encrypt cannot reach, and because it is the only way to get a wildcard:
two certificates, *.k8s.wlkr.ch and *.infra.k8s.wlkr.ch, cover every
hostname the cluster serves.
At a glance¶
| Namespace | cert-manager; the certificates it issues land in kube-system |
| Stage | 03-controllers for the controller; 06-certificates for the certificates Application with the ClusterIssuers and Certificates |
| Depends on | External Secrets for the Route53 credential, so transitively on OpenBao |
| If it is down | Nothing immediately. Certificates stop renewing, and the consequence surfaces up to sixty days later |
| Health check | kubectl get certificate -A → all READY=True |
| Files | payload/platform/cert-manager/, payload/platform/certificates/ |
Configuration¶
| Setting | Why |
|---|---|
Issuers and certificates in their own certificates Application |
Kept with cert-manager they would hold 03-controllers until OpenBao, two stages later, held the Route53 credentials — see GitOps |
Three sync waves inside certificates: ExternalSecret, then the ClusterIssuers, then the Certificates |
In one wave ArgoCD orders custom resources alphabetically — Certificate, ClusterIssuer, ExternalSecret, exactly backwards — and an issuer applied before its Secret stays Ready=False with InvalidSolver until something resyncs it |
Sync retry on both Applications |
Without one a failed apply ends the operation where it fell; one flake at the front of the chain, such as the ESO webhook being unreachable on a fresh CNI, leaves every issuer and certificate behind it unmade |
ServiceMonitor rendered unconditionally |
The chart cannot sync until the Prometheus operator CRDs exist, which is why they are a separate Application in 01-crds — see Monitoring |
letsencrypt-staging and letsencrypt-prod |
Use staging first: production allows five duplicate certificates per week, a misconfigured solver retries until that is gone, and there is no appeals process |
The IAM user needs at minimum:
{
"Effect": "Allow",
"Action": ["route53:GetChange", "route53:ChangeResourceRecordSets", "route53:ListHostedZonesByName"],
"Resource": "*"
}
Usage¶
Store the credentials once OpenBao and ESO are up; make bao-secrets prompts
for them, or by hand:
bao kv put kv/cert-manager/route53 \
access-key-id="YOUR_AWS_ACCESS_KEY_ID" \
secret-access-key="YOUR_AWS_SECRET_ACCESS_KEY"
ESO creates the route53-credentials Secret within its refreshInterval, or
immediately on request.
Health check¶
Work down the chain of custody; the answer is nearly always further back than
the Certificate:
kubectl describe certificate -n kube-system <name>
kubectl get certificaterequest,order,challenge -A
kubectl -n cert-manager logs deploy/cert-manager --tail=100
A Challenge stuck in pending is a DNS problem: either the credentials
cannot write to the zone, or the TXT record is there and the resolver has not
caught up. dig +short TXT _acme-challenge.<host> settles which.
Pitfalls¶
A sealed OpenBao means no renewals
Until OpenBao is unsealed and the secret stored, cert-manager cannot issue or renew, and the failure surfaces as an expired certificate roughly two months later — see OpenBao.