As of recently, Let's Encrypt will let you have wildcard certificates, but you have to use the ACMEv2 API server and the DNS challenge. The DNS challenge requires adding a record to DNS with a validation token to allow LE to validate you have control over the domain.
There are a few ways to handle this. The simplest is to manually manipulate the DNS records to add the validation token every time the certificate comes up for renewal, which is 90 days. This is a bit cumbersome and prone to human error. It's therefore possible to automate this, and the certbot
ACME client comes with a bunch of plugins which allow you to hook into various DNS providers' management API's. However, if you're running your own nameserver (like me) and running nameserver software which doesn't support RFC2316 (which looks like everything apart from ISC Bind 9 from what I can tell), then this becomes slightly more involved.
certbot
has an option for performing domain validation manually instead of using a plugin (such as the webroot plugin, which is the most useful one to me), and allows you to specify external hook programs to call to set up the authentication token and to clean up post-verification out of band. All you need to do is write some scripting to automate the DNS changes.
I use djbdns on my authoritative nameservers -- I have it set up like this:
Each nameserver has a dns
user which is dedicated to DNS management. The dns
user's home directory is /var/local/dns
.
tinydns
is configured to chroot()
to /var/local/dns/tinydns
, and look for its data.cdb
file there.
On the primary nameserver, there is a directory at ~dns/zone
which has a file for each of my servers containing the DNS records pertaining to that server, a file with mail-related records for each of my domains and a file with the authority records for each of my domains.
Also on the primary, there's a script in ~dns/bin
which concatenates all the files in ~dns/zone
into the file ~dns/tinydns/data
, changes directory to ~dns/tinydns
and then runs tinydns-data
to compile the data.cdb
file. This script also pipes the contents of the concatenated data file over ssh
to the backup nameserver, which is then similarly compiled locally there.
Also of note is that my webserver and primary nameserver run on the same machine, which simplifies the automation of changing DNS records a lot. However, it wouldn't be overly difficult to adapt the scripting to call a script over ssh
to perform the updates instead.
So, I have the following script at /root/scripts/letsencrypt-authenticator.sh
, which adds the challenge record to the DNS:
#!/bin/bash -e
basedir="/var/local/dns/zone" # directory with DNS data files
dnsuser="dns"
updatescript="/var/local/dns/bin/bootandpush.sh" # this script compiles data.cdb locally and pushes
# the data file to the backup nameservers
authdomain="$CERTBOT_DOMAIN" # domain for which validation is being performed
authstring="$CERTBOT_VALIDATION" # string to be entered into DNS
txtdomain="_acme-challenge.$authdomain" # label to insert record under, e.g. _acme-challenge.in-addr.xyz
tinydnstxt="'$txtdomain:$authstring:5" # tinydns TXT record -- see the documentation at cr.yp.to
filename=$(mktemp -u "$basedir/ACME_VALIDATION.XXXXXX")
echo $tinydnstxt | sudo -u "$dnsuser" tee "$filename" > /dev/null
sudo -u "$dnsuser" "$updatescript"
sleep 5 # wait for the changes to settle
Then, I have this script at /root/scripts/letsencrypt-cleanup.sh
, which removes the challenge records:
#!/bin/bash -e
basedir="/var/local/dns/zone"
dnsuser="dns"
updatescript="/var/local/dns/bin/bootandpush.sh"
sudo -u "$dnsuser" rm -f $basedir/ACME_VALIDATION.*
sudo -u "$dnsuser" "$updatescript"
Certificates can then be requested by running certbot
in manual mode, specifying the above scripts as the authentication and cleanup hooks:
# certbot certonly --manual --preferred-challenges=dns --server https://acme-v02.api.letsencrypt.org/directory \
--manual-auth-hook /root/scripts/letsencrypt-authenticator.sh \
--manual-cleanup-hook /root/scripts/letsencrypt-cleanup.sh -d '*.in-addr.xyz' -d in-addr.xyz