DNSSEC, here we go

TL;DR: I decided to finally take a leap into scary, dark waters and busy myself with learning at least the very basics about DNSSEC and enabling it for a single domain to try out how things would work. As it turns out I was unnecessarily scared and I successfully contributed a small bit to making the Internet a more secure place.

Everything about DNS is, intentionally, plaintext. When the protocol came about in the early 80ies there simply wasn’t serious consideration for proper mechanism to ensure security and integrity. Times were very different back then, the Internet was young and enthusiastic, a space of great prospects. Not the Orwellian hellhole of surveillance capitalism and crypto-related scams it is today.

Sorry, I am digressing. Eventually, after roughly a decade, there were discussions about and explorations of ideas that could make DNS more trustworthy. There were a couple of different proposals, but eventually DNSSEC became a proper standard in 2005.

DNSSEC is a set of extensions to the original DNS that allows for DNS records to be authenticated through cryptographic measures, solving a lot of the trust issues traditionally present with / in the DNS (the most infamous example probably being the ‘cache poisoning attack’ discovered in 2008). I’ll spare you the lengthy details, but the Cloudflare Blog has an excellent article explaining some of the issues DNS suffers from.

Similar to how DNS-resolution starts with the Root-servers, DNS-authentication starts with the Root-servers. There’s one trusted key, managed by the maintainer of the Root servers, which is the base for a chain of trust. From there trust is delegated to the the nameservers for a domain, and from there the delegation continues to the nameservers for the actual domain. If the chain of trust is broken at any point the records for your domain are no longer trusted and will be rejected by DNSSEC-aware resolvers (which, as I learned through my own experiences, are surprisingly few).

On a high level DNSSEC works like this: Each authoritative nameserver ‘owns’ a cryptographic key-pair. The private key of this pair is used to cryptographically sign sets of records within a zone, with the resulting signature itself being published as a DNS-record. The signature can be validated and verified with the help of the corresponding public key, it too being published in a DNS-record. The key-pair I just talked about would be referred to as the so-called ‘Zone Signing Key’.

The public part Zone Signing Key is itself signed by the private key of a different key-pair, the ‘Key Signing Key’. The public part of the Key Signing Key is provided to the parent zone which then publishes it as part of its own records for the child zone and is essentially used to authenticate that the information presented in the child zone is valid.

This creates a chain of trust - a resolver checks if a set of records is cryptographically signed. If it is the resolver makes sure that the public key presented was actually signed with the corresponding private key. It does that by going a level higher, asking the authoritative nameserver for the parent zone, until it eventually reaches the root zone. Or, to put it in more technical terms:

  • Your zone needs to publish two records of type DNSKEY - which contains the public part of your Zone Signing Key-pair

In the case of my test-domain the output of dig DNSKEY dnssec.live currently looks like this:

dnssec.live.		3600	IN	DNSKEY	256 3 13 +MimBhtHYDf6ZOumm5+GrzEo5RGrXDjqVpHEyloZJCPi6TqNt44cfaPW XaXRdkXXmSF7nv4QEHWOQCFSoNDbdQ==
dnssec.live.		3600	IN	DNSKEY	257 3 13 kII2BTnwSVw3HDaUfoTyiEYwc7QK+4/FO+I5pdy7VHDpGzDaEoFJ81jr 9oln7jNOklAlXegBlUta4+290IeJOA==
dnssec.live.		3600	IN	RRSIG	DNSKEY 13 2 3600 20220416085054 20220319085054 24690 dnssec.live. OK9dBBfhuapmKyK8MxqIybg3UGr03qO6Jb2FsOTmUKywGB2hITVbo9xJ VS29TlzoWwf27doK9JdtBi1IQ8K6/w==
  • Your zone needs to publish a record of type DS - which contains the hash of the Key Signing Key. This record needs to also be published by the parent zone. This allows a resolver to verify the authenticity of the child zone by hashing its KSK record, and comparing that to what is in the parent zone’s DS record.

In the case of my test-domain the output of dig DS dnssec.live currently looks like this:

dnssec.live.		3551	IN	DS	24690 13 2 2F8BD39D178E15B2CC24F28C6E37EA050677D0114B3C1C5479892D8F 54831765
dnssec.live.		3551	IN	RRSIG	DS 8 2 3600 20220408155724 20220318145724 38550 live. UYJHW7J/VW3CBxi9Y7bG+zCadCJvyXH1UGGp38lmoVWZBe76HRKN3KK/ S/c9SkJRAz2Y8fgdsFUhyHThlHMIRMWxLSC6sCH2ZQoETAHbNb6V6Arj 388h5xzWSr6ZY6ZQikcjsNIxlbkB9co3lSwpHXy3P32UhaTubZ0jJKCl x0E=

This same procedure also applies to .live, whose authoritative nameservers also have records of the types DNSKEY and DS. The root zone doesn’t have these records, the keys of the root zone are ultimately trusted. And while yes, they are controlled by IANA and thus by an US-based organisation, the ceremony to rotate the root keys is heavily scrutinized and a public event. Great care is taken by everyone involved to ensure utmost transparency to facilitate the necessary trust.

(Sidenote: I wrote and re-wrote the previous couple of paragraphs at least four times, because the mixture of keypairs and validation levels makes my head dizzy. I genuinely hope I am not telling you anything wrong here. I am still not fully satisfied with the output, but if I have to rewrite it one more time I might actually go insanen. If I do happen to be wrong, please let me know. My apologies if that’s the case.)

Even though all of this makes sense and provides significantly improved security to DNS that didn’t really mean that adoption rates were suddenly skyrocketing. Unfortunately DNSSEC is suffering a fate similar to, but worse than IPv6. Recent estimates (provided in 2020 by the Internet Society are that far less than 10% of all second level domains have adopted DNSSEC.

There are a lot of reasons for this. Network operators not wanting the added complexity or the organisational overhead, critical software lacking support for it, top level domains not supporting it, lack of awareness, network administrators steadfastly refusing to open up port 53 for TCP-traffic as well or because, at least for the first couple of years, documentation on how to implement it was severely lacking.

But I’m not an operator of a large network, I am willing to enable port 53 for TCP, software support is there, plentiful documentation is available as well. No, my reason for not implementing DNSSEC for so long was a different one.

I’m not going to lie, DNSSEC intimidated me quite a bit. Trying to secure technology that wasn’t built with security in mind is a challenging task, almost guaranteed to end up in a complex, complicated solution - especially because it involves cryptography. I understand the concept of public-key cryptography, but that’s about all I know about cryptography.

On top of that it comes with a high chance of availability issues if you make any mistakes. That in combination with my, as of now, still limited understanding of the intricacies of DNSSEC, was enough to make me a bit nervous I have recklessly destroyed important parts of my computer networks with experiments in the past. But I thought to myself: What the hell, let’s just do it and see where I end up.

Given my lack of thorough understanding of what I was about to do I decided that I’d better be safe than (potentially) really sorry. I bought a domain, dnssec.live. for the specific purpose of getting comfortable before activating DNSSEC for gmmi.me. This precaution set me back a whoping two dollars and twenty four cents. Those domain prices are getting outrageous, that’s robbery. Robbery!

In theory I thought the necessary steps to be simple and straightforward:

  • Generate key-signing key-pair for the zone
  • Generate zone-signing key-pair for the zone
  • Publish DNSKEY-records for the zone
  • Publish DS-records for the zone, and add them to my registrar
  • Sign the zonefile, do so every time a change is made to the zone

And much to my surprise my theory turned out to be right. No, seriously, that’s unusual. Usually, whenever I perceive something to be simple, I tend to have missed out some critical details that end up making my life hell.

The authoritative resolver I am using for my nameservers, nsd supports DNSSEC out of the box. A simple change to the configuration file was everything that was needed in order to be able to handle signed zonefiles:

    name: "{{ zone.name }}"
        {% if zone.signed is true %}
	    zonefile: "{{ zone.name }}.zone.signed"
	        {% else %}
		    zonefile: "{{ zone.name }}.zone"
		        {% endif %}

As for generating keys, ldns-keygen (1) is the tool of choice. For generating a Zone Signing Key:

ldns-keygen -a ECDSAP256SHA256 dnssec.live

And for generating a Key Signing Key:

ldns-keygen -a ECDSAP256SHA256 -k dnssec.live

This will result in the following output:

-rwxr-x--- 1 nsd nsd 153 Mar 18 16:01 Kdnssec.live.+013+05180.key
-rwxr-x--- 1 nsd nsd 114 Mar 18 16:01 Kdnssec.live.+013+05180.private
-rwxr-x--- 1 nsd nsd  95 Mar 18 16:01 Kdnssec.live.+013+24690.ds
-rwxr-x--- 1 nsd nsd 154 Mar 18 16:01 Kdnssec.live.+013+24690.key
-rwxr-x--- 1 nsd nsd 114 Mar 18 16:01 Kdnssec.live.+013+24690.private
  • A keyfile containing the public Zone Signing Key
  • A keyfile containing the private Zone Sigining Key
  • A DS-record, to be published with my registrar
  • A keyfile containing the public Key Signing Key
  • A keyfile containing the private Key Sigining Key

A note for people who are, as I was, looking at different blogposts on the Internet: A lot of tutorials are severely outdated, and all algorithms involving SHA-1 are definitely not to be used anymore. Even though some registrars still support DS-records with that algorithm. According to guidance by the IETF there is a specific recommendation:

Due to the industry-wide trend towards elliptic curve cryptography, ECDSAP256SHA256 is the RECOMMENDED DNSKEY algorithm for use by new DNSSEC deployments, and users of RSA-based algorithms SHOULD upgrade to ECDSAP256SHA256.

These keys can now be used to sign a zonefile through ldns-signzone (1). For example:

sudo nsd ldns-signzone -n -p -s $(head -n 1000 /dev/random | sha1sum | cut -b 1-16) /etc/nsd/zones/dnssec.live.zone /etc/nsd/zsk/dnssec.live/Kdnssec.live.+013+05180 /etc/nsd/ksk/dnssec.live/Kdnssec.live.+013+2469
  • -n ensures that NSEC3 is used instead of NSEC. I didn’t go into this, but on a very basic level those records provide an authenticated denial of existence, meaning that they ensure that a malicious third party can’t pretend that a domain name isn’t resolving despite it actually existing. NSEC had some design issues that allowed third parties to enumerate your zonefile (referred to as zone walking). The Internet Society has a post going into greater details
  • -s instructs ldns-signzone to use a Salt which is then generated by the following shell commands

After signing your zones that way all that’s left is to restart your nameserver and your responses should be secured by DNS. Querying for an A-record will now always return an RRSIG-record containing all the necessary information for your resolver to verify the authenticity of the request.

dnssec.dnssec.live.	3600	IN	A
dnssec.dnssec.live.	3600	IN	RRSIG	A 13 3 3600 20220416085054 20220319085054 5180 dnssec.live. vazkO5wwYWQxVgMuByd7GK+3JLS9W7f894MQHB4urVAWVav5t8tE+Dt2 ygeVEtW6ISllbCqo6rDfldA987nU6g==

The last thing to do is to publish your DS-record with your registrar. After this is said and done that’s really it. Not only does your resolver return authenticated responses, the trust can be verified since your resolver is now part of the chain of trust.

Aside from manually doing lookups there are various tools that help you debug issues with your DNSSEC-setup. I found dnsviz.net particularly helpful. Plus: They do create nice graphics, so please, enjoy the trust path for dnssec.live, which is currently successfully protected by DNSSEC:

dnssec trust graph for dnssec.live

The first draft of my post ended with the following paragraph:

Now that dnssec.live is successfully secured by DNSSEC I will try to do the same for this domain. So if this page is suddenly unavailable in a couple of days you know why that is - I fucked my DNS up, and given my luck the mistake I made will be so severe that recovery will take days, if not weeks. Who are you calling a cynic?

But that didn’t happen. Except for some small issues with a malformed zonefile (accidentally making this domain a nameserver) and a hiccup involving time synchronisation - both of which were fixed within a git commit each - everything went fine, this domain is now secured with DNSSEC. I learned a lot and got rid of another “I really should learn about this”-issue that unnecessarily occupied parts of my brain without paying rent.