$_ BSDHowTo.ch
How To... Why not..? Scripts Patches RSS logo

How to build internal DNS using unbound(8) and nsd(8)

Last update: 2020-08-14

Introduction

OpenBSD comes with unbound(8) and nsd(8) in base. This post shows you how to combine these two tools to provide DNS for an internal network including an internal DNS zone.

Configuration of nsd(8)

In this first part you will configure nsd(8) as the authoritative name server for forward and reverse lookup zones of your internal network. I will call the forward zone example.net and the reverse zone 2.0.192.in-addr.arpa. The following entries belong into /var/nsd/etc/nsd.conf:

server:
    hide-version: yes
    ip-address: 127.0.0.1
    server-count: 1

remote-control:
    control-enable: yes

zone:
    name: "example.net"
    zonefile: "/var/nsd/zones/master/%s.dns"

zone:
    name: "2.0.192.in-addr.arpa."
    zonefile: "/var/nsd/zones/master/%s.dns"

Remote control for nsd(8)

The remote control tool nsd-control(8) let you send commands to the running nsd(8), e. g. to reread a changed zone file without restarting the whole daemon. You need to run a setup tool once to generate the certificates and keys for nsd-control(8):

$ doas nsd-control-setup

The zone files for nsd(8)

The next step is to write the zone files for nsd(8). First the forward lookup zone example.net:

$ORIGIN .
$TTL 3600   ; 1 hour
example.net IN SOA  dns.example.net. admin.example.net. (
            1   ; serial
            86400   ; refresh (1 day)
            3600    ; retry (1 hour)
            604800  ; expire (1 week)
            3600    ; minimum (1 hour)
            )
            NS  dns.example.net.
$ORIGIN example.net.
dns         A   192.0.2.11
router      A   192.0.2.1
server      A   192.0.2.80
www         CNAME   server

Save this zone file as /var/nsd/zones/master/example.net.dns. Second, the reverse lookup zone 2.0.192.in-addr.arpa.:

$ORIGIN .
$TTL 3600   ; 1 hour
2.0.192.in-addr.arpa    IN SOA  dns.example.net. admin.example.net. (
            1   ; serial
            86400   ; refresh (1 day)
            3600    ; retry (1 hour)
            604800  ; expire (1 week)
            3600    ; minimum (1 hour)
            )
            NS  dns.example.net.
$ORIGIN 2.0.192.in-addr.arpa.
1           PTR router.example.net.
11          PTR dns.example.net.
80          PTR server.example.net.

Save this zone file as /var/nsd/zones/master/2.0.192.in-addr.arpa.dns.

Configuration of unbound(8)

The configuration of unbound(8) I show you in this post turns unbound(8) into a iterative, caching and validating resolver. The content of the file /var/unbound/etc/unbound.conf is:

server:
    access-control: 192.0.2.0/24 allow
    auto-trust-anchor-file: "/var/unbound/db/root.key"
    do-not-query-localhost: no
    domain-insecure: example.net
    domain-insecure: 2.0.192.in-addr.arpa
    hide-identity: yes
    hide-version: yes
    insecure-lan-zones: yes
    interface: 192.0.2.11
    local-zone: example.net nodefault
    local-zone: 2.0.192.in-addr.arpa nodefault
    num-threads: 1
    prefetch: yes
    private-domain: example.net
    private-domain: 2.0.192.in-addr.arpa
    qname-minimisation: yes
    root-hints: "/var/unbound/db/root.hints"

remote-control:
    control-enable: yes
    control-interface: 192.0.2.11

stub-zone:
    name: "example.net"
    stub-addr: 127.0.0.1

stub-zone:
    name: "2.0.192.in-addr.arpa"
    stub-addr: 127.0.0.1

If the data in your zones changes very often you may consider setting stub-no-cache: yes for each of the affected stub zones. This disables the caching for the zone it is set, making unbound always querying nsd for the latest data.

Remote control for unbound(8)

The remote control tool unbound-control(8) let you send commands to the running unbound(8), e. g. to flush the cache without restarting the whole daemon. You need to run a setup tool once to generate the certificates and keys for unbound-control(8):

$ doas unbound-control-setup

Configuration check and start

Both nsd(8) and unbound(8) bring along a tool to check the configuration file before you start or reload the daemon. You should start with nsd(8):

$ doas nsd-checkconf

Any errors are reported, so no news are good news. You can go ahead and start nsd(8):

$ doas rcctl enable nsd
$ doas rcctl start nsd

Now repeat the dance for unbound(8). First check the configuration file for errors:

$ doas unbound-checkconf
unbound-checkconf: no errors in /var/unbound/etc/unbound.conf

Time to start unbound(8);

$ doas rcctl enable unbound
$ doas rcctl start unbound

If you want the server to use unbound(8) itself you must adapt resolv.conf(5) to use the IP address unbound(8) is listening on:

search example.net
nameserver 192.0.2.11
lookup file bind