Challenge validators¶
A validator proves that the client controls one identifier. The server offers a challenge type
only when Config.Validators holds a validator for it, so the map decides what clients see in
their authorizations. The challenge package implements the three network challenges of RFC 8555
and RFC 8737 and the Authority Token challenge of RFC 9447, described in Authority Token challenges.
Validators: map[acmeserver.ChallengeType]acmeserver.Validator{
acmeserver.ChallengeHTTP01: http01,
acmeserver.ChallengeDNS01: dns01,
acmeserver.ChallengeTLSALPN01: tlsalpn01,
},
| Identifier | Challenge types offered |
|---|---|
| DNS name | Every configured network type |
| Wildcard DNS name | dns-01 only |
| IP address | http-01 and tls-alpn-01, never dns-01 |
The resolver¶
The network validators never use the system resolver. You configure the recursive DNS endpoints they may ask, which keeps validation traffic predictable and lets the deployment decide which view of DNS counts.
resolver, err := challenge.NewResolver(challenge.ResolverOptions{
Servers: []netip.AddrPort{netip.MustParseAddrPort("192.0.2.53:53")},
})
Replace the documentation address with your recursive resolver. Up to three servers are accepted. The resolver speaks TCP, accepts a response only when its ID and question match the query, follows CNAMEs only from the answer owner and bounds the lookup time, the CNAME chain, the message size and the record count. The defaults are ten seconds, eight hops, 16 KiB and 64 records.
A custom IPResolver or TXTResolver may replace it, for example a resolver that consults an
internal zone during tests. It must honor context cancellation and bound its own work, because the
validator's timeout is the only thing standing between a slow resolver and a stuck worker slot.
Egress policy¶
HTTP-01 and TLS-ALPN-01 connect to whatever address the identifier resolves to. NetworkOptions
decides which addresses are acceptable:
network := challenge.NetworkOptions{
Resolver: resolver,
AllowedNetworks: []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")},
}
By default only ordinary public unicast addresses are allowed. Loopback, private, link-local,
carrier-grade NAT, documentation, multicast and the other IANA special-purpose ranges are denied.
AllowedNetworks adds explicit exceptions for a CA that issues to private networks. Every resolved
address must pass, IPv4-mapped IPv6 addresses are unmapped before the check and a name that
resolves to more than MaxAddresses addresses is refused. The policy is applied again on every
redirect. challenge.PublicAddress exposes the default rule.
Access to the resolver itself is separate from this policy, so a private recursive resolver works without an exception.
HTTP-01¶
http01, err := challenge.NewHTTP01(challenge.HTTPOptions{Network: network})
The validator fetches http://<identifier>/.well-known/acme-challenge/<token> on port 80, follows
at most ten redirects to http or https URLs on ports 80 and 443, rechecks the egress policy on
every hop, reads at most 4096 bytes and compares the trimmed body with the key authorization. It
ignores proxy environment variables. An HTTPS redirect target is not checked against the web PKI,
because the key authorization is the proof, not the website certificate.
MaxRedirects and MaxResponseBytes change the limits. TestPort replaces port 80 for local
tests only, which is how the Getting started program reaches a client on
port 5002.
DNS-01¶
dns01, err := challenge.NewDNS01(challenge.DNSOptions{Resolver: resolver})
The validator looks up TXT records at _acme-challenge.<identifier> and accepts the response when
any record equals the base64url SHA-256 digest of the key authorization. Accepting any matching
record is what lets a client prove a wildcard and its base name at the same owner name in one
order. A response with more than 64 records is a DNS failure.
DNS-01 is the only challenge offered for wildcard names and is never offered for IP identifiers.
TLS-ALPN-01¶
tlsalpn01, err := challenge.NewTLSALPN01(challenge.TLSALPNOptions{Network: network})
The validator opens a TLS connection to port 443 and offers only the acme-tls/1 protocol. The
SNI is the identifier or the reverse-mapped name for an IP identifier as RFC 8738 requires. The
handshake is limited to 256 KiB. It then checks that acme-tls/1 was negotiated, that the
certificate carries exactly one SAN equal to the identifier and that the critical acmeIdentifier
extension holds the SHA-256 digest of the key authorization. Ordinary certificate trust and
validity are not applied. TestPort replaces port 443 for tests.
Timeouts, retries and outcomes¶
Each validator bounds one attempt to NetworkOptions.Timeout or DNSOptions.Timeout, ten seconds
by default. The worker adds its own WorkerConfig.TaskTimeout. The outcome decides what
happens next:
| The validator returns | Meaning | Server behavior |
|---|---|---|
nil |
The proof matched | Challenge and authorization become valid |
*acmeserver.Problem |
The proof was wrong or the target refused | Final. Challenge and authorization become invalid, the problem is shown to the client |
| Any other error | A transport failure | Retried with exponential backoff until WorkerConfig.MaxAttempts or the authorization expiry |
The bundled validators return incorrectResponse for a wrong proof, dns for a resolver failure
that is unlikely to change and a plain error for connection failures. They never include fetched
content in the problem detail.
Writing a validator¶
type Validator interface {
Validate(ctx context.Context, req ValidationRequest) error
}
ValidationRequest carries the challenge, the identifier with its wildcard flag, the key
authorization, the account key thumbprint captured when the client responded and, for
tkauth-01, the Authority Token. A custom validator should:
- check that
req.Challenge.Typeis the type it handles and that the identifier type is one it can prove - return
*acmeserver.Problemfor a definitive failure and any other error for one worth retrying - finish within the context deadline and never follow untrusted redirects or resolve names without a policy
- keep fetched content out of error details
A validator that also authorizes something beyond control of the identifier, such as a CA
certificate or a validity bound, implements GrantingValidator. The tkauth-01 validator is the
example.
Checklist¶
ResolverOptions.Serversnames your recursive resolvers.AllowedNetworkslists exactly the private ranges the CA issues into or nothing at all.TestPortis unset in production.- At least one validator is configured for every identifier type you accept, including
dns-01for wildcards.