NSD
NSD is an open-source authoritative-only non-recursive Name Server Daemon. In its master-only operations it resembles tinydns server of djbdns suite. It was developed by NLnet Labs of Amsterdam in cooperation with the RIPE NCC. In 2004 NSD was in top10 most used DNS servers [1].
One of the important merits of the server is ability to work with IPv6 out of the box. It supports both, IPv6 AAAA resource records and IPv6 network stack to bind to AF_INET6 sockets. Another feature which one could find useful is ability to read BIND zone format. Alike tinydns, NSD compiles zone data into efficiently searchable DB format.
Contents
Usage Example
Lets look on the case with nsd3 on Ubuntu Natty Narval.
Installation
NSD package is available in universe repository of Ubuntu, thus could be installed by executing
~# aptitude install nsd3
Configuration
Once installed, you can review sample config file at /etc/nsd3/nsd.conf.sample to get familiar with various directives and file format.
Server configuration
Default entries are suitable for the most of the people, thus most of the entries could be omitted. I have dnscache running on local address, thus need to bind ipv4 socket to some other port:
server: ip-address: 10.0.0.252@5300 ip-address: 2001:db8:1:1::1
as such I can redirect external queries to this port with firewall (iptables) to provide XFRs on "legacy" IPv4. After I bought USB adapter and connected my box directly to the internet I am able to bind it to this external interface on default port (53) leaving internal interface solely for recursive cache.
server: ip-address: 1.1.1.1 ip-address: 2001:db8:1:1::1
Next, register our primary zones, forward and reverse:
zone: name: "my.name" zonefile: "my.name.zone" notify: 2001:db8:1:1::2 NOKEY provide-xfr: 2001:db8:1:1::2 NOKEY zone: name: "2.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa" zonefile: "2001.db8.1.2.rev.zone" notify: 2001:db8:1:1::2 NOKEY provide-xfr: 2001:db8:1:1::2 NOKEY
here we have prefix 2001:db8:1:2::/64 f.i., hence its reverse form will be reversed, null-expanded and dot-separated notation in ip6.arpa domain.
Zone configuration
Next, create zone file - BIND format is well known and no details should be provided I presume:
$TTL 3600 ; SOA Record MY.NAME. 3600 IN SOA ns me ( 1 28800 7200 604800 86400 ) ; A+ Records IN AAAA 2001:db8:1:1::2 500 IN A 1.2.3.4 ; NS Records 3600 IN NS ns 3600 IN NS he ; MX Records IN MX 0 mx IN MX 10 relay.myext.hosting.net. ; SRV Records _sip._udp IN SRV 10 10 5060 me.dyndns.org. _sip._udp IN SRV 20 10 5060 mx ; Host Records me IN AAAA 2001:db8:1:2::1 ns IN AAAA 2001:db8:1:1::2 mx IN AAAA 2001:db8:1:1::2 ; CNAME Records www 3600 IN CNAME @
and reverse
$TTL 3600 @ IN SOA ns.my.name me.my.name. ( 1 ; serial 24h ; refresh 30m ; retry 2d ; expire 7d ; ttl ) IN NS ns.my.name. IN NS he.my.name. ; tu endpoints $ORIGIN 0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ipv6.arpa. 1 IN PTR he-peer.my.name. 2 IN PTR he.my.name. ; he prefix $ORIGIN 2.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
Verification
Now let start the server - startup script will compile zones on start
/etc/init.d/nsd3 start
and check resolver, forward
~# dig my.name @2001:db8:1:1::2 soa ; <<>> DiG 9.7.3 <<>> my.name @2001:db8:1:1::2 soa ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 7948 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 2 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;my.name. IN SOA ;; ANSWER SECTION: my.name. 3600 IN SOA ns.my.name. me.my.name. 4011061804 28800 7200 604800 86400 ;; AUTHORITY SECTION: my.name. 3600 IN NS ns.my.name. my.name. 3600 IN NS he.my.name. ;; ADDITIONAL SECTION: ns.my.name. 3600 IN AAAA 2001:db8:1:1::2 me.my.name. 3600 IN AAAA 2001:db8:1:2::1 ;; Query time: 11 msec ;; SERVER: 2001:db8:1:1::2#53(2001:db8:1:1::2) ;; WHEN: Mon Jun 20 01:09:35 2011 ;; MSG SIZE rcvd: 168
and reverse
~# dig -x 2001:db8:1:2::1 @2001:db8:1:1::2
; <<>> DiG 9.7.3 <<>> -x 2001:db8:1:2::1 @2001:db8:1:1::2 ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61950 ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 2, ADDITIONAL: 0 ;; WARNING: recursion requested but not available ;; QUESTION SECTION: ;1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.2.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. IN PTR ;; ANSWER SECTION: 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. 3600 IN PTR he.my.name. ;; AUTHORITY SECTION: 2.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. 3600 IN NS ns.my.name. 2.0.0.0.1.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. 3600 IN NS he.my.name. ;; Query time: 4 msec ;; SERVER: 2001:db8:1:1::2#53(2001:db8:1:1::2) ;; WHEN: Mon Jun 20 01:57:15 2011 ;; MSG SIZE rcvd: 222
Once configuration is tested, we can open firewall for secondaries, add them to nsd.conf file (zone section) and as NS to zone file. Finally recompile zone
~# nsdc rebuild
and reload configuration
~# nsdc reload
Dynamic external IP
Having direct connection to ISP reveals another problem - ISP assigns IP dynamically, and frankly I don't want to pay additional monthly fee for static IP. I'll do it my way. This script below checks assigned to external interface IP address and fixes all required (DNS) configuration. Script is executed from rc.local file on system boot, and by cron periodically.
#!/bin/bash EXTIF="eth2" CURRIP=$(ip -4 ad li dev $EXTIF | awk '/inet /{print$2}' | sed 's/\/[0-9]\+//') LASTIP=$(awk '/300\tIN\tA/{print$4}' /etc/nsd3/example.com.zone) if [ "$LASTIP" != "$CURRIP" ] then echo "Fixing our dynamic IP..." SERIAL=$( awk '/; serial/{print$1}' /etc/nsd3/example.com.zone ) NEWSER=$(( $SERIAL + 1 )) sed -i "s/$LASTIP/$CURRIP/g" /etc/nsd3/nsd.conf sed -i "/; serial/s/$SERIAL/$NEWSER/;s/$LASTIP/$CURRIP/g" /etc/nsd3/example.com.zone nsdc rebuild && nsdc restart fi
Script swaps old ip with new one in DNS zone configuration and in nsd.conf - so that daemon will be able to bind to specified address. The script takes old IP from DNS zone. expecting it to be formatted following way:
- SERIAL field is on separate line with comment '; serial'
- Our v4 IP has TTL 300 - and it is the only RR with such TTL in zone file - you can use any other unique TTL to identify IP.
- Fields in this RR (with our old IP) are separated by tabs - though you can modify script to use spaces if you find it more convenient.
Zone can have other occurrences of the IP with any TTL - all of them just will be replaced by sed with new one.