DNS01 challenge¶
The DNS01 challenge proves to an ACME server (for example, Let's Encrypt) that you control a domain. You prove it with a DNS TXT record. cert-manager creates and removes this record through the API of your DNS provider.
DNS01 is the only challenge type that issues wildcard certificates. It also works for domains that are not reachable from the internet. It does not work for IP addresses.
Resource chain¶
cert-manager creates one resource per step. To debug an issue, follow this chain:
Certificate -> CertificateRequest -> Order -> Challenge (one per DNS name)
kubectl get certificate,certificaterequest,order,challenge -n <namespace>
kubectl describe challenge <name> -n <namespace>
How the process works¶
- cert-manager creates an Order on the ACME server for the DNS names of the Certificate.
- The ACME server returns one authorization per DNS name. Each authorization contains a token.
- cert-manager creates a Challenge resource for each DNS name.
- cert-manager selects a solver from the
solverslist of the Issuer. - The solver creates a TXT record at
_acme-challenge.<domain>. The value is a key computed from the token. - cert-manager runs a self check until it sees the record (see below).
- cert-manager tells the ACME server that the challenge is ready.
- The ACME server reads the TXT record from public DNS. If the value is correct, the ACME server accepts the authorization.
- cert-manager finalizes the Order and stores the certificate in the Secret.
- cert-manager deletes the TXT record.
While cert-manager waits, the Challenge shows State: pending and Reason: Waiting for dns-01 challenge propagation.
Self check¶
The self check makes sure that the TXT record exists before cert-manager asks the ACME server to read it. The purpose is to not send failed challenges to the ACME server.
By default, the self check does two steps:
- It uses the recursive nameservers from
/etc/resolv.confof the controller pod to find the authoritative nameservers (SOA lookup). - It queries the authoritative nameservers directly for the TXT record.
A recursive nameserver resolves names for clients and caches the answers. An authoritative nameserver holds the real records of a zone.
Controller flags that change the process¶
| Flag | Default | Effect |
|---|---|---|
--dns01-recursive-nameservers |
empty | Replaces /etc/resolv.conf with this list of nameservers. Format is host:port, comma separated. DNS-over-HTTPS URLs (https://...) are also accepted. |
--dns01-recursive-nameservers-only |
false |
The self check only queries the configured recursive nameservers. It never queries the authoritative nameservers. The check can take longer because of the cache of the recursive nameservers. |
--dns01-check-retry-period |
10s |
Time between two self checks. |
--issuer-ambient-credentials |
false |
Lets an Issuer use ambient credentials (for example IRSA or EKS Pod Identity). A ClusterIssuer uses them by default. |
With Helm, you set these flags in extraArgs:
extraArgs:
- --dns01-recursive-nameservers-only
- --dns01-recursive-nameservers=8.8.8.8:53,1.1.1.1:53
Use these flags in the following cases:
- Split-horizon DNS: the cluster DNS returns a private zone and the public zone has the TXT record. The self check never passes.
- The cluster DNS filters SOA queries (for example, dnsmasq with
--filterwin2k). cert-manager cannot find the zone. - A firewall blocks port 53 from the cluster to the authoritative nameservers. Use
--dns01-recursive-nameservers-only.
Solver selection¶
An Issuer can have more than one solver. cert-manager selects one solver per DNS name with the selector field:
| Selector | Match rule |
|---|---|
dnsNames |
Exact DNS name. It has priority over dnsZones. |
dnsZones |
The zone and all its subdomains. The longest zone wins (sys.example.com wins over example.com for www.sys.example.com). |
matchLabels |
All labels must be on the Certificate. |
| no selector | Matches all names. A more specific selector wins over it. |
If two solvers match at the same level, the solver with more matchLabels wins. If they are still equal, the first solver in the list wins.
Delegation with CNAME¶
You can delegate the challenge to a different zone. Then cert-manager only needs write access to that zone. Create this record one time in the main zone:
_acme-challenge.example.com CNAME _acme-challenge.less-privileged.example.org
The cnameStrategy field of the dns01 solver controls this:
None(default): cert-manager writes the TXT record at_acme-challenge.<domain>. It does not follow a CNAME.Follow: cert-manager follows the CNAME records and writes the TXT record in the target zone.
solvers:
- selector:
dnsZones:
- example.com
dns01:
cnameStrategy: Follow
route53:
region: eu-central-1
hostedZoneID: <zone-id-of-less-privileged.example.org>
If you use
Follow, cert-manager follows any CNAME at_acme-challenge.<domain>. This includes a CNAME that a wildcard record creates. Make sure that no wildcard CNAME exists in the zone, or the TXT record goes to the wrong zone.
Route53 solver¶
| Field | Effect |
|---|---|
region |
AWS region of the API calls. |
hostedZoneID |
Hosted zone to change. If empty, cert-manager finds the zone with ListHostedZonesByName. |
accessKeyID / accessKeyIDSecretRef and secretAccessKeySecretRef |
Static credentials. If empty, cert-manager uses ambient credentials. |
role |
IAM role that cert-manager assumes before it changes the zone. |
auth.kubernetes.serviceAccountRef |
Service account token used to assume role (web identity). |
The IAM policy needs route53:GetChange, route53:ChangeResourceRecordSets, route53:ListResourceRecordSets and route53:ListHostedZonesByName. You can limit ChangeResourceRecordSets to TXT records with the route53:ChangeResourceRecordSetsRecordTypes condition.