Caching DNS queries with Unbound

By default on Linux distributions like CentOS, if a process needs to resolve a hostname it will query a remote name server. The IP address of the name server(s) which is queried is controlled by /etc/resolv.conf. While this process is very straightforward, it does mean if the same address is queried multiple times, the system will have to send multiple DNS queries over the network. This post is going to look at configuring Unbound to act as a local DNS cache.

Caching requests

When a process calls a function like getaddrinfo, a query is sent via the network:

A diagram showing a standard DNS query

Instead of the process making the call directly to the remote name server, traffic can initially be sent to Unbound. Unbound will then forward the request on to a remote server:

A diagram showing Unbound forwarding a DNS query

However once Unbound received a reply it can cache the response. Subsequent requests can then be answered using the cached answer:

A diagram showing Unbound using a cached DNS query

This does add some additional complexity, however the cached responses will avoid network latency and should therefore be quicker:

$ time host example.com 1.1.1.1
Using domain server:
Name: 1.1.1.1
Address: 1.1.1.1#53
Aliases:

example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

real    0m0.093s
user    0m0.007s
sys     0m0.008s

$ time host example.com 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases:

example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

real    0m0.018s
user    0m0.012s
sys     0m0.005s

Using a cache for DNS queries is also helpful if the connection to a remote name server is unreliable, as cached requests do not depend on the network.

Setting up Unbound

On CentOS 7 Unbound can be installed using yum

yum install -y unbound bind-utils

Once Unbound is installed, the remote name server(s) to forward DNS queries to can be set with configuration similar to the following:

forward-zone:
        name: "."
        forward-addr: 1.1.1.1
        forward-addr: 1.0.0.1

This configuration will tell Unbound to forward all DNS queries onto Cloudflare's public DNS servers. The configuration can either be added to the main configuration file, /etc/unbound/unbound.conf, or to an additional config file under /etc/unbound/conf.d/. You may also want to set additional options such as cache-max-ttl. Information on additional options can be found in the unbound.conf man page.

Once you're happy with the configuration, enable and start the Unbound service:

systemctl enable unbound
systemctl start unbound

If everything goes well you should be able to query the Unbound server:

$ host example.com 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases:

example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946

You can also use the unbound-control command to verify Unbound is forwarding queries on to the correct name server(s):

$ unbound-control lookup example.com
The following name servers are used for lookup of example.com.
forwarding request:
Delegation with 0 names, of which 0 can be examined to query further addresses.
It provides 2 IP addresses.
1.1.1.1                 rto 147 msec, ttl 144, ping 19 var 32 rtt 147, tA 0, tAAAA 0, tother 0, EDNS 0 probed.
1.0.0.1                 rto 194 msec, ttl 144, ping 10 var 46 rtt 194, tA 0, tAAAA 0, tother 0, EDNS 0 probed.

Once you're happy Unbound is functioning correctly, /etc/resolv.conf can be updated to point at 127.0.0.1 with configuration similar to the following:

nameserver 127.0.0.1

If /etc/resolv.conf has been generated by NetworkManager, it will be overwritten when the network is restarted. To get around this the NetworkManager configuration can be updated with commands similar to the following:

nmcli connection modify System\ enp0s3 ipv4.dns 127.0.0.1
nmcli connection modify System\ enp0s3 ipv4.ignore-auto-dns yes

Allowing remote queries

By default Unbound will only accept DNS queries on a local interface. It is however fairly straightforward to allow remote queries, this can be done with the following configuration:

server:
        interface: 0.0.0.0
        access-control: 10.0.0.0/8 allow

This will tell Unbound to listen on any IPv4 interface, and allow remote queries from the 10.0.0.0/8 subnet.