NSD: How to Implement Master and Slave DNS Servers

NSD is a name server implementation developed and maintained by NLnet Labs in cooperation with RIPE. NSD is an authoritative-only DNS implementation, and is memory efficient, secure and fairly straightforward.

NSD can start up quickly, as all zone information is compiled into an efficient binary format prior the the NSD daemon reading it. A few of the root nameservers use NSD, as well as the .se ccTLD. NSD has a proven history of being robust and can meet the demands of the highest-traffic DNS requirements known.

You can read more about NSD over at the project page (http://www.nlnetlabs.nl/projects/nsd/) so I won’t dwell on the preamble. My NSD implementation will run a master instance on server gooby and a slave instance on server dolan. There were a few pitfalls along the way, as this was my first NSD implementation. Unfortunately, there don’t seem to be many NSD resources available on the internet, so I’ll include those pitfalls in this article and hopefully save you some of the time/pain/strace/bash -x that I had to indulge myself in. Both gooby and dolan are running a minimal package install of CentOS 6.3 x86_64.

Building and Installing NSD

The following steps were performed on both gooby and dolan. First, install the appropriate packages required to build NSD via yum:

Build and install NSD to /usr/local/nsd-<version>, and create a symlink from /usr/local/nsd. This presumes you’ve downloaded the source tarball to /usr/local/src:

That’s all there is to it - now we must configure NSD.

Generating our TSIG Key

We will use TSIG to secure and encrypt our zone transfers. To generate a key suitable to use for TSIG, install the bind package via yum in order to provide the dnssec-keygen utility. I also install the bind-utils package to get access to useful utilities such as dig. I only did this on gooby but there would be no harm in installing the packages on both servers:

# yum install bind-utils bind

Check that dnssec-keygen is now installed:

Use dnssec-keygen to generate a HOST key, 160 bits long, using the HMAC-SHA1 algorithm. Make sure you do this in a secure directory as the key file will be created in the present working directory by default:

Note the key name that has been generated. Append .key to the end of this key name, and you have the filename of the key file generated. cat this file to see its contents:

So - we will be using 0915iGWHa1BQ12kkxD57/7fqcJ0= as our secret when configuring secure zone transfers via TSIG. As long as both master and slave have the same key defined, and are using the same key for each zone transfer (i.e. master can encrypt using the key, slave can decrypt), all will work as expected.

Security: Creating chroot Jails and Users

As a security measure, we will configure our servers to drop privileges down to a non-privileged user after binding to port 53. As we are using a default installation of CentOS 6.3, disable iptables and set SELinux to permissive mode. For Production use, iptables should be configured to permit appropriate traffic rather than being disabled, with the appropriate SELinux policies applied. Perform all steps in this section on both nodes:

Add a user and group for NSD.

Create the chroot directories:

And set appropriate ownership:

As we’re using CentOS 6.3, rsyslog must be configured to create a log socket at <NSD_BASE>/dev/log. To do this, create the following file:

And restart rsyslog:

An ls will show that the socket has been created:

NSD will now be able to generate syslog messages from within the chroot.

Starting NSD on the Master Server

Prior to configuring our Slave Server or loading any zones into the master, let’s first start NSD on the master and ensure it starts correctly. Create a basic nsd.conf as follows, under<NSD_BASE>/etc/nsd. In our case, we’re only doing this on gooby for now:

Ensure that the files/directories specified in the zonesdir, difffile, xfrdfile, pidfile and database directives are accessible within the chroot (NSD is smart enough to remove the chroot path from these directives).

Next, start NSD using nsdc:

You should see a message such as the following in /var/log/messages:

If NSD has started correctly, proceed.

Creating and Loading a Zone

We will use example.com as per usual. First, create a zonefile at the appropriate location - in our case /var/chroot/nsd/zonefiles. If you have a large number of zonefiles, it is worth creating a directory tree under the zonefiles directory to aid in administration. Therefore, I’ll place my zonefiles in /var/chroot/nsd/zonefiles/<first-letter-of-zone-name>/db.<zone-name>.

Next, add the appropriate zone entry to /usr/local/nsd/etc/nsd/nsd.conf:

A few things to note here. We configure the zonefile directive relative to the zonesdir directive declared in the server section of nsd.conf. We also lock down notifies to be sent to dolan only (once we’ve configured it), and to be encrypted with our TSIG signature tsig-testtsig via the notify directive. Configure provide-xfr accordingly, to only allow dolan to AXFR from gooby. Also, as we’ve bound NSD to a specific interface on our host (as specified via the ip-address directive in the server section), outgoing-interface should be set to ensure notifies are sent out of the right interface.

Once your configuration and zonefile are in place, run nsdc rebuild to regenerate nsd.db - the binary configuration database. You can rebuild individual zonefiles, if required, with zonec - NSD calls this during a rebuild - observe:

Next, restart NSD.

You should now be able to dig the SOA record for example.com, and receive a result:

If a zone has been added or removed from NSD, you need to run:

If you modify a zone, you need to run:

Configuring the Slave Server

We can now configure NSD on our slave server dolan as follows:

There are many things to note here. We bind to ALL interfaces on the server, using the ip-address: 0.0.0.0 directive. This is required, as an nsdc update (used to update all zones on the server) works by sending a notify to localhost. Therefore, we need to ensure that NSD is listening on localhost, and that an additional allow-notify statement is included to allow this with NOKEY (i.e. no TSIG key). The rest of the configuration is as per gooby, noting that we allow-notify from gooby only, and we only request-xfr from the master server - in this case also gooby.

Create the appropriate zonefile directory tree on the slave too. Using nsdc patch to write out the zonefiles from the journal will obviously fail if it can’t write to the appropriate directories:

Start NSD on the slave:

NSD should start, and transfer the zone from the master. This can be verified by taking a look at /var/log/messages:

Excellent. Make a change to the zone on the master, and update the serial:

On the slave, you should see the notify received, and the zone updated in /var/log/messages:

Excellent. The TSIG-verified zone transfer has worked.

You can run

from the master to manually send notifies to the slave servers. You can also run

on the slave to manually initiate zone transfers.

Once the transfers have been committed, you can run nsdc patch to write them out to disk, for example:

The zonefiles have been written to disk as expected. This would have failed had we not created the appropriate directory structure.

Issues

During the course of my initial investigations into NSD, a few errors cropped up.

If you see a message like this on the slave …

… and a message like this on the master

… this indicates that time is out of sync. Set correct time on both servers (here we can see that our servers were over 2 days apart), and the zone transfer will work as expected.

The following message:

and this whilst debugging with nsdc notify

was caused by iptables running. Either disable iptables, or modify the ruleset accordingly, depending upon the Production status of the server.

The following update failure:

was caused by exactly what it said - I needed to allow-notify from localhost. I also had to ensure NSD was listening to localhost. This is not really what I wanted (I wanted to only bind to eth0), but from much time spent with bash -x, it seems to be all that NSD will allow (unless we hack the way the notifies are sent). Ensure your slaves all listen on localhost.

If you attempt to start NSD and receive permission denied errors when trying to bind to port 53, it is likely being caused by SELinux. Either define an appropriate SELinux ruleset, or set it to disabled or permissive, again depending upon the security requirements of the server.

Conclusion

This has been a very brief introduction and has really only just scraped the surface of what is a very mature and robust piece of technology. NSD is great for sites that require reliability, scalability and ease of administration - this is why various root nameservers and the .SE NS run it.

I’ll definitely be delving deeper into NSD over the coming months, and will be writing more articles around this and other DNS technologies.