Using Zone Key Tool to Manage DNSSEC Signed Domains with NSD

Why Use NSD?

NSD is an original authoritative DNS server from NLnet. Because it focuses solely on serving authoritative DNS data, rather than also acting as recursive DNS server (ie, one used directly by client applications), it has a simpler code base and configuration file syntax. It does have a companion program, Unbound, which is a full featured DNS resolver (which in turn, does not serve authoritative data).

NSD is an efficient and simple alternative to BIND. Having major portions of Internet infrastructure rely on one piece of software can be risky, so many large DNS providers now use both authoritative servers in production (splitting them among their infrastructure). NSD's use of BIND format zone files makes this easy, and makes it compatible with many existing tools.

Why Use ZKT?

If you are new to DNSSEC, I recommend reading this DNSSEC HOWTO, a tutorial in disguise. It covers the different methods of key rollover. Zone Key Tool, or ZKT, is used to automate the management of the various DNSSEC signing keys for your domains. It implements automatic Zone Signing Key (ZSK) rollover using the pre-publish method, meaning that the most frequent key changes are handled automatically. Key Signing Keys (KSK, which are used to sign the ZSKs) must be provided to the maintainer of the parent zone (or a DLV registry) and so their rollover cannot be handled completely automatically, but ZSK automates as much of the process as is feasible using the double signature method.

ZKT uses the standard BIND command-line tools to do the work of actually signing the zone, and stores the keys and any metadata it needs in a simple hierarchical filesystem structure. This makes it easy to use on any system, and indeed, any DNS server, as well as providing for simple maintenance and the ability to easily incorporate it into other processes.

Setting up NSD

The following procedure was used on an Ubuntu 10.10 system, though it should apply generally to any current GNU/Linux system.

Install NSD and the BIND utilities (which include the DNSSEC tools that ZKT will use). Create a directory to hold the zones, copy the sample config and start customizing it:

root@zkt1:~# apt-get install nsd3 bind9utils
root@zkt1:~# cd /etc/nsd3/
root@zkt1:/etc/nsd3# mkdir zones
root@zkt1:/etc/nsd3# cp nsd.conf.sample nsd.conf
Edit nsd.conf and make the following changes:

This directory will have a subdirectory for each zone which will contain the zone file for that zone as well as its keys:

         # The directory for zonefile: files.
zonesdir: "/etc/nsd3/zones"

If you will be performing zone transfers, you'll need to set the same TSIG key on the master and slave. Here's a quick way to generate a key:

root@zkt1:~# dd if=/dev/random bs=16 count=1 2>/dev/null | openssl base64 

Add the key to the config file, with a unique name.

name: examplekeyname
algorithm: hmac-md5
secret: "AWGPLrhHD6oLOXI7vZBToQ=="

Add more key stanzas if you have more slave servers then remove the remaining example key and zone stanzas. Finally, start the server:

root@zkt1:~# /etc/init.d/nsd3 start 

Setting Up a Slave with NSD

Install NSD and copy the sample config as before, and copy the appropriate key: section of nsd.conf from the master. Then start the server:

root@zkt1:~# /etc/init.d/nsd3 start 

Setting up ZKT

ZKT is not (yet) packaged in Debian or Ubuntu, so it will need to be downloaded, built, and installed:

root@zkt1:~# apt-get install build-essential
root@zkt1:~# wget
root@zkt1:~# tar xvfz zkt-1.0.tar.gz
root@zkt1:~# cd zkt-1.0
root@zkt1:~/zkt-1.0# ./configure --enable-configpath=/etc/nsd3
root@zkt1:~/zkt-1.0# make
root@zkt1:~/zkt-1.0# make install
root@zkt1:~/zkt-1.0# make install-man

Create a config file for ZKT. Normally the location defaults to /var/named, but since you may not have BIND installed on your system, the configure line supplied above changes the location to /etc/nsd3/dnssec.conf:

# @(#) dnssec.conf vT0.99d (c) Feb 2005 - Aug 2009 Holger Zuleger

# dnssec-zkt options
Zonedir: "/etc/nsd3/zones"
Recursive: True
PrintTime: True
PrintAge: True
LeftJustify: False

# zone specific values
ResignInterval: 1d # (604800 seconds)
Sigvalidity: 10d # (864000 seconds)
Max_TTL: 1h # (28800 seconds) maximum ttl actually in zone
Propagation: 5m # (300 seconds) expected slave propagation time
KEY_TTL: 1h # (14400 seconds)
Serialformat: incremental

# signing key parameters
Key_algo: NSEC3RSASHA1 # (Algorithm ID 7)
KSK_lifetime: 1y # (31536000 seconds)
KSK_bits: 2048
KSK_randfile: "/dev/urandom"
ZSK_lifetime: 4w # (7257600 seconds)
ZSK_bits: 1024
ZSK_randfile: "/dev/urandom"
SaltBits: 24

# dnssec-signer options
LogFile: ""
LogLevel: ERROR
SyslogFacility: NONE
SyslogLevel: NOTICE
VerboseLog: 0
Keyfile: "dnskey.db"
Zonefile: "zone.db"
DLV_Domain: ""
Sig_Pseudorand: False
Sig_GenerateDS: True
Sig_Parameter: ""

You may be interested in editing that file to tune the output format of the tools, resigning interval, and the key algorithm.

To automate the signing of zones with ZKT and updating NSD, create the following script at /usr/local/sbin/update-dns and make it executable:


echo "Current DNSSEC signing keys:"
/usr/local/bin/zkt-ls -f

echo "Resigning zones:"
/usr/local/bin/zkt-signer -v || exit

echo "Update NSD:"
/usr/sbin/nsdc rebuild && /usr/sbin/nsdc reload && /usr/sbin/nsdc notify

echo "Done."

The initial installation and configuration is complete, the next sections detail how to add a new zone with instructions that can be repeated each time.

Adding a New Zone

Add the following to nsd.conf on the master:

notify: SLAVEIP examplekeyname
provide-xfr: SLAVEIP examplekeyname
notify-retry: 5

Or zone.db rather than zone.db.signed if you are not planning on signing this zone. Add this to any slaves:

allow-notify: MASTERIP examplekeyname
request-xfr: AXFR MASTERIP examplekeyname

Where SLAVEIP is the IP address of your slave server, and MASTERIP that of the master. On the master, create a directory to hold the zone file and keys. This directory must have the same name as the zone (ie, the $ORIGIN line):

root@zkt1:~# mkdir /etc/nsd3/zones/ 

I recommend starting with the following stub zone file for each new zone you create. Copy this to /etc/nsd3/zones/

; -*- mode: zone -*-                                                            
$TTL 1h
@ IN SOA (
1296347822 ; serial number unixtime
1h ; refresh (secondary checks for updates)
10m ; retry (secondary retries failed axfr)
10d ; expire (secondary ends serving old data)
1h ) ; min ttl (cache time for failed lookups)
$INCLUDE dnskey.db

If you edit that file within Emacs, the serial number will automatically be updated. Note that the dnskey.db file which is to be included has not been created yet. If you are not going to sign the zone, remove this line:

$INCLUDE dnskey.db 

Run nsdc restart on the master and any slaves after editing the configuration file. If you are not going to sign the zone, run:

root@zkt1:~# update-dns 

to recompile the zone database and send notifications to the slaves. It will also re-sign any zones that need it. If you are going to sign the zone, continue to the next section.

Signing a Zone for the First Time

This procedure is only required to initially sign a zone; subsequent updates are handled automatically. ZKT's global configuration is stored in /etc/nsd3/dnssec.conf. Local, per-zone settings may be added to a dnssec.conf in the zone directory if needed. To sign a zone, simply create an empty signed zone file, and then run the signing program:

root@zkt1:~# touch /etc/nsd3/zones/
root@zkt1:~# zkt-signer -v -v

This will sign any zones that need updating, and also create the keys for your new zone and sign it. If everything went well, run:

root@zkt1:~# update-dns 

which will perform another pass over the zones (extraneous but harmless), then update NSD on the master and slaves.

Adding the Zone to DLV

Since the root is not yet signed, you probably want to use DNSSEC Lookaside Validation (DLV). ISC, the maintainers of BIND, provide an easy to use and well regarded DLV registry at Create an account there and log into it. Click Manage Zones, and then (add a zone). Enter the name, and then it will prompt you to add a record or upload a file. Look in your zone directory for the dsset file:

root@zkt1:~# /etc/nsd3/zones/ 

Copy the last line out of that file. It should look like:  IN DS 38448 7 2 EFF15AE6AA31A1D552DDE68FE874253959A2DA8B28DE39D42FE025B5 541B5CBE 

Paste the entire line into the Add Record prompt on the DLV website. You will then be prompted to add a TXT record to the domain. Follow the instructions in this document for modifying a zone to add the TXT record. Very shortly afterwords (about 5 minutes), the DLV website will have checked for the TXT record. If everything worked, you can then remove the record, and you're done.

Updating a Zone

Edit the zone file at /etc/nsd3/zones/ Don't edit anything else in the zone directory, it's all managed by ZKT. When finished, run

root@zkt1:~# update-dns 

To re-sign any zones that need it, recompile the NSD database, reload it, and notify the slaves.

ZSK rollover

Zone Signing Keys are configured by default to roll over using the pre-publish procedure every 30 days. To make sure this happens, set up the following cron job in /etc/cron.d/dnssec:

# Re-sign dnssec domains
17 3 * * * root /usr/local/sbin/update-dns

KSK rollover

Key Signing Keys, which are the DNSSEC Secure Entry Point (SEP) for the zone and used to sign the ZSKs, are configured for a one year lifetime. They are NOT rolled over automatically, since updating the upstream DS (or DLV) records is not automatic. Once a year, when they near expiration, use this procedure:

  1. Generate a new key and allow it to propogate through DNS:
    root@zkt1:~# zkt-keyman -1
    root@zkt1:~# update-dns
  2. After a while (zkt will calculate it for you), publish the new DS (or DLV) record:

    root@zkt1:~# zkt-keyman -2
    root@zkt1:~# update-dns

    Follow the instructions above for sending the DS record to the DLV registry.

  3. After that has propagated, you can remove the old key:

    root@zkt1:~# zkt-keyman -3
    root@zkt1:~# update-dns

At any time during the process, you can see what ZKT thinks the status is by running:

root@zkt1:~# zkt-keyman --ksk-status 



James E. Blair

I love hacking Free Software and have been fortunate to do so professionally with some wonderful people and organizations throughout my career. This is my blog.