From Bind9 to NSD, a (relatively short) DNS journey

Posted by Wxcafé on Sun 24 September 2017

Hey! It’s been a while…

I’m sorry, I’ve had a lot on my plate in august and at the beginning of september.

Anyway. Recently (meaning, last week) I started having a bit of time on my hands again, and as I had been planning for a few months I set to migrate my DNS servers from Bind9 to NSD. There are a few reasons for that, mainly related to how Bind has found itself in the subject line of several CVEs in the last few months and that I wasn’t using any of the thousand of features that Bind offers. Okay, I’ll admit that the “hype” around NSD/Unbound, if you can call it that, played a bit of a role in the decision.

Anyways, my setup is pretty simple: I have two servers. Both are primary for some zones and replicas for other zones, and one of them is behind a v4 NAT (it’s on my home network) and has to do recursive resolution in addition to being an authoritative server. Both are IPv6-accessible.

So, the previous setup was a bind9 server on both, with a port-forwaring rule for IPv4 for the server behind the NAT, and allow-transfer {} blocks instead of keys to transfer the zones from the primary to the replica. It was pretty ugly, but it worked fine and didn’t have any glaring security problems (at least, not that I could think of).

I set on moving the first part of the setup, which is the distant server (the one that’s not behind a NAT). The setup went like this:

$ sudo apt install nsd
$ sudo mkdir -p /etc/nsd/zones/{primary,replica}
$ sudo cp /etc/bind9/zones/primary/* /etc/nsd/zones/primary/
$ sudo vim /etc/nsd/nsd.conf

And here is the nsd.conf file:

# NSD configuration file

server:
    do-ip4: yes
    ip4-only: no
    hide-version: no
    zonesdir: "/etc/nsd/zones/"
    logfile: "/var/log/nsd.log"
    pidfile: "/run/nsd/nsd.pid"

key:
    name: "push-key"
    algorithm: hmac-sha256
    secret: "redacted"

pattern:
    name: "copy-to"
    notify: <other server's IP> push-key
    provide-xfr: <other server's IP> push-key

pattern:
    name: "copy-from"
    allow-notify: <other server's IP> push-key
    request-xfr: AXFR <other server's IP> push-key



# Primary Zones

zone:
    name: wxcafe.net
    zonefile: primary/wxcafe.net
    include-pattern: "copy-to"

# [other zones ...]


# Replica zones

zone:
    name: home.wxcafe.net
    zonefile: replica/home.wxcafe.net
    include-pattern: "copy-from"

# [other zones ...]

That should be pretty straightforward, but basically the first block defines basic server options, the second one defines the push key and its settings, the third and fourth define “patterns” that are repeatable configuration for zones, and the rest are zone definitions.

Aaaaaand, that’s it! Nothing more to do, everything works. Just

$ sudo systemctl stop bind9 && sudo systemctl disable bind9
$ sudo systemctl start nsd && sudo systemctl enable nsd

and you’re done. Now, of course, since the other server isn’t configured, the replication doesn’t actually happen yet. So let’s configure the other server then!

Well, we want it to do recursive resolving, so, since NSD doesn’t do that, we’re gonna need to run Unbound too. So, to start off of course we need to install Unbound so we’re doing an apt install unbound. Then we start with the unbound configuration:

# Unbound configuration file for Debian.
include: "/etc/unbound/unbound.conf.d/*.conf"
server:
    logfile: ""
    interface: 0.0.0.0
    access-control: 10.0.0.0/8 allow

That’s it. That’s all we have to do to configure Unbound. Now we start it and enable it: sudo systemctl start unbound && sudo systemctl enable unbound, and we’re done with unbound

You’ll notice that the server doesn’t listen on v6. That’s because it’d be useless, since the DNS server is given to hosts on my network via DHCP, that there’s no v6-only hosts on my network, and that we’re gonna play with the v6 port later in ways that would render the server inaccessible either way.

Anyways, it’s now time to install and configure NSD on that server. Installation is the same as earlier, and even the config file differs only a little bit:

# NSD configuration file for Debian.

server:
    do-ip4: yes
    ip4-only: no
    hide-version: no
    zonesdir: "/etc/nsd/zones/"
    logfile: "/var/log/nsd.log"
    pidfile: "/run/nsd/nsd.pid"
    port: 1053

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

key:
    name: "push-key"
    algorithm: hmac-sha256
    secret: "redacted"


pattern:
    name: "copy-to"
    notify: <other server's ip> push-key
    provide-xfr: <other server's ip> push-key

pattern:
    name: "copy-from"
    allow-notify: <other server's ip> push-key
    request-xfr: AXFR <other server's ip> push-key

# Primary Zones

zone:
    name: home.wxcafe.net
    zonefile: primary/home.wxcafe.net
    include-pattern: "copy-to"

# Replica zones

zone:
    name: wxcafe.net
    zonefile: replica/wxcafe.net
    include-pattern: "copy-from"

As you can see, it’s pretty similar, the only differences being that the primary zones of the other server replicas on this one and vice-versa, and that the port is 1053. This is the interesting part.

So, of course, since we now have two DNS servers running on the same host, one is going to bind to port 53 and then the other one won’t be able to. That’s problematic, obviously, since most hosts are going to do DNS queries on port 53 and I can’t really change that. Since the authoritative part of this server is behind a NAT, I can easily just port forward packets from the external IP’s port 53 to any internal port, which is the obvious solution considering I have no recourse changing which port internal hosts who get their DNS resolver through DHCP query on.

Once I added the NAT port forwarding, there was still a small problem, tho… external IPv6 hosts don’t benefit from that port forward, they still get a reply from Unbound (telling them they’re not in the allowed range) instead of from NSD.

To make that happen, I had to do something pretty ugly… a port forward in IPv6. I used this ip6tables rule:

-A PREROUTING -p udp -m udp --dport 53 -j REDIRECT --to-ports 1053

YES, I KNOW, port forwarding in v6 is dirty, but right now it works! Next step is moving each service (the authoritative name server and the recursive one) to their own VM. For now, it works fine and was really easy to setup!

Hope that might be useful, or at least interesting, to you 😊! I’ll be posting a write-up of EuroBSDCon soon…-ish!

See ya!