There are two TV series adaptations of Johannes Mario Simmel’s “Es muss nicht immer Kaviar sein”: the first with O.W. Fischer in 1961, and the second with Siegfried Rauch in 1977. I think I saw the second, but I digress: the name of the book occurred to me when thinking about this blogticle.

NLnet Labs make DNS software (and more), and amongst their best-known utilities are probably the recursive Unbound server and the authoritative NSD server, one which was originally written to drive a root DNS server.

I’m occasionally asked whether NSD can be used to serve a DNSSEC-signed zone (the answer is ‘yes’), but NSD isn’t a signer: it requires other utilities to actually sign zones which it can then serve. I’ve occasionally mentioned ldns, specifically when I wrote about using a SmartCard-HSM for DNSSEC and again when I wrote about DNSSEC signing with an offline KSK.

But let’s look at the task of signing one or more zones and serving them with NLnet Labs programs.


We start with an unsigned zone file, here for the domain example.aa (aa is a user-assigned code element.)

$TTL 3600
@	SOA	nsd  jp  1 3H 1H 1W 1H
	NS      nsd
nsd	A
	AAAA	2001:0db8:0:0:0202:b3ff:fe1a:8329

I’d like to sign this zone with a single Combined Signing Key (CSK), also called a Single Signing Key (SSK), and I chose algorithm ECDSA Curve P-256 with SHA-256 (number 13) for the key, and I specify the key will have flags 257 on it (KSK), though that is not strictly necessary:

$ ldns-keygen -a13 -k example.aa

$ ls -l K*
-rw-r--r--  1 jpm  staff   94 Nov  6 13:16 Kexample.aa.+013+22967.ds
-rw-r--r--  1 jpm  staff  153 Nov  6 13:16 Kexample.aa.+013+22967.key
-rw-------  1 jpm  staff  114 Nov  6 13:16 Kexample.aa.+013+22967.private

The key filenames begin with a capital K (for “key”), are followed by the zone name (“example.aa”, the key algorithm (13), the key ID or key tag, and the file extension:

  • the file containing the Delegation Signer record(s)
  • the public key (later queryable via the DNSKEY resource record)
  • the private key

I keep the private key safely (ldns has removed group and other permissions, and I tend to chmod 400 the file to ensure I don’t overwrite it accidentally).

The *.ds file contains the Delegation Signer (DS) record which must be added to the parent zone. I can reproduce this file at will with a different ldns utility, and with the -n option I decide whether I want the DS printed to stdout or not:

$ ldns-key2ds -2 Kexample.aa.+013+22967.key
$ cat Kexample.aa.+013+22967.ds
example.aa.	3600	IN	DS	22967 13 2 cfb29a4218cf608d93c08c0d14c3e315b9ab85797bf20269b820ed3a2c1d8f47

$ ldns-key2ds -n -2 Kexample.aa.+013+22967.key
example.aa.	3600	IN	DS	22967 13 2 cfb29a4218cf608d93c08c0d14c3e315b9ab85797bf20269b820ed3a2c1d8f47

Having generated the required key(s), I can now sign the zone. I specify -u to have the SOA serial number set to the Unix epoch time, the -o origin (i.e. zone name) of the zone I’m signing, and the key filename of the key ldns should use to sign the zone (without its file extension).

$ ldns-signzone -u \
                -o example.aa \
		example.aa \

$ ls -l *.signed
-rw-r--r--  1 jpm  staff  1762 Nov  6 13:22 example.aa.signed

ldns-signzone uses NSEC by default. Use -n to have it create NSEC3 records compliant with RFC 9276 — SHA-1, no extra iteration, empty salt and opt-out not set.

The ldns-read-zone utility is very practical. I can use it to strip DNSSEC data from a zone file – something I do when I want to concentrate on the actual zone data. Here I’ve used it to print out just one record type.

$ ldns-read-zone -E SOA example.aa.signed
example.aa.	3600	IN	SOA	nsd.example.aa. jp.example.aa. 1667737361 10800 3600 604800 3600

Finally, to ensure the signed zone is valid, I can use another utility to verify that, though I prefer using something written by a different developers to ensure they agree that the zone is valid.

$ ldns-verify-zone -V4 example.aa.signed
Zone is verified and complete

$ validns -p all example.aa.signed
example.aa.signed:3: there should be at least two NS records per name
example.aa.signed:5: No KSK found

validns is a bit of an older utility but still quite useful. Here it complains that my zone has only one NS record (correct, it does), and that no KSK was found. This latter has to do with validns not realizing that a zone can be signed with a single key. I ignore this warning because I know the zone is otherwise valid.


I configure NSD to load my zone from the *.signed file and serve it:

    name: "example.aa"
    zonefile: "example.aa.signed"
    provide-xfr: NOKEY

After reloading the server I query it and see the signatures (I use +nocrypto to omit the dreary base64-encoded stuff):

$ dig @::1 example.aa SOA +dnssec +multi +nocrypto +norec
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 65367
;; flags: qr aa; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

; EDNS: version: 0, flags: do; udp: 1232
;example.aa.		IN SOA

example.aa.		3600 IN	SOA nsd.example.aa. jp.example.aa. (
				1667737361 ; serial
				10800      ; refresh (3 hours)
				3600       ; retry (1 hour)
				604800     ; expire (1 week)
				3600       ; minimum (1 hour)
example.aa.		3600 IN	RRSIG SOA 13 2 3600 (
				20221204122241 20221106122241 22967 example.aa.
				[omitted] )

;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Sun Nov 06 12:37:16 UTC 2022
;; MSG SIZE  rcvd: 188

Signatures (RRSIG) have a default validity of 30 days, but I can easily change that. I’ve created eitime which prints a time stamp in UTC YYYYMMDDHHMMSS for RRSIG expiration/inception times and makes my life easier:

$ ldns-signzone -e $(eitime -z 180) ...

Don’t forget to re-sign and reload the zone in a timely manner before its signatures expire. Cron is your friend.


There’s one thing missing in order to complete the chain of trust to my zone: submit the DS record to the parent, and all I can do here, unfortunately, is to say “that’s your problem”. The method by which you do this varies widely.

Regarding caviar: signing with ldns and serving with NSD might well be very appealing to you, and I know people for whom this is true.

further reading

nsd, ldns, and dnssec :: 06 Nov 2022 :: e-mail