Lets Encrypt Behind The Firewall
Dehydrated is a client for getting certificates from an ACME server (think let’s encrypt). Being written in bash, its dependencies are quite simple: curl/sed/grep/mktemp. Dehydrated allows us to write simple hooks that, in this post, we’ll use to do dns validation against our authoritative bind server. That way, you can use let’s encrypt certificates even if your server is behind a firewall! (provided it has access to your dns authoritative server)
Prerequisites #
For the rest of this post, we’ll assume that the hostname of the server we’re executing dehydrated on is vm.xiu.io
and the hostname for which we want a certificate is www.xiu.io
.
useradd -d /home/letsencrypt -m
apt update
apt install curl bind9utils
mkdir /home/letsencrypt/nskey
cd /home/letsencrypt/nskey
dnssec-keygen -a HMAC-MD5 -b 512 -n HOST -C vm.xiu.io
We have to install bind9utils to be able to generate our dnssec key, this key will then be used to push updates to specific dns records to our authoritative server.
Install dehydrated #
su - letsencrypt
wget https://github.com/lukas2511/dehydrated/archive/master.tar.gz
tar xvf master.tar.gz
mv dehydrated-master dehydrated
cp dehydrated/docs/examples/config .
Then edit config with your preferred editor and change the following:
CHALLENGETYPE="dns-01"
[...]
BASEDIR=/home/letsencrypt
[...]
HOOK=/home/letsencrypt/hook/nsupdate.sh
Also, create a hook
directory and then put the following in hook/nsupdate.sh:
#!/usr/bin/env bash
set -e
set -u
set -o pipefail
NSUPDATE="nsupdate -k /home/letsencrypt/nskey/<.key FILE WE JUST GENERATED>"
DNSSERVER="<IP OF THE AUTHORITATIVE SERVER>"
TTL=300
case "$1" in
"deploy_challenge")
ZONE=$(echo $2 | rev | cut -d'.' -f-2 | rev)
printf "server %s\nzone %s.\nupdate add _acme-challenge.%s. %d in TXT \"%s\"\nshow\nsend\n" "${DNSSERVER}" "${ZONE}" "${2}" "${TTL}" "${4}" | $NSUPDATE
sleep 30 # sleeping a bit to make sure changes have been propagated
;;
"clean_challenge")
ZONE=$(echo $2 | rev | cut -d'.' -f-2 | rev)
printf "server %s\nzone %s.\nupdate delete _acme-challenge.%s. %d in TXT \"%s\"\nsend\n" "${DNSSERVER}" "${ZONE}" "${2}" "${TTL}" "${4}" | $NSUPDATE
;;
"deploy_cert")
# do nothing for now
;;
"unchanged_cert")
# do nothing for now
;;
"startup_hook")
;;
"exit_hook")
;;
*)
echo Unknown hook "${*}"
exit 1
;;
esac
exit 0
chmod +x hook/nsupdate.sh
Configure bind #
Now that we have our key, we can tell bind to accept modifications from it. Edit /etc/bind/names.conf.local and add the following:
key "vm.xiu.io" {
algorithm hmac-md5;
secret "<CONTENT OF Kvm.xiu.io....private>"
};
Then find where your zone is configured (zone “domain.tld” {};) and add the following in it:
zone "xiu.io" {
[...]
grant vm.xiu.io name _acme-challenge.www.xiu.io. TXT;
[...]
};
And reload bind:
rndc reload
Profit! #
Now add the certificate you need in domains.txt:
echo "www.xiu.io" > /home/letsencrypt/domains.txt
Tip: if you add multiple domains on the same line, then it’ll create a certificate with multiple subjectAltNames.
The first time you start dehydrated, it’ll ask for you to review and accept let’s encrypt TOS.
/home/letsencrypt/dehydrated/dehydrated -c -f /home/letsencrypt/dehydrated/config
Execute it a second time, if all goes well, you should get the following:
+ Creating fullchain.pem...
+ Done!
You can now find your certificates in /home/letsencrypt/certs/
!
The -c
option is meant to be executed regularly, it’ll renew certificates when needed. You can create a cronjob for that!