Unbound DNS Server

My local DNS resolver of choice was dnsmasq for a long time. It’s not the best, it’s not “enterprise-y”, but it’s easy to set up, and most importantly, it works. As part of my last network overhaul, I decided to give unbound a try.

This is a short article about my experience and configuration.

All you really need to know is in this excellent tutorial over at calomel.org. You should take a moment to read it.

A few months later, I am still happy with unbound. DNSSEC works, and it has some security benefits such as thwarting cache poisoning attempts. The caveat is that it does not have DHCP integration. That’s fine, OpenBSD has dhcpd. However, if you want DNS lookups to work with DHCP assignments, you have to look elsewhere.

My current solution is a hybrid. Unbound for DNS, dnsmasq for DHCP. Unbound forwards lookups for the local network to dnsmasq. There’s probably a better way to do this, but again, it works.

Configuration

My abbreviated configuration looks like this. See the calomel.org article for more detail. Paths shown are for OpenBSD, your installation may vary.

/var/unbound/etc/unbound.conf:

:::Text only
remote-control:
    control-enable: yes

server:
    verbosity: 1
    interface: 0.0.0.0
    port: 53
    do-ip4: yes
    do-ip6: no
    do-udp: yes
    do-tcp: yes
    access-control: 127.0.0.0/8 allow
    access-control: 192.168.0.0/16 allow
    root-hints: "/var/unbound/etc/root.hints"
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: yes
    cache-min-ttl: 3600
    cache-max-ttl: 86400
    prefetch: yes
    num-threads: 2

    msg-cache-slabs: 8
    rrset-cache-slabs: 8
    infra-cache-slabs: 8
    key-cache-slabs: 8
    rrset-cache-size: 256m
    msg-cache-size: 128m

    private-address: 192.168.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    unwanted-reply-threshold: 10000
    auto-trust-anchor-file: "/var/unbound/etc/root.key"
    val-clean-additional: yes

    include: "/var/unbound/etc/local_blocks"

    do-not-query-localhost: no

    # Your internal network name and addresses go here
    private-domain: "my.internal.domain.name"
    domain-insecure: "my.internal.domain.name"
    private-domain: "1.168.192.in-addr.arpa."
    domain-insecure: "1.168.192.in-addr.arpa."
    local-zone: "1.168.192.in-addr.arpa." transparent

    # Forward lookups for your internal domain name to dnsmasq
    forward-zone:
        name: "my.internal.domain.name."
        forward-addr: 127.0.0.1@5353

    # Reverse lookups for your internal domain name to dnsmasq
    forward-zone:
        name: "1.168.192.in-addr.arpa."
        forward-addr: 127.0.0.1@5353

    forward-zone:
        name: "."
        forward-addr: 8.8.4.4        # Google
        forward-addr: 8.8.8.8        # Google
#      forward-addr: 37.235.1.174   # FreeDNS
#      forward-addr: 37.235.1.177   # FreeDNS
#      forward-addr: 50.116.23.211  # OpenNIC
#      forward-addr: 64.6.64.6      # Verisign
#      forward-addr: 64.6.65.6      # Verisign
#      forward-addr: 74.82.42.42    # Hurricane Electric
#      forward-addr: 84.200.69.80   # DNS Watch
#      forward-addr: 84.200.70.40   # DNS Watch
#      forward-addr: 91.239.100.100 # censurfridns.dk
#      forward-addr: 109.69.8.51    # puntCAT
#      forward-addr: 208.67.222.220 # OpenDNS
#      forward-addr: 208.67.222.222 # OpenDNS
#      forward-addr: 216.146.35.35  # Dyn Public
#      forward-addr: 216.146.36.36  # Dyn Public

You can block or override specific domains with this:

/var/unbound/etc/local_blocks: (from calomel.org)

:::Text only
# Blocking Ad Server domains. Google's AdSense, DoubleClick and Yahoo
# account for a 70 percent share of all advertising traffic. Block them.
local-zone: "doubleclick.net" redirect
local-data: "doubleclick.net A 127.0.0.1"

local-zone: "googlesyndication.com" redirect
local-data: "googlesyndication.com A 127.0.0.1"

local-zone: "googleadservices.com" redirect
local-data: "googleadservices.com A 127.0.0.1"

local-zone: "google-analytics.com" redirect
local-data: "google-analytics.com A 127.0.0.1"

local-zone: "ads.youtube.com" redirect
local-data: "ads.youtube.com A 127.0.0.1"

local-zone: "adserver.yahoo.com" redirect
local-data: "adserver.yahoo.com A 127.0.0.1"

local-zone: "ask.com" redirect
local-data: "ask.com A 127.0.0.1"

local-zone: "amazon-adsystem.com" redirect
local-data: "amazon-adsystem.com A 127.0.0.1"

local-zone: "scorecardresearch.com" redirect
local-data: "scorecardresearch.com A 127.0.0.1"

local-zone: "doubleclick.com" redirect
local-data: "doubleclick.com A 127.0.0.1"

local-zone: "quantserve.com" redirect
local-data: "quantserve.com A 127.0.0.1"

You can find dnsmasq configuration examples online, and the defaults are reasonable, so I won’t go into them here. You do need to make the following changes to dnsmasq.conf:

  • port=5353 Make sure dnsmasq’s DNS server is listening on an alternate port. This should match the forward-addr: 127.0.0.1@5353 lines in unbound.conf.
  • domain=my.internal.domain.name Again, make sure this matches up with unbound.conf.
  • resolv-file=/etc/somefile. Make sure dnsmasq uses specific resolvers (such as 8.8.8.8 and 8.8.4.4). This helps avoid a loop back issue when resolving hosts on the local network.

Caveats

On OpenBSD, you may need to change the permissions on /var/unbound/etc so that it can update the root key files. chown _unbound:wheel /var/unbound/etc && chmod 755 /var/unbound/etc.

Final Thoughts

You can test DNSSEC with DNSSEC-Check.

Another quick test is to see if www.dnssec-failed.org resolves:

:::Text only
# Example of working DNSSEC:
$ dig +short www.dnssec-failed.org
$

# Example of failed DNSSEC:
$ dig +short www.dnssec-failed.org @216.146.35.35
68.87.109.242
69.252.193.191
$