DNSSEC-aware DNS caching with Unbound

Name Game

Configuring Unbound

After I prove that Unbound does validate DNSSEC, I need to make some changes to the config files for both Unbound and the Docker container

To change the Unbound configuration, I edit the configuration file:

cd /var/lib/docker/volumes/unbound_unbound_conf/_data/
cp unbound.conf unbound.conf.old #just in case

I do not want to use any forward DNS servers, so I delete all the lines of the forward section (Listing 2, with hashes). No forward zone at all should remain in the config file.

Listing 2

Forward Section

    forward-zone:
        # Forward all queries (except those in cache and
        # local zone) to upstream recursive servers
        #name: "."
        # Queries to this forward zone use TLS
        #forward-tls-upstream: yes
        # Cloudflare
        #forward-addr: 1.1.1.1@853#cloudflare-dns.com
        #forward-addr: 1.0.0.1@853#cloudflare-dns.com

I will also tweak the security settings (Listing 3). You'll need to adjust the access-control setting to your internal LAN IP range: the IP ranges you provide are networks allowed to use recursive resolving. If you don't set up this section properly, you might end up amplifying distributed denial of service (DDoS) attacks on the Internet.

Listing 3

Security Settings

 # only give access to recursion clients from LAN IPs
    access-control: 127.0.0.1/32 allow
    access-control: 192.168.0.0/24 allow  # adjust to your lan ip-range
    auto-trust-anchor-file: "var/root.key"
    chroot: "/opt/unbound/etc/unbound"
    harden-algo-downgrade: no
    harden-below-nxdomain: yes
    harden-dnssec-stripped: yes
    harden-glue: yes
    harden-large-queries: yes
    harden-referral-path: no
    harden-short-bufsize: yes
    hide-identity: yes
    hide-version: yes
    identity: "DNS"
    # These private network addresses are not allowed to be returned for public
    # internet names. Any  occurrence of such addresses are removed from DNS
    # answers. Additionally, the DNSSEC validator may mark the  answers  bogus.
    # This  protects  against DNS Rebinding
    private-address: 10.0.0.0/8
    private-address: 172.16.0.0/12
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    ratelimit: 1000
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    unwanted-reply-threshold: 10000
    use-caps-for-id: no  #gives many false errors
    val-clean-additional: yes

You also should adjust the performance settings according to the number of CPU cores you have (Listing 4). My VM has one CPU with two cores. See the Unbound documentation for more on optimizing Unbound [8].

Listing 4

Performance Settings

   ## performance settings
   infra-cache-slabs: 4
    key-cache-slabs: 4
    msg-cache-size: 302921045
    msg-cache-slabs: 4
    num-queries-per-thread: 4096
    num-threads: 2
    outgoing-range: 8192
    rrset-cache-size: 605842090
    rrset-cache-slabs: 4
    minimal-responses: yes
    so-reuseport: yes
    prefetch: yes
    prefetch-key: yes
    serve-expired: yes

Of course, if the server does not work after a change; start it again with:

docker-compose up

Look for the errors, then look for a solution online.

You can also move into the container and ask Unbound to check the configuration:

docker exec -ti unbound /bin/bash
sudo unbound-checkconf
exit

Adding a Local Zone

In my LAN, I have a few (Dockerized) servers I want to be able to find easily: let's say time.hanscees.com , canon.hanscees.com (a printer), and bananas (a Banana Pi NAS server). Unbound can serve these DNS resource records (RRs) without a problem. It is handy to give zones their own file by adding a line in unbound.conf pointing to a separate file:

include: /opt/unbound/etc/unbound/a-records.conf
include: /opt/unbound/etc/unbound/ptr-records.conf

To define local records, edit a-records.conf and add entries for the local records (Listing 5).

Listing 5

Adding Local Records

#local-zone: "hanscees.com" static
local-data: "canon.hanscees.com A 192.168.0.60"
local-data: "time.hanscees.com A 192.168.0.61"
local-data: "bananas.hanscees.com A 192.168.0.62"

Notice you can choose whether to define the zone in the config file or not: uncheck the local-zone line if you want to define the local zone. If you do, Unbound assumes that somename.hanscees.com does not exist if you have not added it to the config file. If you do not define the local-zone, and a name does not exist in the config file, Unbound will try to look it up on the Internet.

Control Client

Unbound has a control feature that lets you connect the Unbound client to the Unbound server. This way, you can get statistics and add hosts and changes to your configuration while Unbound is running.

The default config file has the control feature switched off. To enable it, you need to tweak the configuration file, generate some keys for authentication, and expose the port via Docker. For better security, I will configure the Unbound server to allow only one IP address to the control port. See the changes in Listing 6.

Listing 6

The Control Client

01 remote-control:
02     control-enable: yes
03     control-interface: 172.18.0.2
04     control-port: 953
05     server-key-file: "/opt/unbound/etc/unbound/unbound_server.key"
06     server-cert-file: "/opt/unbound/etc/unbound/unbound_server.pem"
07     control-key-file: "/opt/unbound/etc/unbound/unbound_control.key"
08     control-cert-file: "/opt/unbound/etc/unbound/unbound_control.pem"

The control-interface line is a bit tricky: you need to pick the IP address Unbound can see inside the container, and it needs to be the interface that the VM exposes via your LAN device.

You can easily see the necessary address and gateway with:

docker inspect caa97f2fe1fe | egrep "Gateway|IPAddress" | egrep 172

To generate the keys, enter:

docker exec -ti unbound /bin/bash #log into container
unbound-control-setup

You will see Unbound generating keys. Now restart container and test whether nothing has broken:

docker restart caa97f2fe1fe
dig @192.168.0.110 time.hanscees.com

To install the remote client, just install Unbound on a remote Linux machine. In my case, this also installed the Unbound server, so I had to stop it.

The client needs a config file with the right keys to authenticate itself to the server. These commands worked on the client PC (running Ubuntu 18):

sudo apt-get install unbound
sudo systemctl stop unbound.service
sudo systemctl disable unbound.service

To authenticate the client to the Unbound server, move some of the control keys from the server to the control client:

sudo scp root@192.168.0.110:/var/lib/docker/volumes/unbound_unbound_conf/_data/unbound_control.key ~/keys/
sudo scp root@192.168.0.110:/var/lib/docker/volumes/unbound_unbound_conf/_data/unbound_server.pem ~/keys/

Also, reflect this information in the config file for the client (Listing 7).

Listing 7

Remote Control Settings

remote-control:
    server-cert-file: "/home/hanscees/keys/unbound_server.pem"
    control-key-file: "/home/hanscees/keys/unbound_control.key"
    control-cert-file: "/home/hanscees/keys/unbound_control.pem"

Now I can start using the client. I'll enable extended stats and then take a look at some statistics:

sudo unbound-control -c ~/keys/unbound.conf -s 192.168.0.112@953 set_option extended-statistics: yes
sudo unbound-control -c ~/keys/unbound.conf -s 192.168.0.112@953 stats_noreset | egrep "total.num|secure"

The output appears in Listing 8.

Listing 8

Statistics

[sudo] password for hanscees:
total.num.queries=88937
total.num.queries_ip_ratelimited=0
total.num.cachehits=83513
total.num.cachemiss=5424
total.num.prefetch=22534
total.num.zero_ttl=11075
total.num.recursivereplies=5424
num.answer.secure=238

The statistics there show that, from 88,937 queries, 8,351 were served out of the cache (94%), and 238 of these queries were validated by DNSSEC.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus