Skip to content

example dns 01 nsupdate script

JP Mens edited this page Dec 19, 2020 · 17 revisions

Example hook script using Dynamic DNS update utility for dns-01 challenge

This hook script uses the nsupdate utility from the bind package to solve dns-01 challenges.

Code

#!/usr/bin/env bash

#
# Example how to deploy a DNS challenge using nsupdate
#

set -e
set -u
set -o pipefail

NSUPDATE="nsupdate -k /path/to/Kdnsupdatekey.private"
DNSSERVER="127.0.0.1"
TTL=300

case "$1" in
    "deploy_challenge")
        printf "server %s\nupdate add _acme-challenge.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${TTL}" "${4}" | $NSUPDATE
        ;;
    "clean_challenge")
        printf "server %s\nupdate delete _acme-challenge.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${TTL}" "${4}" | $NSUPDATE
        ;;
    "deploy_cert")
        # optional:
        # /path/to/deploy_cert.sh "$@"
        ;;
    "unchanged_cert")
        # do nothing for now
        ;;
    "startup_hook")
        # do nothing for now
        ;;
    "exit_hook")
        # do nothing for now
        ;;
esac

exit 0

The file /path/to/Kdnsupdatekey.private looks like this:

key "<keyname>" {
  algorithm hmac-sha512;
  secret "<key>";
};

To avoid making your entire production DNS subject to dynamic DNS updates, then for each certificate domain you want:

  1. In your main DNS infrastructure create a delegation: _acme-challenge.<domain>. NS <your-nameserver>.
  2. Create a new zone _acme-challenge.<domain> on <your-nameserver>, with an empty zonefile (just an SOA and NS record), writeable by the nameserver
  3. Create a new TSIG key: tsig-keygen -a sha512 <keyname>
  4. Enable dynamic updates on the _acme-challenge.<domain> zone with this key

e.g. for bind9:

key "<keyname>" {
  algorithm hmac-sha512;
  secret "<key>";
};
zone "_acme-challenge.<domain>" {
  type master;
  file "/var/cache/bind/_acme-challenge.<domain>";
  masterfile-format text;
  allow-update { key "<keyname>"; };
};

This is a secure approach because each host will have its own key, and hence can only obtain certificates for those domains you have explicitly authorized it for.

An alternative approach is to use CNAMEs to put all your dynamic updates into a single zone. You will need to modify the script:

ZONE="acme.mydomain.com"
...
    "deploy_challenge")
        printf "server %s\nupdate add _acme-challenge.%s.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${ZONE}" "${TTL}" "${4}" | $NSUPDATE
        ;;
    "clean_challenge")
        printf "server %s\nupdate delete _acme-challenge.%s.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${2}" "${ZONE}" "${TTL}" "${4}" | $NSUPDATE

You then only need to create a single zone acme.mydomain.com which accepts dynamic DNS updates, but you will need to add static CNAMEs for _acme-challenge.<certname> pointing at _acme-challenge.<certname>.acme.mydomain.com for each certificate you want to issue.