Wildcard TLS via cert-manager + DNS-01/Cloudflare
Date: 2026-03-23
Status: Accepted
Context: Providing HTTPS for all team subdomains (e.g., team.prod.fontysvenlo.dev) automatically
Decision
Use cert-manager with Let's Encrypt and the DNS-01 challenge via Cloudflare to provision wildcard TLS certificates. A single wildcard cert per domain is replicated to all namespaces via ExternalSecrets.
Architecture
graph TB
cm["cert-manager"] -->|"ACME DNS-01"| le["Let's Encrypt"]
le -->|"via Cloudflare API"| cm
cm --> certs["Wildcard certs<br/>*.prod.fontysvenlo.dev<br/>*.spohf.fontysvenlo.dev"]
certs -->|"ExternalSecrets<br/>replication"| ns["All workload namespaces<br/>prj2-*, spohf-*, harbor,<br/>argocd, monitoring, ..."]
ns --> traefik["Traefik<br/>terminates TLS"]
Rationale
Why wildcard certificates?
- Dynamic subdomains — new teams get subdomains (
{team}.prod.fontysvenlo.dev) without any certificate provisioning step. The wildcard covers them all. - Simplicity — one cert to manage per domain instead of one per team/service.
Why DNS-01 challenge?
- Required for wildcards — HTTP-01 challenges cannot issue wildcard certificates (ACME specification limitation).
- fontysvenlo.dev already on Cloudflare — DNS-01 via the Cloudflare API was the natural path.
Why replicate via ExternalSecrets?
- cert-manager provisions the cert in its own namespace. Traefik and workloads in other namespaces need access to it. ExternalSecrets handles this cross-namespace distribution declaratively.
Alternatives Considered
HTTP-01 per-service certificates
- ✅ No DNS provider dependency
- ❌ Cannot issue wildcard certs — would need a cert per team subdomain
- ❌ Provisioning delay when adding new teams
- Rejected: Doesn't support our dynamic subdomain model
Per-namespace cert-manager issuers
- ✅ Each namespace manages its own cert
- ❌ More CRDs, more issuers, more potential for misconfiguration
- ❌ Each namespace would need Cloudflare API credentials
- Rejected: More moving parts for no benefit
Consequences
Positive
- New teams get HTTPS automatically — zero TLS configuration per team
- Single renewal point — cert-manager handles rotation automatically
- Works regardless of ingress reachability (DNS-01 doesn't need inbound HTTP)
Negative
- Dependency on Cloudflare — Cloudflare API token must be maintained as a manual secret (not in git)
- Single wildcard cert is a shared failure point — if renewal fails, all services lose TLS
- Cross-namespace replication adds a layer of indirection