Setting up Certbot DNS Verification with Hurricane Electric DNS
TLS is an essential part of the standards that form the Internet, preventing eavesdropping, impersonation, and interference by all manner of parties with access to network traffic. In addition to these practical benefits, it is incresingly becoming a requirement of web and email servers in the modern era.
Therefore, as a system administrator, it is critical that I maintain
up-to-date TLS certificates;
for this, I use Let's Encrypt as its service is free,
easy to automate, secure (as certificates are issued for
90 days, limiting damage in the event of a compromise), and flexible.
I have always used certbot
to manage these certificates,
and overall it is a useful tool.
To verify one's control of the domains that one is attempting to get
a certificate for, the ACME protocol used by certbot
and Let's Encrypt
prescribes several methods—called "challenges"—for said verification.
Initially, I used the HTTP challenge, where a certificate authority
directs the owner of a domain to put a text file with specific contents
at a special location in their HTTP server's directory tree.
While it is possible, I never configured Apache HTTPd to do this automatically;
instead, I shut down my main HTTP server and let certbot
take control of
port 80 to perform the verification.
Recently, though, I switched to using the DNS challenge,
where the owner of a domain creates a TXT record under a particular subdomain
(_acme-challenge.
) to verify their control of said domain.
This has several advantages: one can get wildcard certificates valid for all
subdomains of a particular domain name and
one can get certificates for domains that don't have web servers attached to
them, among other things.
However, my DNS provider (Hurricane Electric) isn't supported by certbot
by default.
Hurricane Electric does let you dynamically configure TXT records, though.
To do so, one enables dynamic DNS for the record in the edit dialog
and then clicks on the circular arrow icon to the right of the record to set
a key.
Then, to update a dynamic TXT record,
one must send an HTTP POST request to https://dyn.dns.he.net/nic/update
with the data parameters hostname
, password
, and txt
set to the domain
name of the record that is to be updated,
the key configured above,
and the desired contents of the record, respectively.
For dynamically-updatable TXT records to be useful, their TTL (time to live; the time that a record will be cached for) must be set to a low value. Five minutes—300 seconds—is currently the lowest possible value, and works well.
Thus, to configure DNS verification, one must create a TXT record for the
_acme-challenge.
subdomain of each domain that one wishes to verify
and configure it for dynamic configuration as described above.
Next, one must set up scripts
for certbot
to use to update the DNS records.
Mine are below; feel free to adapt them.
Code
certbot.zsh
This script is used to obtain a new certificate,
to add domains to an existing certificate,
or to renew a certificate.
The -d
and --certname
options will (obviously) have to be changed if this
script is to be reused, as will the path to certbot-dns.zsh
.
Once properly configured, generating new certificates, adding additional
domains to current certificates, and renewing certificates can simply be
accomplished by running ./certbot.zsh
.
Please note that this script will attempt to verify that each domain
has been updated and that the cache has expired;
this can take up to 6 minutes 40 seconds when the TTL is set to 5 minutes.
As of yet, I know of no way to parallelize the updating and verification
process because certbot
calls its manual auth script serially, once for each
domain.
1#!/usr/bin/env zsh
2sudo certbot certonly --manual --manual-auth-hook /home/tucker/certbot-dns.zsh \
3 --preferred-challenges dns --cert-name maildrop.the-twomeys.com \
4 -d maildrop.the-twomeys.com -d autoconfig.the-twomeys.com \
5 -d basslake.the-twomeys.com
certbot-dns.zsh
This script is used by certbot
.
Each domain must have an entry in the case
statement at the top
that sets the variable $key
to the key used when configuring dynamic DNS in
Hurricane Electric's DNS record editor.
1#!/usr/bin/env zsh
2case $CERTBOT_DOMAIN
3in
4 maildrop.the-twomeys.com)
5 key=XXXXXXXXXXXXXXXX ;;
6 autoconfig.the-twomeys.com)
7 key=XXXXXXXXXXXXXXXX ;;
8 basslake.the-twomeys.com)
9 key=XXXXXXXXXXXXXXXX ;;
10 esac
11print $CERTBOT_DOMAIN $key $CERTBOT_VALIDATION
12acm_domain="_acme-challenge.$CERTBOT_DOMAIN"
13result=$(curl -vv "https://dyn.dns.he.net/nic/update" \
14 -d "hostname=$acm_domain" \
15 -d "password=$key" \
16 -d "txt=$CERTBOT_VALIDATION")
17print $result
18[[ $result != "badauth" ]] || exit 2
19
20if [[ $(dig +short $acm_domain TXT) != "\"$CERTBOT_VALIDATION\"" ]]
21then
22 print "Waiting for updates"
23 sleep 100
24 if [[ $ctr > 3 ]]
25 then
26 exit 1
27 else
28 ctr=$(( $ctr + 1 )) exec $0
29 fi
30fi