Skip to main content
julia's space

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.

  1. Following 51.5. Configuring ACME for DNS Validation. Generate the tsig_bind_key using tsig-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 ];
  2. Create the bind zone file /var/db/bind/_acme-challenge.example.com manually or with this creator.

  3. 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>''
  4. 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";
    };
  5. Also setup the ACME client for any subdomains you wish to use.

Fastmail DNS Configuration #

  1. 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
  2. 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.
  3. 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.
  4. Repeat the previous step for each subdomain you also want a certificate for.

  5. 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 #

  1. An ACME client, e.g. certbot or NixOS security.acme.certs, etc., requests a token from a CA for your domain(s).
  2. 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 use RFC2136 to edit the bind9 records. In NixOS's case, security.acme uses LEGO to perform this request.
  3. 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 #

  1. Here we assume that your nameserver domain is a subdomain of your root domain, and so would therefore be configured in Fastmail. ↩︎

  2. You could alternatively set up NS records for each _acme-challenge.$subDomain which point to the nameserver domain (follow step 2). ↩︎

  3. It's not actually the same token, there's some extra steps performed by the ACME client. ↩︎

  4. This is the case for wildcard domains, e.g. *.$domain; the ACME client simply sets a "wildcard" property to true when requesting the certificate. ↩︎