I’ve been using DNSSEC for some quite time now and it is working quite well. When LetsEncrypt went public beta I jumped on the train and migrated many services to LE-based TLS. However there was still one small problem with LE certs:
When there is a new cert, all of the old TLSA resource records are not valid anymore and might give problems to strict DNSSEC checking clients. It took some while until my pain was big enough to finally fix it by some scripts.
There are at least two scripts involved:
1) dnssec.sh
This script does all of my DNSSEC handling. You can just do a “dnssec.sh enable-dnssec domain.tld” and everything is configured so that you only need to copy the appropriate keys into the webinterface of your DNS registry.
host:~/bin# dnssec.sh No parameter given. Usage: dnsec.sh MODE DOMAIN MODE can be one of the following: enable-dnssec : perform all steps to enable DNSSEC for your domain edit-zone : safely edit your zone after enabling DNSSEC create-dnskey : create new dnskey only load-dnskey : loads new dnskeys and signs the zone with them show-ds : shows DS records of zone zoneadd-ds : adds DS records to the zone file show-dnskey : extract DNSKEY record that needs to uploaded to your registrar update-tlsa : update TLSA records with new TLSA hash, needs old and new TLSA hashes as additional parameters
For updating zone-files just do a “dnssech.sh edit-zone domain.tld” to add new records and such and the script will take care e.g. of increasing the serial of the zone file. I find this very convenient, so I often use this script for non-DNSSEC-enabled domains as well.
However you can spot the command line option “update-tlsa“. This option needs the old and the new TLSA hashes beside the domain.tld parameter. However, this option is used from the second script:
2) check_tlsa.sh
This is a quite simple Bash script that parses the domains.txt from letsencrypt.sh script, looking up the old TLSA hash in the zone files (structured in TLD/domain.tld directories), compare the old with the new hash (by invoking tlsagen.sh) and if there is a difference in hashes, call dnssec.sh with the proper parameters:
#!/bin/bash set -e LEPATH="/etc/letsencrypt.sh" for i in `cat /etc/letsencrypt.sh/domains.txt | awk '{print $1}'` ; do domain=`echo $i | awk 'BEGIN {FS="."} ; {print $(NF-1)"."$NF}'` #echo -n "Domain: $domain" TLD=`echo $i | awk 'BEGIN {FS="."}; {print $NF}'` #echo ", TLD: $TLD" OLDTLSA=`grep -i "in.*tlsa" /etc/bind/${TLD}/${domain} | grep ${i} | head -n 1 | awk '{print $NF}'` if [ -n "${OLDTLSA}" ] ; then #echo "--> ${OLDTLSA}" # Usage: tlsagen.sh cert.pem host[:port] usage selector mtype NEWTLSA=`/path/to/tlsagen.sh $LEPATH/certs/${i}/fullchain.pem ${i} 3 1 1 | awk '{print $NF}'` #echo "==> $NEWTLSA" if [ "${OLDTLSA}" != "${NEWTLSA}" ] ; then /path/to/dnssec.sh update-tlsa ${domain} ${OLDTLSA} ${NEWTLSA} > /dev/null echo "TLSA RR update for ${i}" fi fi done
So, quite simple and obviously a quick hack. For sure someone else can write a cleaner and more sophisticated implementation to do the same stuff, but at least it works for meTM. Use it on your own risk and do whatever you want with these scripts (licensed under public domain).
You can invoke check_tlsa.sh right after your crontab call for letsencrypt.sh. In a more sophisticated way it should be fairly easy to invoke these scripts from letsencrypt.sh post hooks as well.
Please find the files attached to this page (remove the .txt extension after saving, of course).
Public keys
You don’t have to change public key with every cert renewal. Actually you can just resign same keys and then you don’t have to change TLSA if it contains only public key and not whole cert hash.
True, and basically I thought
True, and basically I thought I did everything right and listing the issueing CA for the TLSA RRs, but for some unknown reasons, this didn’t work with my setup. Instead everytime LE renewed the certs, the TLSA was wrong. So scripting around this issue was really a quick fix. The correct fix might have been to investigate why the TLSA RRs are not working properly as expected.
But for now this script seems to easily fix my problem. Maybe I investigate the other problem later. 🙂
Download permissions
Hi,
it is not possible to download the scripts.
Could you check the permissions, please?
Or put it on a gist on github instead
Greetings
Christian
The permissions on disk were
The permissions on disk were ok, but anyway, after re-uploading as *.sh.txt this seems to work (Save Link as…)
Dankeschön und ein schönes
Dankeschön und ein schönes Wochende
What about the lifetime of the (old) TLSA record
Hello,
while it’s nice to have script support to fix TLSA records that became wrong because of a cert update I wonder if cached records are a problem. That is, the lifetime of a record is (say) one day and even after you updated your name server a machine trying to send you an email might still use the old record for nearly a whole day and so fail to send the message.
I don’t see an easy way to fix this. You could change IPs at the same time and assume that a client connecting to the new IP also has the new TLSA record, but I’m not sure this is robust. So maybe the only way is to decrease the life time of the TLSA record before the update. Or find out how to keep the key on an update …
Best regards
Uwe
Sure, if the TTL is not
Sure, if the TTL is not expired for the old TLSA RR, you can nothing do about this. So the solution would be to have a short TTL for the TLSA RRs to have the impact of renewing the TLSA RR as small as possible.
In the past I realized a wrong TLSA RR only after some days, so when the TTL is now 30 mins or so, this is a good improvement overall. Not perfect, but fairly ok in my opinion.