The following is ideal for scenarios where you have an authoritative bind server and an internal/non-public system that's used to distribute SSL certificates to internal/non-public/private IP sites.
Topology:
- Bind Server - The authoritative (public facing) DNS server for your TLD that will carry the wildcard. I'm using example.com in these notes.
- Distribution Server - The intranet server that generates, accepts and disperses the certs (the latter part isn't part of these notes at this time)
Operating Systems in Use:
- Bind Server: Ubuntu
- Distribution Server: Debian
dnssec-keygen is part of Bind-Utils, so it should already be installed if you're running BIND9.
Create a key:
On Ubuntu 16.04 (was in use at the time of documenting), generating a key with dnssec-keygen will take forever. Using /dev/urandom will significantly speed up the process. Run this as a non-root / non-sudo user.
dnssec-keygen -r /dev/urandom -a HMAC-SHA512 -b 512 -n HOST example.comThe host makes sense to be the actual host you're using, but it appears it will take "certbot" type names, also.
That will create a keypair in the following conventions:
- Pubkey: Kns.example.com.+165+12345.key
- Private Key: Kns.example.com.+165+12345.private
To view the content of the keys (which you'll need to modify the upcoming config):
cat Kns.example.com.+165+12345.keyIn /etc/bind/named.conf, add:
key "example.com" {
algorithm hmac-sha512;
secret "asd.....re==;
};Find the root zone file for example.com:
cd /etc/bind && grep "example.com" -R ./
(/etc/bind/named.conf.local in most cases)
Under the zone entry for example.com, before:
zone "example.com" {
type master;
file "/etc/bind/db.example.com";
};
The file path may vary, depending on who set up the system, originally. Modify it to suit your environment.
After, adding acme challenges needed by Certbot/Let's Encrypt:
zone "example.com." IN {
type master;
file "db.example.com";
update-policy {
grant example.com name _acme-challenge.example.com. txt;
};
};If this server is running Apparmor, you'll run into some issues, out of the box:
ns kernel: [11277.173074] audit: type=1400 audit(123456.021:45): apparmor="DENIED" operation="mknod" profile="/usr/sbin/named" name="/etc/bind/db.example.com.jnl" pid=5396 comm="named" requested_mask="c" denied_mask="c" fsuid=110 ouid=110
To fix/prevent the Apparmor permissions issue, whitelist named for writing the journal file:
pico /etc/apparmor.d/local/usr.sbin.namedAppend the following:
/etc/bind/db.example.com.jnl rw,After that, you will still likely run into:
ns named[4193]: /etc/bind/db.example.com.jnl: create: permission denied
Let's look at the current permissions of /etc/bind:
ls -l /etc/ | grep binddrwxr-sr-x 3 root bind 4096 May 10 13:11 bind
Group doesn't have write permissions, hence the above error.
Since Bind will need to write to a file that doesn't currently exist, the path of least resistance is mildly weakening the security and granting it to all of /etc/bind:
chmod g+rw /etc/bind/Check the permissions again:
drwxrwsr-x 3 root bind 4096 May 10 13:11 bind
Changes to the Bind Server are now complete.
This server ideally shouldn't be public facing and secured behind a firewall with no external access.
Install the backports repo so we get the latest version of certbot
echo "deb http://deb.debian.org/debian stretch-backports main" >> /etc/apt/sources.list && apt updateInstall dependencies:
apt install certbot python3-certbot-dns-rfc2136 -t stretch-backportsCreate a config file that will be used by Certbot:
mkdir -p /root/.secrets/certbot && pico rfc2136.iniPaste the following into rfc2136.ini
# Target DNS server
dns_rfc2136_server = # "Bind Server" IP
# Target DNS port
dns_rfc2136_port = 53
# TSIG key name - this is what you named the key while running dnssec
dns_rfc2136_name = example.com
# TSIG key secret
dns_rfc2136_secret = asd.....re==
# TSIG key algorithm
dns_rfc2136_algorithm = HMAC-SHA512Note: If you will be running the following for the first time, append a --dry-run flag; you're limited to 5 cert calls per week - don't waste them on testing!
Obtain a new cert from Let's Encrypt:
certbot certonly --dns-rfc2136 --dns-rfc2136-credentials /root/.secrets/certbot/rfc2136.ini -d example.com,*.example.com --preferred-challenges dns-01 --dns-rfc2136-propagation-seconds 5You should now have a wildcard SSL certificate from Let's Encrypt!
To automate, build a cron at crontab -e (as root/sudo) with certbot renew
certbot renew cron fails:
First thing to try is a dry run on the distribution server:
certbot certonly --dry-run -vvv --dns-rfc2136 --dns-rfc2136-credentials /root/.secrets/certbot/rfc2136.ini -d example.com,*.example.com --preferred-challenges dns-01 --dns-rfc2136-propagation-seconds 5Performing the following challenges:
dns-01 challenge for example.com
No authoritative SOA record found for _acme-challenge.example.com
No authoritative SOA record found for example.com
No authoritative SOA record found for com
Encountered exception:
Traceback (most recent call last):
... python traceback errors
The authoritative record isn't being updated, which is what the automation process is supposed to take care of.
On the public-facing DNS server:
grep "zone" -R /var/logHere's the problem:
/var/log/syslog:Sep 6 14:16:29 ns named[6658]: zone example.com/IN: journal rollforward failed: journal out of sync with zone
/var/log/syslog:Sep 6 14:16:29 ns named[6658]: zone example.com/IN: not loaded due to errors.
The zone was updated manually at some point between the last successful renewal and now, causing the bind setup to get out of sync.
Fix:
- Remove it:
rm /etc/bind/db.example.com.jnl - Restart Bind:
service bind9 restart