Unbound DNS Resolver

It is my strong opinion that every server on the Internet should run a local DNSSEC validating caching / recursive resolver and use it for all of the operating system name resolution needs. DNSSEC helps protect a server from attacks that are based on fraudulent DNS query results. This protection exists even if the applications themselves do not have the ability to validate the answer to a DNS query.

With mail servers, it is particularly important to have a local DNSSEC enforcing recursive resolver. Many e-mail services are particularly vulnerable to DNS based attacks. If the DNS zone responsible for the query being performed is signed with DNSSEC, a local DNSSEC enforcing caching resolver will protect you from fraudulent results.

Installing Unbound

From the Unbound homepage:

Unbound is a validating, recursive, caching DNS resolver. It is designed to be fast and lean and incorporates modern features based on open standards.

It certainly is not the only DNSSEC validating recursive resolver out there, but it is both fast and easy to set up.

To install Unbound in LibreLAMP:

[root@host ~]# yum -y install unbound bind-utils
[root@host ~]# systemctl enable unbound.service
[root@host ~]# systemctl start unbound.service

The bind-utils is in the yum command simply because it includes the dig utility which is useful for testing Unbound.

Unbound in it’s default configuration as packaged for LibreLAMP will DNSSEC validate and only listen on the localhost, which is exactly what we want. Additional optional configuration can be used if you want Unbound to act as a caching only resolver that queries another recursive nameserver for results. This can have some performance benefits.

Optional Configuration: Caching Only

Caution: Do not do this optional step on MX servers if you use a DNS based blacklist or whitelist that limits the number of queries you are allowed in a twenty-four period. The IP address they count the queries from is the recursive resolver that queries them, so if your queries to the their list go through a recursive resolver that is shared with others, the limit is shared with others too.

By default, when Unbound does not have the answer to a DNS query cached, it will look for the right authoritative nameservers to query and query those.

This is usually very fast, and certainly fast enough for mail server needs, but it is often slower than if Unbound is configured to query an upstream recursive resolver. Most data centers have on-site recursive resolvers that answer queries for hundreds of other servers at the data center. Chances are they have the answer to the query you need, configuring Unbound to get it from them will be faster than finding and querying the proper authoritative DNS server, and Unbound will still DNSSEC validate the results, so you get the same protection.

In this example, we will pretend 8.8.8.8 and 8.8.4.4 are those recursive resolvers provided by your data center. You will have to get the actual IP addresses to use from your data center.

Note that I personally never take this step on servers, Unbound is plenty fast at retrieving results I need from the authoritative nameservers. But some people like to, and this step can be very beneficial to the home workstation that is farther from backbone, so this is how.

Edit the file /etc/unbound/unbound.conf and add the following to the end of it:

forward-zone:
      name: "."
      forward-addr: 8.8.8.8
      forward-addr: 8.8.4.4

You get all the performance benefits of the recursive resolvers at your data center that probably already have the answer you seek cached and are likely tuned to get the answer very quickly when they do not have it cached and you still get all the security benefits of local DNSSEC validation.

The reason I personally never make this change on my servers, no performance issue I have ever encountered has been solved by configuring the local Unbound instance to query another recursive resolver. So to me, taking this step just adds complexity that can break if the upstream caching resolvers go offline. So to me personally it is illogical to make this change to sometimes save a few milliseconds.

I do make the above change on my workstation, but it has very limited bandwidth making the time to find and query the authoritative nameservers take much longer. My servers though are at facilities connected to backbone.

If you do make this (or any configuration) change, restart the Unbound daemon after making the change:

[root@host ~]# systemctl restart unbound.service

Test Unbound

Before setting up the operating system to use Unbound, test it to make sure DNSSEC validation works. These tests can be performed as a non-root user. First, test for a positive result from a signed zone. The @127.0.0.1 at the end of the command tells dig to use the nameserver on your localhost rather than what the operating system is currently configured to use by default.

[user@host ~]$ dig MX deviant.email +dnssec +multi @127.0.0.1

You should get results like this with status: NOERROR, along with the RRSIG signature record:

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7_5.1 <<>> MX deviant.email +dnssec +multi @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30919
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags: do; udp: 4096
;; QUESTION SECTION:
;deviant.email.		IN MX

;; ANSWER SECTION:
deviant.email.		3443 IN	MX 10 mail.deviant.email.
deviant.email.		3443 IN	RRSIG MX 7 2 3600 (
				20181215051714 20181117051714 5240 deviant.email.
				P+oR46evxz6ZHbJ23exAmcFQBgqcz18J0moImP99MWjo
				Z8nPFoVsLJAv1Ckr3gNJe8w93To/fvvdB1/BGQE5aMfp
				F0chch0a5UjExCOA4H+B1XSkXTp5AtOqHXXGS+sQ69xn
				fdTn3QJR6amPY9vW6hkku48IYEabumt7il8h58pzHo+y
				3eu6wfGl0lYfvgV8kkaBmkk6K/FWLQoM9+Pe6ltgrOgd
				lEsE5lPIwBoWjSI2DOia2AmhwgyBEHEP3EBRAeHkAePg
				2r0Q8PBcA+SJhg68JH9kuN7X/EDjh0371mvVHMIURqIj
				Ez+0VN9Pq6miQyKl71XIbGW/q7S+nl+r1A== )

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Nov 17 04:48:29 PST 2018
;; MSG SIZE  rcvd: 364

If you do not get results like above, Unbound is not working properly.

Now test by querying a record from a signed zone but for which the validation should fail:

[user@host ~]$ dig www.dnssec-failed.org @127.0.0.1

The result from that query should contain status: SERVFAIL:

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7_5.1 <<>> www.dnssec-failed.org @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 35463
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.dnssec-failed.org.		IN	A

;; Query time: 2751 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Nov 17 05:00:53 PST 2018
;; MSG SIZE  rcvd: 50

Assuming both tests gave the results they should have, Unbound is working properly in DNSSEC validation mode. It can now be configured as the default name resolver for the server.

Configure Server To Use Local Resolver

Your operating system will use the contents of /etc/resolv.conf to determine what nameservers are used. To configure your server to use Unbound running on the localhost the contents of that file should look like this:

nameserver 127.0.0.1
nameserver ::1

Even when the IP addresses of hosts at data centers are static, hosts at data centers tend to get their network settings via DHCP and it wants to overwrite the contents of that file on boot. We have to configure CentOS not to.

Edit the file /etc/sysconfig/network-scripts/INTERFACE where the INTERFACE is usually ifcfg-eth0. On one VM system of mine, the name of the file is ifcfg-Wired_connection_1. The name is actually arbitrary and often chosen by the engineer who created the VM image your hosting facility uses when setting you up. ifcfg-eth0 is what is usually chosen, but not always. Anyway…

Add the following lines to end of that file:

PEERDNS=no
IPV6_PEERDNS=no
DNS1=127.0.0.1
DNS2=::1

Usually that is all you need to do on CentOS 7.

Sometimes PEERDNS and IPV6_PEERDNS are already defined to a value of yes in which case remove those previous definitions before adding the above four lines.

Final Thoughts

Security on the Internet begins with the integrity of DNS results.

DNS is also unfortunately very easy to attack. Results to queries are typically sent without encryption or any means to verify they have not been altered during transit.

Furthermore, modern server administration makes it easy for bad actors to completely undermine the name resolution system on a server. It is a weakness so obvious, I do not understand why so few do anything about it.

Modern servers in data centers grab the IP addresses for the nameservers they use from DHCP whenever they boot. No mechanism for authenticating the response from the DHCP server is genuine is implemented.

Servers in data centers are usually virtual machines and can often be rebooted remotely with some ease. Trigger a reboot, modify the response when the VM requests resolvers to use, and the VM is now happily sending all of it’s DNS queries to the attacker for resolution.

DNSSEC provides some protection, it provides a mechanism by which DNS results can be validated against cryptographic signatures. Most applications are not capable of validating the responses they are fed, and asking them to is the wrong approach.

The operating system name resolution needs to use either a nameserver running on the host that performs DNSSEC validation, or it needs to use a secure connection (DNS over TLS) to a namerserver running on a different host that is trusted to performs DNSSEC validation.

For a server on the Internet, the first option is more practical.

Secure the name resolution on your servers. Run a local DNSSEC validating resolver and use it for all name resolution on the server.

If you can, take it one step further and implement DNSSEC on the DNS zones for your domain names.