NSD

From SixXS Wiki
Jump to: navigation, search

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.

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:

  1. SERIAL field is on separate line with comment '; serial'
  2. 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.
  3. 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.