acme-client (letskencrypt) dns-01 how-to

I just spent half my day literally yelling at the screen trying to figure out how to use acme-client (formally known as letskencrypt) for dns-01 challenges. there's no examples in the man page, none online, and the source code didn't help much.


why not certbot? I've tried it, but it insists on making its own crazy filetree structure, and I needed everything contained to a single directory (more specifically, a mountpoint shared between my LXC containers, with appropriate file permissions set). why not dehydrated? I probably could have used it, but I was attracted to acme-client for its implementation in C, portability, and minimal dependencies. I was pretty much stubborn to make it work. why not just use http-01? I run a dedicated server with containerised services and a bunch of NAT black magic, so DNS challenges allow me to create my certs in one container instead of entrusting all my containers with the task and causing more headache for myself.

anyway, after digging through an issue on github and dehydrated's source, I finally had enough information in order to implement a working dns-01 script. I hope this saves someone else from spending a day like I did, and wanting to kill themselves at the end of it.

I use mksh, but with a bit of editing you can translate it to POSIX sh, or just replace the shebang with bash. as you can see, I didn't really place much effort into making this pretty; I just wanted it to work.

#!/bin/mksh

domains=(
    'anime.website  anime.website'
    'krustykrab.restaurant  bfbb.krustykrab.restaurant'
    'gentoo.today   gentoo.today install.gentoo.today'
    'volatile.bz    git.volatile.bz'
    'krustykrab.restaurant  krustykrab.restaurant'
    # ...
)
nsupdate_key=/etc/bind/ddns.key

for line in "${domains[@]}"; do
    zone=`cut -f1 <<<"$line"`
    domainlist=`cut -f2 <<<"$line"`
    echo "Updating '$domainlist' in $zone"
    pemdir=/mnt/certs/`cut -d' ' -f1 <<<"$domainlist"`
    mkdir -p $pemdir
    acme-client -vnNmt dns-01 -c $pemdir -k $pemdir/privkey.pem $domainlist |&
    while read -p type domain token; do
        keyauth=`printf '%s' "$token" | openssl dgst -sha256 -binary | base64 | tr '+\/' '-_' | tr -d '='`
        nsupdate -4l -k $nsupdate_key <<-EOF
        zone $zone
        update delete _acme-challenge.$domain TXT
        update add _acme-challenge.$domain 60 TXT $keyauth
        send
        EOF
        wait 10
        print -p "$type $domain $token"
    done
    wait
    for domain in `echo $domainlist`; do
        nsupdate -4l -k $nsupdate_key <<-EOF
        zone $zone
        update delete _acme-challenge.$domain TXT
        send
        EOF
    done
done > /var/log/acme.log

this script is under the same licence as the rest of my site (Creative Commons Zero) and is free to redistribute and modify. let me know if this has been of any use to you.