NSD and OpenDNSSEC under FreeBSD 10 [Part 2: NSD]

This is the second part in the series of articles explaining how to run NSD and OpenDNSSEC under FreeBSD 10.

Today, we’re going to deploy NS-FEED, NS-3 and NS-4 dealing with plain zones only. We’re not going to sign anything yet — we’ll deploy NS-FEED as a hidden master, configure it to feed NS-3 and NS-4 and configure NS-3/NS-4 to be masters for the ISP slave. This is probably the easiest part since configuration of NSD is quite easy.

For the sake of simplicity, let’s assume we’re going to host a zone called plain.org.

Let’s start with the installation of required software. Repeat the procedure below on all three servers:

% cd /usr/ports/dns/nsd && make install clean

You’ll be prompted to configure additional options to compile NSD with: since I’m not planning to use IPv6 I uncheck it and check BIND8_STATS and CHECKING. BIND8_STATS is useful for statistical purposes (nsd-control stat).

% cd /usr/ports/dns/bind-tools && make install clean

Installing bind-tools has nothing to do with NSD and could be omitted. I just like having dig present in the system for troubleshooting reasons. bind-tools will also install dnssec-keygen which will be later used to generate TSIG keys, however they could be generated using various methods.

Let’s create directories and fix permissions:

% mkdir -p /usr/local/etc/nsd/var/db/nsd
% mkdir -p /usr/local/etc/nsd/var/run/nsd
% mkdir /usr/local/etc/nsd/var/log
% mkdir /usr/local/etc/nsd/zones

% chown -R bind:bind /usr/local/etc/nsd/var

[20140909] : starting from version 4.1.0 NSD port in FreeBSD will create nsd user, hence bind is no longer needed:

% chown -R nsd:nsd /usr/local/etc/nsd/var

Modify /etc/rc.conf to start NSD at boot:

# enable nsd
nsd_enable="YES"

The following steps should be followed from our hidden master NS-FEED:

We’re going to generate a TSIG key which will be used for secure zone transfers between NS-FEED and NS-3/4. TSIG or Transactional Signature is a mechanism to secure DNS messages (for example NOTIFY) and to enable secure server communication (for example zone transfer between master and slave):

% cd /usr/local/etc/nsd

% dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST ns-feed-plain

I used ns-feed-plain as a hostname to separate the TSIG that will be used for plain zones from the TSIG that will be used for signed zones. The output of dnssec-keygen will be two files:

Kns-feed-plain.+163+26684.key
Kns-feed-plain.+163+26684.private

Take a note of the content of Kns-feed-plain.+163+26684.key file: we’re going to use it in nsd.conf:

% cat Kns-feed-plain.+163+26684.key

ns-feed-plain. IN KEY 512 3 163 IseujRBzonAlWpVBLyyvn6ErSTp/CGui6+RIeXXLxb4=

Here is the alternative way to generate TSIG (it does require base64 installed though):

% cd /usr/ports/converters/base64 && make install clean
% rehash

% dd if=/dev/random of=/dev/stdout count=1 bs=32 | base64

And one more using ldns-keygen (it does require ldns installed):

% cd /usr/ports/dns/ldns && make install clean
% rehash

% ldns-keygen -a hmac-sha256 -b 256 ns-feed-plain

Create nsd.conf in /usr/local/etc/nsd directory (use nsd.conf.sample as a reference). Here is nsd.conf of our hidden master:

server:
        ip-address: 10.9.128.1
        do-ip4: yes
        do-ip6: no
        verbosity: 2
        chroot: "/usr/local/etc/nsd"
        zonesdir: "/usr/local/etc/nsd"
        zonelistfile: "zone.list"
        database: "var/db/nsd/nsd.db"
        logfile: "var/log/nsd.log"
        pidfile: "var/run/nsd.pid"
        xfrdfile: "var/db/nsd/xfrd.state"
        xfrdir: "var/db/nsd/"
        hide-version: yes

remote-control:
        control-enable: yes
        control-interface: 127.0.0.1
        control-port: 8952
        server-key-file: "/usr/local/etc/nsd/nsd_server.key"
        server-cert-file: "/usr/local/etc/nsd/nsd_server.pem"
        control-key-file: "/usr/local/etc/nsd/nsd_control.key"
        control-cert-file: "/usr/local/etc/nsd/nsd_control.pem"

key:
        name: "tsig.sha256.plain"
        algorithm: hmac-sha256
        secret: "IseujRBzonAlWpVBLyyvn6ErSTp/CGui6+RIeXXLxb4="

pattern:
        name: "plain-to-slaves"
        zonefile: "zones/%s"
        notify: 192.168.128.1 tsig.sha256.plain
        notify: 192.168.128.2 tsig.sha256.plain
        provide-xfr: 192.168.128.1-192.168.128.2 tsig.sha256.plain

zone:
        name: "plain.org"
        include-pattern: "plain-to-slaves"

Several points of interest and extra care:

chroot: instructs NSD to drop privileges to a non-privileged user (in the case of FreeBSD it’s going to be bindnsd) after it binds to port 53. Note that this is also going to be our BASE directory, so all path definitions after chroot: should be relative to /usr/local/etc/nsd. You can still use full paths, just make sure all directories are inside /usr/local/etc/nsd.

key: take a note of the name. Once we start configuring our slaves the name of the key should be identical to the one configured on the master. There might be multiple keys defined in nsd.conf.

pattern: allows you to bundle a set of zone config statements. For example, notify: and provide-xfr: statements are normally defined in the zone: section. If you host multiple zones that share the same slaves you can bundle it together in the pattern and include in the zone: section. Next time you add a new zone, you just define the name of the zone and append with include-pattern:.

zonefile: “zones/%s” instructs NSD to look for the zone name (%s) in /usr/local/etc/nsd/zones directory where %s is a name: configured in zone: section. In our example, NSD will look for /usr/local/etc/nsd/zones/plain.org file. Here you can find more detailed explanation of pattern use.

Let’s continue with the configuration of NS-FEED. Create the private and public keys for nsd-control. nsd-control is a utility to manage the NSD daemon.

% cd /usr/local/etc/nsd

% nsd-control-setup

This will result in creating of four files that are already defined in nsd.conf under remote-control: section.

We’re almost done with our hidden master. Before we start the daemon we need to prepare the zone file for our domain. Create a file called plain.org under /usr/local/etc/nsd/zones directory:


$ORIGIN plain.org.
$TTL 1h
@       IN      SOA     plain.org.      hostmaster.plain.org. (
                        2014082701              ; serial number
                        3600                    ; refresh
                        900                     ; retry
                        1209600                 ; expire
                        1800                    ; ttl
                        )
; Name servers
                    IN      NS      ns1.plain.org.
                    IN      NS      ns2.plain.org.

; A records for name servers
ns1                 IN      A       192.168.128.1
ns2                 IN      A       192.168.128.2

; Additional A records
@                   IN      A       192.168.128.20
www                 IN      A       192.168.128.20

That’s it! We’re ready to fire up NSD:

% /usr/local/etc/rc.d/nsd start

Verify that’s it’s up and running and you can query it:

% sockstat -4 | grep 53

bind     nsd        46557 4  udp4   10.9.128.1:53      *:*
bind     nsd        46557 5  tcp4   10.9.128.1:53      *:*

% dig @10.9.128.1 plain.org soa

; <<>> DiG 9.10.0-P2 <<>> @10.9.128.1 plain.org soa
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 24606
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;plain.org.                      IN      SOA

;; ANSWER SECTION:
plain.org.               3600    IN      SOA     plain.org. hostmaster.plain.org. 2014082701 3600 900 1209600 1800

;; AUTHORITY SECTION:
plain.org.               3600    IN      NS      ns1.plain.org.
plain.org.               3600    IN      NS      ns2.plain.org.

;; ADDITIONAL SECTION:
ns1.plain.org.           3600    IN      A       192.168.128.1
ns2.plain.org.           3600    IN      A       192.168.128.2

;; Query time: 1 msec
;; SERVER: 10.9.128.1#53(10.9.128.1)
;; WHEN: Wed Aug 27 15:59:41 CEST 2014
;; MSG SIZE  rcvd: 164

Finally, check the NSD log file (/usr/local/etc/nsd/var/log/nsd.log):

[1409155342] nsd[46556]: info: setup SSL certificates
[1409155350] nsd[46558]: notice: nsd started (NSD 4.0.3), pid 46557
[1409155410] nsd[46557]: error: xfrd: zone plain.org: max notify send count reached, 192.168.128.1 unreachable
[1409155425] nsd[46557]: error: xfrd: zone plain.org: max notify send count reached, 192.168.128.2 unreachable

We're done with the hidden master! You see those errors -- that's because our slaves are not yet configured. Also, NSD writes logs with Unix timestamps. You can use the following to display it in a human-readable format:

% less /usr/local/etc/nsd/var/log/nsd.log | perl -p -e 's/^\[(.*)\]/"[". localtime($1) . "]"/e'

[20140909] : starting from version 4.1.0 NSD will log in a readable timestamp by default.

Let's configure our slaves now: NS-3 and NS-4. I'm going to do it for NS-3 only -- just duplicate the same changes on the secondary slave (NS-4).

There is no need to generate TSIG on the slave, so we go straight to nsd.conf. Here is the content of nsd.conf from NS-3:

server:
        ip-address: 192.168.128.1
        do-ip4: yes
        do-ip6: no
        verbosity: 2
        chroot: "/usr/local/etc/nsd"
        zonesdir: "/usr/local/etc/nsd"
        zonelistfile: "zone.list"
        database: "var/db/nsd/nsd.db"
        logfile: "var/log/nsd.log"
        pidfile: "var/run/nsd.pid"
        xfrdfile: "var/db/nsd/xfrd.state"
        xfrdir: "var/db/nsd/"
        hide-version: yes

remote-control:
        control-enable: yes
        control-interface: 127.0.0.1
        control-port: 8952
        server-key-file: "/usr/local/etc/nsd/nsd_server.key"
        server-cert-file: "/usr/local/etc/nsd/nsd_server.pem"
        control-key-file: "/usr/local/etc/nsd/nsd_control.key"
        control-cert-file: "/usr/local/etc/nsd/nsd_control.pem"

key:
        name: "tsig.sha256.plain"
        algorithm: hmac-sha256
        secret: "IseujRBzonAlWpVBLyyvn6ErSTp/CGui6+RIeXXLxb4="

pattern:
        name: "from-master"
        zonefile: "zones/%s"
        request-xfr: AXFR 10.9.128.1 tsig.sha256.plain
        provide-xfr: 172.16.128.1 tsig.sha256.plain
        allow-notify: 10.9.128.1 tsig.sha256.plain
        notify: 172.16.128.1 tsig.sha256.plain

zone:
        name: "plain.org"
        include-pattern: "from-master"

The important change in the slaves' nsd.conf is request-xfr: and allow-notify: -- those directives instruct NSD to become a slave for plain.org zone. provide-xfr: and notify: directives, on the other hand, makes NS-3 a master for 172.16.128.1, which is our ISP DNS. You will have to provide your ISP with the name of TSIG key (tsig.sha256.plain), algorithm and secret, in order to allow successful zone transfer.

Note that zonefile: directive is irrelevant for the slave. This file doesn't exist and never will therefore you will get an info message in the logs. Once the zone is transferred from the master it's compiled into a binary database file (/usr/local/etc/nsd/var/db/nsd/nsd.db) and that makes NSD pretty fast to initialize.

Execute nsd-control-setup on the slave and start the daemon. You should see a successful zone transfer logged:

 nsd[25856]: info: setup SSL certificates
 nsd[25858]: info: zonefile plain.org does not exist
 nsd[25858]: notice: nsd started (NSD 4.0.3), pid 25857
 nsd[25857]: info: xfrd: zone plain.org committed "received update to serial 2014082701 at 2014-08-27T17:36:09 from 10.9.128.1 TSIG verified with key tsig.sha256.plain"
 nsd[25860]: info: zone plain.org. received update to serial 2014082701 at 2014-08-27T17:36:09 from 10.9.128.1 TSIG verified with key tsig.sha256.plain of 2496 bytes in 0.000179 seconds
 nsd[25857]: info: Zone plain.org serial 0 is updated to 2014082701.

To double-check, go to NS-FEED, increase the serial of plain.org and reload the zone:

% nsd-control reload plain.org

Your zone should be transferred immediately. Use 'dig @192.168.128.1 plain.org soa' to prove that.

That's it for today. In the next article we're going to introduce NS-SIGN and start signing zones with OpenDNSSEC.

Tags: , , , , , , ,

Leave a Reply