I recently set up DNSSEC on grepular.com. This turned out to be a little more complicated than I was expecting. Most of the articles I read about DNSSEC either assumed knowledge that I didn’t have, or missed out things which I later found out to be important. This is my experience of setting up DNSSEC, in the hope that it makes things a little easier for somebody else.
Why bother setting up DNSSEC in the first place?
Setting up DNSSEC on grepular.com has allowed me to sign my DNS records. So any system that has an authenticating DNS resolver, can automatically verify if the grepular.com records it looks up are valid, or have been tampered with by a MITM.
Hear are some use cases:
1.) SSL Certificate Pinning
I have published a fingerprint of the SSL certificate used on this website in the DNS, following the latest draft revision of the DANE protocol. So not only is my SSL certificate signed by a CA, it is also signed by my own DNSSEC key. So in other words, if a CA is compromised, and they generate an SSL certificate for grepular.com, browsers with DANE capability will know that the certificate isn’t to be trusted:
mike@alfa:~$ dig +short TYPE65468 _443._tcp.grepular.com \# 35 010101CA046E204044FDD508DCB096FED9881A052061ABCD29D915C8 8712A818F283E7
No browsers support this capability natively yet, as the specification is still going through the standards process. However, there is a Firefox addon. There is a similar piece of technology in Google Chrome called DNSSEC Stapled Certificates, but it’s not backwards compatible with CA signed certificates unfortunately.
I also publish the fingerprint of my SSH servers public key in the DNS (rfc4255). Usually, when people SSH into a server for the first time, they are prompted with a fingerprint and asked to confirm that it is valid. More often than not, people just accept whatever they’re given, even though this leaves the possibility that the connection is being MITM’d. Because I have a DNSSEC protected SSHFP record, OpenSSH is able to lookup whether or not the key is valid, automatically. To turn this option on, you have to add “VerifyHostKeyDNS yes” to /etc/ssh/ssh_config, or supply that option on the command line when using ssh (ssh -o “VerifyHostKeyDNS yes” grepular.com).
mike@alfa:~$ dig +short sshfp grepular.com 2 1 D027033D70738EEF8F2D30ED4B0D507C99D35BB1 1 1 4BDBAB48F0CE98D51FCA81AC7C915B00B20993BF
ssh-keygen can tell you what values to stick in your zone file:
mike@alfa:~$ ssh-keygen -r grepular.com grepular.com IN SSHFP 1 1 4bdbab48f0ce98d51fca81ac7c915b00b20993bf grepular.com IN SSHFP 2 1 d027033d70738eef8f2d30ed4b0d507c99d35bb1
I use PKA to publish fingerprints of my public PGP keys, and their location, in the DNS. The value in protecting these records with DNSSEC should be obvious. GnuPG supports these records with the “pka-lookups” option:
mike@alfa:~$ dig +short txt mike.cardwell._pka.grepular.com "v=pka1\;fpr=35BCAF1D3AA21F843DC3B0CF70A5F5120018461F\;uri=http://grepular.com/0018461F.pub.asc"
If you wanted to automatically fetch my key, and encrypt something using it, you’d run:
gpg --auto-key-locate pka -ear email@example.com
GnuPG will happily download public keys from the DNS, even if the DNS records aren’t signed. If you’re using a resolver which supports DNSSEC though, you benefit from the fact that URL to the key, and more importantly, the fingerprint, can’t be tampered with.
I also use DKIM and SPF for “signing” my email, and can be confident that systems which use DNSSEC are getting the correct information.
mike@alfa:~$ dig +short txt dkim1._domainkey.grepular.com "k=rsa\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwe0CGlJQq6Y0poniuhn80rsC1kKrcVg19STXqgM8wxe4HidRjr8KfSfCo0wcgqVq8saqsB0JCt2WOquRlUG5qRtrZag6G/wpYkRYCjYm8VsaJDxNZFpiauXeyz9HGKkHTo2QxLVIYFzkSo7ZtRQNnUB1N12/v5BscLk6X1DlmawIDAQAB" mike@alfa:~$ dig +short txt grepular.com "v=spf1 include:spf.grepular.com -all"
If I haven’t convinced you that using, or at least learning about DNSSEC is useful, you may as well leave now. The rest of this article is about how it works, and how to use it.
How DNSSEC works
Imagine your normal non-dnssec capable DNS resolver looks up the NS record for grepular.com:
mike@alfa:~$ dig +short NS grepular.com puck.nether.net. ns1.grepular.com. ns1.rollernet.us. ns2.rollernet.us.
Now, what happens when your dnssec capable DNS resolver does the same?
mike@alfa:~$ dig +short +dnssec NS grepular.com puck.nether.net. ns1.grepular.com. ns1.rollernet.us. ns2.rollernet.us. NS 5 2 86400 20120104091001 20111205091001 22506 grepular.com. HeHr+9TxG5pIk2nnTTxX8jLiC3vh/W4s5VqRCZ9KAXN+JkBDtqNSRjRf 727BzKjyvhikDNyWENOuAf9BZP0u5Nmwnp1eeyF0sb+PKstH7tbGvF2t NXXn0Yzng3ykcGvqL+5e06sdiRIgksJwSiTksFZCr3wNbBNgw1ZajQW4 yYg=
Not only did you get the “NS” records in the response, but you also received a Resource Record Signature (RRSIG) record. In a DNSSEC signed zone, every single record set (RRset) also has one (sometimes more) corresponding RRSIG record. These RRSIG records basically contain the signature generated by signing the result with your private key.
In order to verify that the signature is correct. The resolver now needs to get your “DNSKEY” record:
mikeshort DNSKEY grepular.com 257 3 5 AwEAAcu5rCE586e51lJN6VbfFeEahZkzFoDTVraDm9mdBhNBlvezy0uY TFjr393XAiwSwUkci5ntOnmKNbRXV/1FlppQMN0OGz1O7oQk0tjHsZyh tQuWEIn1LtCTSL4oQ7sx7NmAClYVoNnJLzEMnzKLGoki/HvDAhQ1YCq6 NW29PFtaDPCDPdO7sUjdc5g+0Nsg/AK22lC8ycFWTXU5HX8MdJSQ2+qP nQaDrxdpMkhvIPl/QZnDDKxp3ztx0Ptb3pFtqvD8SadZ9n0wi5eW8VLF A8SzCgZS5dR1T5XvrVGX/QfrEevssys34J5izrRcozazXlv1JntQuVFp X2+/v6GQHVk= 256 3 5 AwEAAb1g1AdtBqWi8eo83Q86+7YOUBfeAmOU7smXNQBIB1KkUvVUVdfZ 4apv0qosp8eMLF639YXnosMP9Pm3Cdl9T9IaPdL1DgLvR8FQrfZZQBB1 sGuQ36ZoXUJy2B6qq6y5IHSDEPhaJ0qUQz4m3ofNxWlCEXX3EqziQPxj hzI9cEsH mike :~$:~$ dig +
There are two records there rather than one. For now, we will just pretend there is one. I’ll explain why that is later.
So now your resolver has the NS records, their signature, and the public key corresponding with the private key which was used to sign them. That’s enough to verify that the record is valid. Or is it? What if the DNSKEY and RRSIG values were modified, or stripped altogether? How would we know? We generate a fingerprint of our public key, and we tell the parent zone “com” to stick it in a Delegation Signer (DS) record:
mike@alfa:~$ dig +short DS grepular.com 24568 5 1 6E2169767F181F91FE26DEF510AB051FF5689BC6
This DS record is not in the “grepular.com” zone. It’s in the “com” zone. So how does your resolver know that this DS record hasn’t been modified/stripped? Because “com” also has a DNSKEY record, and has signed the DS record using its private key, placing the result in another “RRSIG“ record. How do we know that the DNSKEY record for “com” is valid? Because the root zone has a DS record containing a fingerprint of it, and a DNSKEY record, and an RRSIG record to go along with it. How do we know that the root zone key hasn’t been tampered with? Basically, because your resolver should come primed with that information. I use Bind on Debian, and the key can be found in /etc/bind/bind.keys and can also be downloaded from https://www.isc.org/bind-keys.
Because of this hierarchical system of verification, and the requirement that you should be able to supply a record for your parent zone to put in a DS record, not everyone can set up DNSSEC for their domain presently.
Requirements for signing
Your parent zone, and it’s parent zones, all the way to the root, must support DNSSEC. To pick a TLD at random, “ru” does not support DNSSEC. We can tell this by looking to see if it has a DNSKEY record:
mike@alfa:~$ dig +short DNSKEY ru mike@alfa:~$
Nope. So any domain ending “.ru” can not use DNSSEC.
Your registrar must provide a method for uploading DS records to the parent zone. grepular.com used to be with name.com, who do currently provide a method for uploading DS records for “.org” domains and other TLDs, but not for “.com” domains yet. I transferred grepular.com to GKG as they do support this. They provide both a web interface for uploading the DS key, and also a very basic but easy to use API for automatically uploading it.
The authoratitive DNS servers and management interfaces for your domain must all support DNSSEC. The DNS for grepular.com was previously hosted by Linode, as I was already using them for my actual server hosting. Their DNS management interface doesn’t let you add DNSKEY, RRSIG or any other DNSSEC related records. So I decided to host the DNS for myself. I installed the Bind DNS server and hunted down two different free secondary DNS providers which support both DNSSEC and IPv6. The providers I settled with are puck.nether.net and rollernet.us.
Another benefit of using DNSSEC is that your secondary nameservers can’t interfere with your zone. If they serve any records other than the ones that you send them, then DNSSEC supporting resolvers will be able to tell.
Different Key Types
To complicate things a little, we don’t just have a single keypair. We have two different types: Zone Signing Keys (ZSK) and Key Signing Keys (KSK). We publish both of these in the DNS:
mikeshort DNSKEY grepular.com *257* 3 5 AwEAAcu5rCE586e51lJN6VbfFeEahZkzFoDTVraDm9mdBhNBlvezy0uY TFjr393XAiwSwUkci5ntOnmKNbRXV/1FlppQMN0OGz1O7oQk0tjHsZyh tQuWEIn1LtCTSL4oQ7sx7NmAClYVoNnJLzEMnzKLGoki/HvDAhQ1YCq6 NW29PFtaDPCDPdO7sUjdc5g+0Nsg/AK22lC8ycFWTXU5HX8MdJSQ2+qP nQaDrxdpMkhvIPl/QZnDDKxp3ztx0Ptb3pFtqvD8SadZ9n0wi5eW8VLF A8SzCgZS5dR1T5XvrVGX/QfrEevssys34J5izrRcozazXlv1JntQuVFp X2+/v6GQHVk= *256* 3 5 AwEAAb1g1AdtBqWi8eo83Q86+7YOUBfeAmOU7smXNQBIB1KkUvVUVdfZ 4apv0qosp8eMLF639YXnosMP9Pm3Cdl9T9IaPdL1DgLvR8FQrfZZQBB1 sGuQ36ZoXUJy2B6qq6y5IHSDEPhaJ0qUQz4m3ofNxWlCEXX3EqziQPxj hzI9cEsH:~$ dig +
You can tell the type of key by looking at the first number in the result. Records starting “257” contain KSKs, and records starting “256” contain ZSKs. We sign the zone with our ZSK, and we then sign the ZSK with our KSK. The DS record that our parent zone contains, is the fingerprint of our KSK.
This is done for efficiency and security reasons. We want to use a small key for signing records, and change that regularly, but we want to protect the whole thing with a large key, which we update less frequently, and which can be kept offline, so our parent zones don’t have to change the DS record all the time. The default key size for a KSK in Bind is 2048 bits, and 1024 bits for ZSKs. I will explain how to generate these keys and use them to sign, later on.
So far we’ve discussed DS, DNSKEY and RRSIG records. There are also two other DNSSEC record types called NSEC and NSEC3.
You don’t need to know much about these, as the tool you use to sign your zone will just automate their creation. However, you do need to know what they are, as there are privacy implications. Looking up nonexistanthostname.grepular.com and nonexistanthostname2.grepular.com both return the same result. “NXDOMAIN”. The result of signing NXDOMAIN with a particular key is always going to be the same. So what is stopping a MITM from intercepting my signed response for a valid record, and replacing it with a signed NXDOMAIN response, which they have previously looked up? We don’t want to dynamically sign the response with the request being performed as that would require the signing key to be online, and would provide an opportunity for DOS attacks. We can’t pre-sign every possible NXDOMAIN record as there are so many possibilities we’d run out of disk space. So NSEC records were invented.
When you get an NXDOMAIN, you also get NSEC records. Your zone is sorted alphabetically, and the NSEC records point to the record after the one you looked up. Without going into much detail, this fixes the problem. So what is NSEC3, and why do we need it? Unfortunately, the existance of NSEC records, means it is possible for anyone to “walk” your entire zone, getting a list of all of your hostnames:
mike@alfa:~$ dig +short NSEC grepular.com _adsp._domainkey.grepular.com. A NS SOA MX TXT AAAA SSHFP RRSIG NSEC DNSKEY mike@alfa:~$ dig +short NSEC _adsp._domainkey.grepular.com _asp._domainkey.grepular.com. TXT RRSIG NSEC mike@alfa:~$ dig +short NSEC _asp._domainkey.grepular.com dkim1._domainkey.grepular.com. TXT RRSIG NSEC mike@alfa:~$ And so on...
All you need to know, is that NSEC3 solves this problem by hashing the hostnames, at the expense of being more processor intensive. I intend to move to using NSEC3 when I have learnt more about it. What’s so bad about walking the DNS anyway? Imagine a world where everybody used DNSSEC, NSEC and PKA records for PGP. Spammers would abuse domain walking to obtain lists of every email address.
Setting up DNSSEC with Bind
Bind comes with several DNSSEC helper tools. The ones you will use most are “dnssec-keygen”, “dnssec-signzone” and “dnssec-dsfromkey”. If you have read everything above, you should have some idea of what each of these do. Imagine a scenario where you want to sign the zonefile for grepular.com, the zone is stored in a file at /etc/bind/grepular.com/zone and the keys are stored in /etc/bind/grepular.com/keys/.
cd /etc/bind/grepular.com/ # Create a Key Signing Key (KSK) dnssec-keygen -K keys/ -f KSK grepular.com # Create a Zone Signing Key (ZSK) dnssec-keygen -K keys/ grepular.com # Sign the zone, and write it out to a file named zone.signed dnssec-signzone -S -o grepular.com -K keys/ zone # Generate a Delegation Signer (DS) record to upload to your registrar dnssec-dsfromkey -1 -K keys/ -f zone.signed grepular.com
When you sign a zone, the signature records (RRSIG) expire after 30 days. The recommendation that I came across is that you should resign your zone once a day. I also came across recommendations that you should replace your ZSK once every few months, and your KSK once a year. Replacing your ZSK or KSK is known as “rolling”. It’s not as straight forward as pulling out the old key and records, and replacing with new ones. DNS propagation delays confuse matters. Imagine you have a maximum TTL on your records of 24 hours. If you replace your DNSKEY record and resign, there may be DNS servers out there which have an RRSIG record cached, but which need to look up the DNSKEY record again. They then find themselves with a signature which was not generated using the key they have just retrieved. It’s not just the TTL either, if you have DNS slaves which take a while to update their records, you need to take that time into consideration too.
The solution is simple. You can have multiple DNSKEYs and/or RRSIGs in the zone. Only one of the available keys needs to be able to verify one of the available signatures for a record in order for it to pass. So you have two options, publish your new key alongside the old key until DNS propagation has completed, then resign all your records with the new key and drop the old one. Alternatively, publish your new key alongside the old key, and immediately add a second signature for each record. After DNS propagation has ended, drop the old key and the old signatures.
Your parent zone should also contain multiple DS records when you’re advertising multiple KSKs. One for each key.
Wow. This sounds complicated now doesn’t it? Well, it isn’t actually. dnssec-keygen has options to deal with key expiry. You can add flags for when a key should be Published (DNSKEY record added to zone), Activated (RRSIG records added to the zone), Inactive (RRSIGS removed from the zone) and Deleted (DNSKEY removed from the zone). Using these options just adds some meta data to the comments at the top of the key file that you generated. However, when you use “dnssec-signzone”, it uses this data to decide whether or not to put keys into the zone, and whether or not to sign with them.
Bind can now actually be configured to automate the process of signing new records and rolling out new keys. Look up the “update-policy” option. However, if you hand over automation to Bind, you lose the ability to use plain text zone files. And if you want to create new records, you have to dynamically insert them into the running Bind process.
I wrote my own short scripts which call dnssec-signzone and dnssec-keygen from cron in order to sign my zone, so I would have a better overview of the process, and so that I would be able to continue to use plain text zone files. Don’t forget to always resign your zone if you make any changes to it.