- on Fri 28 February 2014
NOTE: This article is not in any way a complete walkthrough/tutorial of NSD deployment or DNSSEC best practices, and should not be used as such. It is more a testimony of my small experience doing those things, and using your head is required (e.g. reading the relevant RFC sections, restarting the DNS server at the right times, or clicking through the registrar web UI, and searching the web to do what you want).
DANE is, I believe, the least harmful way of handing certificates, but sadly it is restricted to a fraction of the domains which have DNSSEC enabled. The idea is to publish information about the certificates into the DNS zone, and use DNSSEC to certify the information. Sadly, DNSSEC itself isn’t widely deployed or supported on the software side, so DANE is kind of a marginal thing that is waiting to get traction.
Firefox has an addon, as well as prosody, XMPP.net has a dedicated section, and I am looking into dnspython to see how I could integrate this into poezio or sleekxmpp in the future, as part of the XMPP encryption manifesto.
I have been able to successfully and easily (systemd nonwithstanding) run NSD with DNSSEC & DANE on my domains in very little time.
First, you will probably have to run the DNS server yourself, as many registrars do not want to handle the signing themselves, leaving only a form to submit the public keys used for the signature.
If you already have a working DNS server, skip to the DNSSEC section.
If you already have a DNSSEC-enabled zone, skip to the DANE section.
Get a working NSD
I chose NSD as a nameserver instead of bind because it is lighter and less cumbersome, but bind also works, if that is what you are into.
The NSD configuration file is very simple, and does not need comments (if you need them, every option is detailed in the manpage):
server: port: 53 zonesdir: "/etc/nsd" logfile: "/var/log/nsd.log" remote-control: control-enable: "yes" zone: name: "example.net" zonefile: "example.net.zone"
Next, make sure you have a correct (working) zone file by taking the zonefile from your previous DNS hosting and adding $ORIGIN and $TTL variables, along with a valid SOA record. This should look like this:
$ORIGIN example.net. $TTL 3600 @ IN SOA ns.example.net. admin.example.net. ( ; nameserver and contact e-mail 2014022701 ; serial number, tradition is YYYYMMDDnn 28800 ; Refresh 7200 ; Retry 864000 ; Expire 86400 ; Min TTL ) @ 10800 IN A 10.0.0.1 www 10800 IN A 10.0.0.1 @ 600 IN AAAA 2a01::f00 www 600 IN AAAA 2a01::f00 _xmpp-client._tcp 3600 IN SRV 5 0 5222 example.net. _xmpp-server._tcp 3600 IN SRV 5 0 5269 example.net.
Use dig or drill to check if your nameserver is replying correctly (dig record @nameserver_ip host).
Enabling DNSSEC
Enabling DNSSEC on a domain is fairly easy.
First, generate a KSK (Key Signing Key) and a ZSK (Zone Signing Key). I chose to use the ldns tools, but there are many more.
The difference between the two is purely cosmetical, the KSK will be used to sign the ZSK, and the ZSK will be used to sign the rest of the zone. As the KSK will sign less data, less often, it can be kept longer than the ZSK, and in a more secure place.
The KSK:
$ ldns-keygen -a RSASHA512 -b 4096 -k example.net
The ZSK:
$ ldns-keygen -a RSASHA512 -b 2048 example.net
Both should output three files, a .key, a .ds, and a .private, prefixed with K<domain name>+<alg id, here 0007>+<5-digit id>.
The update frequency should be something like zone > ZSK > KSK, with several orders of magnitude inbetween.
Your registrar has probably an online form where you can submit the public part of your KSK.
Now you only have to sign the zone, and tell NSD to serve it.
Sign it with both keys (it will detect which key is a KSK and which is a ZSK and act accordingly):
$ ldns-signzone -n example.net.zone Kexample.net+010+12345 Kexample.net+010+67890
This command will produce a new example.net.zone.signed file, so you have to tell NSD to use it, which is done by changing the configured filename, that is all.
You can check the validity of your deployment using the VeriSign DNSSEC debugger (tip: use +/- to add or remove details to the results).
The default expiration date is around one month, and there are many tools to automatize the process, or send you emails when a signature is about to expire. I also strongly encourage you to read the RFC 4641 (DNSSEC Operational Practices).
Enabling DANE
Ok, so here is the easiest part.
A TLSA record (DANE is the name of the RFC) looks like this:
_443._tcp.jeproteste.info. 3600 IN TLSA 2 0 2 a37405a646c994cd594bf79ad4cbdb22e2576797340fc2806e16692f4c82d112905846f01493b59e2da89d1f93cd2d8b194756e8f8c8bec2d915c19dcdd0c196
You can see here the port, the host, the TTL, the record, and its fields (2, 0, 2, and a hash). The first field is the certificate usage field :
- Usage 0: Pin an authorized public CA (PKIX-TA).
- Usage 1: Pin a certificate, which must still be valid PKIX-wise (PKIX-EE).
- Usage 2: Pin a non-public CA (has to be sent by the server). Mirrors usage 0 (DANE-TA).
- Usage 3: Pin a certificate, which is not necessarily valid PKIX-wise. Mirrors usage 1 (DANE-EE).
The second field is the selector field:
- Selector 0: match the certificate binary structure.
- Selector 1: match the SubjectPublicKeyInfo field.
You probably only need the selector type 0.
The third field is the matching type:
- Type 0: match the raw content
- Type 1: match a SHA256 of the raw content
- Type 2: match a SHA512 of the raw content
The fourth field is naturally the matching data, the hash or content.
Fortunately, ldns integrates a ldns-dane utility that allows you to generate a TLSA record painlessy.
$ ldns-dane create mathieui.net 443 3 0 2 _443._tcp.mathieui.net. 3600 IN TLSA 3 0 2 34c34b1efbc4789ffffc5c1d132d7304a57797a3bef94b961c43bcf393f617b863f0ac9d18b5045d1f028e4f3b5cf0bbe09e030168e4a0107e2975b5db53e7c4
I don’t know how many other protocols it supports, but I know that it does not support XMPP, so you will have to generate the hash and write the record by hand.
The following script takes the filename of the certfile as a parameter and outputs a sha512 hash (change according to what you want):
#!/usr/bin/env python3 import hashlib import ssl import sys with open(sys.argv[1]) as fd: key = fd.read() key = key[key.index('-----BEGIN CERTIFICATE'):] key = ssl.PEM_cert_to_DER_cert(key) hashed = hashlib.sha512(key) print(hashed.hexdigest())
Once you are done signing the records and reloading your dns server, you can check the results with ldns-dane.
$ ldns-dane verify example.net 443
There are few options that might be needed (like -d if your default resolver doesn’t do DNSSEC, or -S -k trusted.key if you have the public keys of the root, etc).
Further reading
- The NSD documentation
- A website with resources and documentation on DNSSEC
- The DNSSEC Wikipedia page
- The DNSSEC operational practices
- The DANE RFC, which is quite straightforward
- DANE best practices
- Another blog post about TLSA
Addendum
I added the -n option to the ldns-signzone command, because otherwise you could enumerate the records in the zone; and while putting private data inside a zone file has never been recommended, if you can prevent zone walking for free, do it.
Also, the semantics of DANE integration into XMPP haven’t been finalized yet, but it will probably be better to have
_xmpp-client._tcp.mathieui.net. 3600 IN TLSA 3 0 2 34c34b1efbc4789ffffc5c1d132d7304a57797a3bef94b961c43bcf393f617b863f0ac9d18b5045d1f028e4f3b5cf0bbe09e030168e4a0107e2975b5db53e7c4
rather than
_5222._tcp.mathieui.net. 3600 IN TLSA 3 0 2 34c34b1efbc4789ffffc5c1d132d7304a57797a3bef94b961c43bcf393f617b863f0ac9d18b5045d1f028e4f3b5cf0bbe09e030168e4a0107e2975b5db53e7c4
as it is nicer to read, write, and remember.