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!