LetsEncrypt Wildcard Certificates with Fastmail DNS
tl;dr (make it work) #
Bind9 DNS Configuration #
Note: This uses NixOS but the configuration is very similar for standard bind9; see the template for the config file.
-
Following 51.5. Configuring ACME for DNS Validation. Generate the
tsig_bind_keyusingtsig-keygen rfc2136_key > tsig_bind_key.services.bind = { enable = true; extraConfig = '' include "/path/to/tsig_bind_key"; ''; extraOptions = '' querylog yes; ''; zones."_acme-challenge.example.com" = { file = "/var/db/bind/_acme-challenge.example.com"; master = true; extraConfig = '' update-policy { grant rfc2136_key zonesub any; }; ''; }; }; networking.firewall.allowedTCPPorts = [ 53 ]; networking.firewall.allowedUDPPorts = [ 53 ]; -
Create the bind zone file
/var/db/bind/_acme-challenge.example.commanually or with this creator. -
Fetch the "secret" from the
tsig_bind_key, and create/path/to/acme_credentialsas:RFC2136_NAMESERVER='ns.example.com:53' RFC2136_TSIG_ALGORITHM='hmac-sha256.' RFC2136_TSIG_KEY='rfc2136_key' RFC2136_TSIG_SECRET='<secret>'' -
Setup your ACME client for RFC2136 validation; here we use NixOS's
security.acme:security.acme.certs."example.com" = { extraDomainNames = [ "*.example.com" ]; dnsProvider = "rfc2136"; credentialsFile = "/path/to/acme_credentials"; dnsPropagationCheck = true; group = "nginx"; }; -
Also setup the ACME client for any subdomains you wish to use.
Fastmail DNS Configuration #
-
Set up the appropriate
Arecords for your nameserver domain[1] to point to the bind9 server.ns.example.com. 3600 IN A 100.53.42.10 -
Set up an
NSrecord for_acme-challenge.$domainpointing to your nameserver domain. This will handle DNS-01 challenges for your root domain._acme-challenge.example.com. 3600 IN NS ns.example.com. -
Set up a
CNAMErecord for_acme-challenge.$subdomainpointing to_acme-challenge.$domain. This will handle DNS-01 challenges for that specific subdomain.[2]_acme-challenge.uwu.example.com. 3600 IN CNAME _acme-challenge.example.com. -
Repeat the previous step for each subdomain you also want a certificate for.
-
Verify you have configured your settings correctly:
$ dig NS _acme-challenge.uwu.example.com +noall +answer _acme-challenge.uwu.example.com. 3600 IN CNAME _acme-challenge.example.com. _acme-challenge.example.com. 3600 IN NS ns.example.com.
How it works #
This is not a deep dive, but a high-level overview. Some specifics may have been left out due to choice, or lack of knowledge.
DNS-01 Challenge #
- An ACME client, e.g. certbot or NixOS
security.acme.certs, etc., requests a token from a CA for your domain(s). - The ACME client creates a
TXTrecord containing this token[3] at_acme-challenge.$domain[4]; this may be done by a separate program. We useRFC2136to edit thebind9records. In NixOS's case,security.acmeuses LEGO to perform this request. - The CA then queries the
TXTrecord and verifies the token within. If successful, it issues certificates to the ACME client.
Querying TXT records #
The CNAME/NS records created are not a "feature" of LetsEncrypt, they are how standard DNS queries work.
To quote RFC1034 §3.6.2. Aliases and Canonical Names:
CNAME RRs [resource records] cause special action in DNS software. When a name server fails to find a desired RR in the resource set associated with the domain name, it checks to see if the resource set consists of a CNAME record with a matching class. If so, the name server includes the CNAME record in the response and restarts the query at the domain name specified in the data field of the CNAME record. The one exception to this rule is that queries which match the CNAME type are not restarted.
For example, suppose a name server was processing a query with for USC-ISIC.ARPA, asking for type A information, and had the following resource records:
USC-ISIC.ARPA IN CNAME C.ISI.EDU C.ISI.EDU IN A 10.0.0.52Both of these RRs would be returned in the response to the type A query, while a type CNAME or * query should return just the CNAME.
That is, CNAME are handled transparently by the DNS layer.
NS (nameserver) records define the nameserver for a particular subdomain. Thus, an NS record for _acme-challenge.example.com pointing to ns.example.com tells your DNS resolver to search for records under _acme-challenge.example.com using that name server, not the nameserver set by the registrar (for us, this is the Fastmail ns1.messagingengine.com domain). This makes it possible to modify the TXT records for the _acme-challenge.example.com subdomain using the bind9 server, without having all the DNS requests go through the nameserver.
Further Reading #
- RFC8555 §8.4. DNS Challenge
- RFC1034
- Dan Langille: ACME domain alias mode
- LetsEncrypt: DNS-01 Challenge
- IPng Networks: Let's Encrypt DNS-01
Here we assume that your nameserver domain is a subdomain of your root domain, and so would therefore be configured in Fastmail. ↩︎
You could alternatively set up
NSrecords for each_acme-challenge.$subDomainwhich point to the nameserver domain (follow step 2). ↩︎It's not actually the same token, there's some extra steps performed by the ACME client. ↩︎
This is the case for wildcard domains, e.g.
*.$domain; the ACME client simply sets a "wildcard" property to true when requesting the certificate. ↩︎