.@ Tony Finch – blog


Last week, the EFF wrote about how to safely allow web servers to update ACME DNS challenges. Whereas non-wildcard Let's Encrypt certificates can be authorized by the web server itself, wildcard certs require the ACME client to put the challenge token in the DNS.

The EFF article outlined a few generic DNS workarounds (which I won't describe here), and concluded by suggesting delegating the _acme-challenge subdomain to a special ACME-DNS server.

But, if your domain is hosted with BIND, it's much easier.

First, you need to generate a TSIG key (a shared secret) which will be used by your ACME client to update the DNS. The tsig-keygen command takes the name of the key as its argument; give the key the same name as the domain it will be able to update. I write TSIG keys to files named tsig.<keyname> so I know what they are.

    $ tsig-keygen _acme-challenge.dotat.at \
        >tsig._acme-challenge.dotat.at

This file needs to be copied to the ACME client - I won't go into the details of how to get that part working.

The key needs to be included in the primary BIND server config:

    include "tsig._acme-challenge.dotat.at";

You also need to modify your zone's dynamic update configuration. My zones typically have:

    update-policy local;

The new configuration needs both the expanded form of local plus the _acme-challenge permissions, like this:

    update-policy {
        grant local-ddns zonesub any;
        grant _acme-challenge.dotat.at self _acme-challenge.dotat.at TXT;
    };

You can test that the key has restricted permissions using nsupdate. The following transcript shows that this ACME TSIG key can only add and delete TXT records at the _acme-challenge subdomain - it isn't able to update TXT records at other names, and isn't able to update non-TXT records at the _acme-challenge subdomain.

    nsupdate -k tsig._acme-challenge.dotat.at
    > add thing.dotat.at 3600 txt thing
    > send
    update failed: REFUSED
    > add _acme-challenge.dotat.at 3600 a 127.0.0.1
    > send
    update failed: REFUSED
    > add _acme-challenge.dotat.at 3600 txt thing
    > send
    > del _acme-challenge.dotat.at 3600 txt thing
    > send

That's it!