Split DNS done right: 2 servers; NSD and BIND9

February 23, 2008


Open recursors are a problem. They can be used by attackers to amplify a packet stream to a victim. As such you should only be running a recursor on your internal network and an authoritative server on your interfaces facing the big, bad Internet.

You can run BIND9 with a split view (also called ‘split brain’), or use my setup: a NSD serving the outside world, and using BIND9 as recursor for your internal network.


I’m the owner of multiple domains, among which miek.nl is just one. For the purpose of this article I will pretend this is the only domain I have. The goal here is to have this domain available both inside my network and on the outside. And to have recursion on the inside, but not on the outside.

Internet side

The config for miek.nl looks like this. This does assume how to read these kind of files. The zone file is relatively short and nothing fancy happens.

For completeness, I’m using puppet for my /etc/ configuration – you should too! :)

; NSD data file for miek.nl for public use
; ## configured with puppet ##
$TTL    1D
$ORIGIN miek.nl.
@       IN      SOA     open.nlnetlabs.nl. miekg.atoom.net. (
                   2007032000     ; Serial
                     4H         ; Refresh
                     1H         ; Retry
                     7D         ; Expire
                     1D )       ; Negative Cache TTL
            IN      NS      open.nlnetlabs.nl.
            IN      NS      omval.tednet.nl.
            IN      NS      elektron.atoom.net.

            IN      MX      20 mail.atoom.net.
            IN      MX      30 sol.nlnetlabs.nl.

miek.nl.    IN  A
localhost   IN  A
a           IN  A
www         IN  CNAME   a

This zonefile contains real IP addresses, which can be resolved on the Internet.

Internal side For internal use I’m using a slightly different zone (the joy of NAT), which looks like this:

; BIND data file for miek.nl for internal use
$TTL    1H
@       IN      SOA     elektron.atoom.net. miekg.atoom.net. (
               2005060700         ; Serial
                     6H         ; Refresh
                     2H         ; Retry
                     7D         ; Expire
                     1H )       ; Negative Cache TTL

@      IN      NS      elektron.atoom.net.
@      IN      MX      10 elektron.atoom.net.
@      IN      A

localhost   IN      A
a           IN      A
www         IN      CNAME   a

And you can see that only RFC1918 addresses are used here.


So how do you actually perform the setup so that you have two servers running? First install the software packages you’ll need (This is on Ubuntu/Debian):

Install nsd:

apt-get install nsd

Install bind9

apt-get install bind9


BIND Well, BIND is pretty well known and you can find loads of howtos on how to setup this daemon. I’m only interested in making is listening on the *intra*net interfaces, in my case (i’m only showing the relevant parts of the named.conf configuration file):

// This is the primary configuration file for the BIND DNS server named.
// ## configured with puppet ##

options {
      directory "/var/cache/bind";
      recursion yes;
      listen-on {;; ::1;; };

Note the recursion yes, without this I wouldn’t be able to resolve names on my internal network.

My /etc/resolv.conf ofcourse lists my own server as nameserver:

# ## configured with puppet ##
search atoom.net. dmz.atoom.net.

So this takes care of my internal needs.

NSD For NSD (version 2) I need to configure which zones are to be loaded. This is done in the nsd.zones file:

; ## configured with puppet ##
; forward
zone miek.nl                  zones/db.miek.nl        notify

Next I need to tell NSD that it should listen on my external network interfaces. On debian/Ubuntu this is handled in the file /etc/default/nsd. On my system it looks like this (only the relevant parts are shown):

# Flags to pass to nsd on startup
flags="-u nsd -a -a 2001:7b8:2ff:c8::2"

This tells NSD to listen on my external IPv4 and IPv6 addresses. Setting up and requesting a domain name goes beyond this article.

Restart the services

Basicly that was it… now BIND9 is only used on my internal network, and NSD is used for the outside. Just the way I like it; just the way it should be.

Just restart the services and you should be done:

/etc/init.d/nsd restart && /etc/init.d/bind9 restart

Check /var/log/messages/ and /var/log/daemon.log for any errors you might get.