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_key
usingtsig-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.com
manually or with this creator. -
Fetch the "secret" from the
tsig_bind_key
, and create/path/to/acme_credentials
as: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
A
records for your nameserver domain[1] to point to the bind9 server.ns.example.com. 3600 IN A 100.53.42.10
-
Set up an
NS
record for_acme-challenge.$domain
pointing 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
CNAME
record for_acme-challenge.$subdomain
pointing 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
TXT
record containing this token[3] at_acme-challenge.$domain
[4]; this may be done by a separate program. We useRFC2136
to edit thebind9
records. In NixOS's case,security.acme
uses LEGO to perform this request. - The CA then queries the
TXT
record 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.52
Both 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
NS
records for each_acme-challenge.$subDomain
which 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. ↩︎