Unbound is a validating, recursive, caching DNS resolver. Developed by NLnet Labs, the software is available in open-source formfor Unix-type systems and Windows.
If all you need is a validating resolver, Unbound is probably a better option than BIND named, the most widely used (authoritative) DNS server that can also function as a validating resolver. Unbound is a compact, dedicated resolver designed to work entirely independently from BIND. SIDN has contributed to the development of Unbound, and uses the resolver itself. Other alternatives to using BIND as a resolver are the PowerDNS Recursor and the KNOT Resolver.
In this article, we consider the configuration of Unbound on a Linux system (in part I). We do not confine ourselves to the set-up of a validating resolver as a base for a DNS service suitable for use by an entire organisation. After all, Unbound is ideal for validation at an endpoint — in other words, an end user's workstation or laptop. Consideration is therefore also given to diversion of the standard POSIX/C stub resolver and the use of systemctl (systemd), even though some of the information presented will already be familiar to operators.
Part II of the article is devoted to DNSSEC-Trigger, a tool for automatically (re)configuring Unbound when it is used as a validator on a system whose network configuration is subject to frequent change. System operators can safely disregard part II, since DNSSEC-Trigger is useful mainly to mobile users. Operators of fixed systems normally route DNS queries to Unbound directly using the /etc/resolv.conf configuration file or indirectly using the NetworkManager.
Contents
Part I — Unbound
1 Linux
Unbound is now the standard resolver on various Linux distributions, and on FreeBSD and OpenBSD. Version 7 of RHEL/CentOS includes Unbound version 1.6.6, while RHEL/CentOS version 6 has for stuck with release 1.4.20 for some time now.
The latest versions of Unbound for RHEL/CentOS 7 can be downloaded/installed from the GhettoForge Plus repository or Psychotic Ninja Plus repository.
Fedora versions 27 and 28 are provided with Unbound version 1.7.3, while version 29 (currently under development) will come with the newly released version 1.8.1.
The information presented here is based on CentOS 7.5 (1804), which, as indicated, is provided with Unbound version 1.6.6. For details of the differences between releases, see the Unbound change log.
A separate manual is available on the installation and configuration of Unbound on Windows. A manual for home users and small networks can be found here.
2 POSIX/C stub resolver
The default configuration of Linux and other Unix-type systems uses the traditional POSIX/C stub resolver to translate host names into IP addresses. Applications can nowadays check the AD flag provided with the response sent by the queried caching resolver [1, 2]. However, the POSIX/C stub resolver does not itself perform DNSSEC validation or check AD flags. The Windows stub resolver can be configured to block a domain if the AD flag has not been set by the queried resolver.
Even if a stub resolver or application does check the AD flag, a user whose system is using the default configuration may reasonably assume that the final leg of the DNS transmission — between the caching resolver and the stub resolver on their own computer — is secure. Security problems are also unlikely in a business network where the local computers use a central DNS server. However, when a laptop is used on a public Wi-Fi network, the insecure 'last mile' is problematic. In that situation, the use of a validating resolver at the endpoint — on your own computer — is strongly recommended. We shall return to this topic in the part of the article dealing with DNSSEC-Trigger.
3 Resolv.conf
The file '/etc/resolv.conf' is central to configuration of the POSIX/C stub resolver. The key information in the file consists of a list of name servers to be used (in the form of a series of 'nameserver' statements). If no name servers are defined, the local host is used.
Whereas the list used to be compiled manually, the name servers are nowadays generally added automatically by the DHCP client, using the NetworkManager. Thus, the contents of '/etc/resolv.conf' will typically be something like the following:
# Generated by NetworkManager search example.nl nameserver 192.0.2.5
Alternatively, the user may compile their own list of public resolvers, e.g. those of the 1.1.1.1 DNS service:
nameserver 1.1.1.1 nameserver 1.0.0.1
And, for IPv6:
nameserver 2606:4700:4700::1111 nameserver 2606:4700:4700::1001
4 NetworkManager
The easiest way to configure a resolver is by using the NetworkManager, the graphical network configuration tool for Linux.
![](http://images.ctfassets.net/yj8364fopk6s/KrGuImdRWoa_61OV9iJqHA/55c801c0bd55d37faef07d5abd99c590/NetworkManager1.png?w=900&fl=progressive)
![](http://images.ctfassets.net/yj8364fopk6s/iRRCxBuzWyiDm_NVMRemNw/14b23df9a567ff61be30cf59d2ff61fd/NetworkManager-DNS1.png?w=900&fl=progressive)
![](http://images.ctfassets.net/yj8364fopk6s/q7o4LJusWR-IUldE2P4O-w/55ce13b6585c825c5b4abe29806daa6c/NetworkManager-DNS2a.png?w=900&fl=progressive)
As indicated in the IPv6 window, you have the option of continuing to use automatic DHCP network configuration, and thus specifying your own name servers. That involves setting the 'DNS: Automatic' switch to 'off'.
# Generated by NetworkManager search example.nl nameserver 1.1.1.1 nameserver 1.0.0.1 nameserver 2606:4700:4700::1111 # NOTE: the libc resolver may not support more than 3 nameservers. # The nameservers listed below may not be recognized. nameserver 2606:4700:4700::1001
Be alert to the possibility that, if you modify the '/etc/resolv.conf' file manually, it will be overwritten by the NetworkManager (which saves its configuration in '/etc/sysconfig/network-scripts/').
5 Hook
In order to use an Unbound resolver that is running locally, it has to be installed as a hook in the resolver chain. That involves using the NetworkManager to add the two IP addresses for the local host to the '/etc/resolv.conf' file. The previously defined name servers then serve as fallbacks.
![](http://images.ctfassets.net/yj8364fopk6s/toBmElDEXiOs1hJ_aEU_gg/4864c6e3e6133d4f9c06f133fdec7df3/NetworkManager3.png?w=900&fl=progressive)
![](http://images.ctfassets.net/yj8364fopk6s/UZkdTKfhXv2Nj9qtEsCmgw/242c4524039f9f5737ee366e21c01dc1/NetworkManager-DNS3a.png?w=900&fl=progressive)
![](http://images.ctfassets.net/yj8364fopk6s/hH2IJKuoVMObH9ciTSEK3w/02dd131b8ee2a7f29c9d95e679a07e97/NetworkManager-DNS4a.png?w=900&fl=progressive)
# Generated by NetworkManager search example.nl nameserver 127.0.0.1 nameserver 1.1.1.1 nameserver 1.0.0.1 # NOTE: the libc resolver may not support more than 3 nameservers. # The nameservers listed below may not be recognized. nameserver ::1 nameserver 2606:4700:4700::1111 nameserver 2606:4700:4700::1001
6 Unbound set-up
The first step in setting up Unbound itself is installation of the RPM package:
yum install unbound
Or, on Fedora:
dnf install unbound
That has the effect of installing four systemd services on the system:
unbound-anchor.service
unbound-anchor.timer
unbound-keygen.service
unbound.service
7 Bootstrapping and RFC 5011
The unbound-anchor.service retrieves the current root KSKtrust anchors for DNSSEC. The service does that using the 'unbound-anchor' command.
If the 'auto-trust-anchor-file' '/var/lib/unbound/root.key' doesn't yet exist, it is initiated using the trust anchors hard-coded into the software. In our case, the file is supplied as part of the Unbound package.
Following the initial bootstrap installation, the 'unbound-anchor.service' (triggered by the 'unbound-anchor.timer') will consult the root servers on a daily basis to check whether a new trust anchor is available (tracking). The check is performed in accordance with the RFC 5011 protocol (supported from version 1.4.0). If there is a new trust anchor, the DNSKEY record's signature is checked and the relevant key is installed in the 'auto-trust-anchor-file' as a dynamic trust anchor. We have previously explained how the RFC 5011 protocol works in this article.
8 Fallback: RFC 7958
If, for any reason, installation of a valid trust anchor by the method described above isn't successful — e.g. because the software is out of date, or because completely new root key pairs have been introduced for security reasons — the 'unbound-anchor.service' falls back on the RFC 7958 mechanism. That involves retrieving the 'well-known' trust anchors from the IANA website and validating them on the basis of an ICANN certificate chain. The relevant certificates are supplied with the Unbound package as '/etc/unbound/icannbundle.pem', but are also hard-coded into the software for fallback purposes.
Because the trust anchors form the basis for DNSSEC validation, RFC 7958 provides a complete list of checks that have to be carried out (or, preferably, performed by an installation script) before a new trust anchor may be accepted. We have previously described the process in full in this article.
9 KSK-2017 trust anchor
The current trust anchor (KSK-2017) is supplied with the Unbound software from version 1.6.1 (in the file '/etc/unbound/root.key'). However, our version 1.4.0 also includes the new trust anchors, provided by the RHEL/CentOS Unbound package maintainer.
We nevertheless advise checking that your new Unbound set-up does have a functional KSK-2017 trust anchor. To do that, first check whether the file '/etc/unbound/root.key' has a public key with key ID 20326. If it doesn't, refer to the series of articles specified in this bulletin for detailed information about the root KSK rollover and installation of the new trust anchor.
If you are using Unbound version 1.6.5 or a later version, you can follow RFC 7958 to install the new trust anchor by deleting the file '/etc/unbound/root.key' and re-running 'unbound-anchor'. With older versions of Unbound, the missing KSK-2017 trust anchor has to be installed manually.
10 More trust anchors
Quite separately from the KSK trust anchors, additional static trust anchors can be installed in the 'trust-anchor' file (blank by default), or in the '/etc/unbound/unbound.conf' configuration file, using the 'trust-anchor' option.
In addition to the command for starting the Unbound daemon, the 'unbound.service' itself includes exactly the same 'unbound-anchor' command as the 'unbound-anchor.service'. Hence, a system's trust anchors can be kept up to date even when Unbound is in use, while also ensuring that Unbound cannot be started without the correct trust anchors being installed.
Although the RHEL/CentOS package is supplied with the trust anchor for ISC's old DLV (Dynamic Lookaside Validation) service (in the file /etc/unbound/dlv.isc.org.key), the service has now been phased out and disabled. In the default configuration file provided with the package, the 'dlv-anchor-file' option is already commented out.
11 Unbound Control Key And Certificate Generator
The final service to be considered is the 'unbound-keygen.service' (the Unbound Control Key And Certificate Generator). It is run once to create the (self-signed) certificates and keys for the 'unbound-control' command. Unbound can then be (re)configured in run-time over a TLS-secured connection (much as with BIND's 'rndc' command). Further details are given in the part of this article that describes the DNSSEC-Trigger.
12 Start-up
That brings us to the activation of the services described above. Before starting Unbound, it is advisable to check the status of the various services:
[root@localhost ~]# systemctl status unbound.service unbound-anchor.service unbound-anchor.timer unbound-keygen.service ● unbound.service - Unbound recursive Domain Name Server Loaded: loaded (/usr/lib/systemd/system/unbound.service; disabled; vendor preset: disabled) Active: inactive (dead) ● unbound-anchor.service - update of the root trust anchor for DNSSEC validation in unbound Loaded: loaded (/usr/lib/systemd/system/unbound-anchor.service; static; vendor preset: disabled) Active: inactive (dead) Docs: man:unbound-anchor(8) ● unbound-anchor.timer - daily update of the root trust anchor for DNSSEC Loaded: loaded (/usr/lib/systemd/system/unbound-anchor.timer; disabled; vendor preset: disabled) Active: inactive (dead) Docs: man:unbound-anchor(8) ● unbound-keygen.service - Unbound Control Key And Certificate Generator Loaded: loaded (/usr/lib/systemd/system/unbound-keygen.service; disabled; vendor preset: disabled) Active: inactive (dead)
From the responses, we can see that all four services are currently disabled and inactive. The resolver is started by giving the following command:
[root@localhost ~]# systemctl start unbound.service unbound-anchor.service unbound-anchor.timer unbound-keygen.service
In addition, the following command is given to ensure that everything will continue running after a system reboot:
[root@localhost ~]# systemctl enable unbound.service unbound-anchor.service unbound-anchor.timer unbound-keygen.service Created symlink from /etc/systemd/system/multi-user.target.wants/unbound.service to /usr/lib/systemd/system/unbound.service. Created symlink from /etc/systemd/system/timers.target.wants/unbound-anchor.timer to /usr/lib/systemd/system/unbound-anchor.timer. Created symlink from /etc/systemd/system/multi-user.target.wants/unbound-keygen.service to /usr/lib/systemd/system/unbound-keygen.service.
If we then recheck the status of the four services, we should see that the start-up procedure has been successful, and that all the services are now enabled and active:
[root@localhost unbound]# systemctl status unbound.service unbound-anchor.service unbound-anchor.timer unbound-keygen.service ● unbound.service - Unbound recursive Domain Name Server Loaded: loaded (/usr/lib/systemd/system/unbound.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2018-09-08 21:17:16 CEST; 2min 23s ago Main PID: 9746 (unbound) CGroup: /system.slice/unbound.service └─9746 /usr/sbin/unbound -d Sep 08 21:17:15 localhost.localdomain systemd[1]: Starting Unbound recursive Domain Name Server... Sep 08 21:17:16 localhost.localdomain unbound-checkconf[9741]: unbound-checkconf: no errors in /etc/unbound/unbound.conf Sep 08 21:17:16 localhost.localdomain systemd[1]: Started Unbound recursive Domain Name Server. Sep 08 21:17:16 localhost.localdomain unbound[9746]: [9746:0] notice: init module 0: ipsecmod Sep 08 21:17:16 localhost.localdomain unbound[9746]: [9746:0] notice: init module 1: validator Sep 08 21:17:16 localhost.localdomain unbound[9746]: [9746:0] notice: init module 2: iterator Sep 08 21:17:16 localhost.localdomain unbound[9746]: [9746:0] info: start of service (unbound 1.6.6). ● unbound-anchor.service - update of the root trust anchor for DNSSEC validation in unbound Loaded: loaded (/usr/lib/systemd/system/unbound-anchor.service; static; vendor preset: disabled) Active: inactive (dead) since Sat 2018-09-08 21:17:15 CEST; 2min 23s ago Docs: man:unbound-anchor(8) Main PID: 9723 (code=exited, status=0/SUCCESS) Sep 08 21:17:15 localhost.localdomain systemd[1]: Starting update of the root trust anchor for DNSSEC validation in ...nd... Sep 08 21:17:15 localhost.localdomain systemd[1]: Started update of the root trust anchor for DNSSEC validation in unbound. ● unbound-anchor.timer - daily update of the root trust anchor for DNSSEC Loaded: loaded (/usr/lib/systemd/system/unbound-anchor.timer; enabled; vendor preset: disabled) Active: active (waiting) since Sat 2018-09-08 21:17:15 CEST; 2min 24s ago Docs: man:unbound-anchor(8) Sep 08 21:17:15 localhost.localdomain systemd[1]: Started daily update of the root trust anchor for DNSSEC. Sep 08 21:17:15 localhost.localdomain systemd[1]: Starting daily update of the root trust anchor for DNSSEC. ● unbound-keygen.service - Unbound Control Key And Certificate Generator Loaded: loaded (/usr/lib/systemd/system/unbound-keygen.service; enabled; vendor preset: disabled) Active: active (exited) since Sat 2018-09-08 21:17:15 CEST; 2min 23s ago Main PID: 9739 (code=exited, status=0/SUCCESS) CGroup: /system.slice/unbound-keygen.service Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: unable to write 'random state' Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: e is 65537 (0x10001) Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: create unbound_server.pem (self signed certificate) Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: create unbound_control.pem (signed client certificate) Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: Signature ok Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: subject=/CN=unbound-control Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: Getting CA Private Key Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: unable to write 'random state' Sep 08 21:17:15 localhost.localdomain unbound-control-setup[9722]: Setup success. Certificates created. Enable in unbo...use Sep 08 21:17:15 localhost.localdomain systemd[1]: Started Unbound Control Key And Certificate Generator. Hint: Some lines were ellipsized, use -l to show in full.
13 Results
If we now check what is running on our system, we see that the Unbound daemon has started four threads ('num-threads: 4') — an IPv4 thread and an IPv6 thread for UDP and the same for TCP. In addition, for both IP versions, Unbound is listening to TCP port 8953, the management portal for 'unbound-control'.
[root@localhost ~]# ps aux | grep unbound unbound 9746 0.0 0.4 284228 33312 ? Ssl 21:17 0:00 /usr/sbin/unbound -d [root@localhost unbound]# ss -nlput | grep unbound udp UNCONN 0 0 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=17)) udp UNCONN 0 0 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=13)) udp UNCONN 0 0 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=9)) udp UNCONN 0 0 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=5)) udp UNCONN 0 0 ::1:53 :::* users:(("unbound",pid=9746,fd=15)) udp UNCONN 0 0 ::1:53 :::* users:(("unbound",pid=9746,fd=11)) udp UNCONN 0 0 ::1:53 :::* users:(("unbound",pid=9746,fd=7)) udp UNCONN 0 0 ::1:53 :::* users:(("unbound",pid=9746,fd=3)) tcp LISTEN 0 128 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=18)) tcp LISTEN 0 128 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=14)) tcp LISTEN 0 128 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=10)) tcp LISTEN 0 128 127.0.0.1:53 *:* users:(("unbound",pid=9746,fd=6)) tcp LISTEN 0 128 127.0.0.1:8953 *:* users:(("unbound",pid=9746,fd=20)) tcp LISTEN 0 128 ::1:53 :::* users:(("unbound",pid=9746,fd=16)) tcp LISTEN 0 128 ::1:53 :::* users:(("unbound",pid=9746,fd=12)) tcp LISTEN 0 128 ::1:53 :::* users:(("unbound",pid=9746,fd=8)) tcp LISTEN 0 128 ::1:53 :::* users:(("unbound",pid=9746,fd=4)) tcp LISTEN 0 128 ::1:8953 :::* users:(("unbound",pid=9746,fd=19))
The 'unbound-keygen.service' has generated new keys and certificates for 'unbound-control' in the directory '/etc/unbound/':
-rw-r-----. 1 root unbound 2459 Sep 8 21:17 unbound_control.key -rw-r-----. 1 root unbound 1330 Sep 8 21:17 unbound_control.pem -rw-r-----. 1 root unbound 2459 Sep 8 21:17 unbound_server.key -rw-r-----. 1 root unbound 1318 Sep 8 21:17 unbound_server.pem
Also, the trust anchors in the file '/var/lib/unbound/root.key' have been checked/updated:
; autotrust trust anchor file ;;id: . 1 ;;last_queried: 1536434236 ;;Sat Sep 8 21:17:16 2018 ;;last_success: 1536434236 ;;Sat Sep 8 21:17:16 2018 ;;next_probe_time: 1536474633 ;;Sun Sep 9 08:30:33 2018 ;;query_failed: 0 ;;query_interval: 43200 ;;retry_time: 8640 . 98799 IN DNSKEY 257 3 8 AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxRixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbRd2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da+sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzuDWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;{id = 20326 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1536235443 ;;Thu Sep 6 14:04:03 2018 . 98799 IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0= ;{id = 19036 (ksk), size = 2048b} ;;state=2 [ VALID ] ;;count=0 ;;lastchange=1536235443 ;;Thu Sep 6 14:04:03 2018
14 Correct performance
There are various ways to check that the Unbound daemon is contactable and working properly. Our preference is to use 'dig' to see whether our set-up can be contacted and is validating independently:
[root@localhost ~]# dig @localhost example.nl +dnssec +multi ; <<>> DiG 9.9.4-RedHat-9.9.4-37.el7 <<>> @localhost example.nl +dnssec +multi ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 60799 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;example.nl. IN A ;; ANSWER SECTION: example.nl. 3600 IN A 94.198.159.35 example.nl. 3600 IN RRSIG A 5 2 3600 ( 20180930184802 20180831175708 15516 example.nl. lPbrdO4oHqwhuAcSMNY1fMAMdLLsPuoUmQKTF1UEk+cL biFgYqrDixiinHIxp7D8AJdm7IHJ/KbxcqS9DSpHJUY9 Xx7lhDjobPkz1OnZw9Y9lSYfCowPBm0aIFwZL14DQxEO Ybm2g+vVf8gU7S2T/JVN3Ijx4ZwaCHik2sf0ReM= ) ;; Query time: 818 msec ;; SERVER: ::1#53(::1) ;; WHEN: Sat Sep 08 21:35:13 CEST 2018 ;; MSG SIZE rcvd: 225
By setting the DO flag (DNSSEC OK) using '+dnssec', we send a query in which validation is also requested. The response should contain the AD (Authentic Data) flag, indicating that the resolver has validated the data for us. The response will include both the Resource Record set (RRset) and the digital signature itself (the RRSIG record).
Unbound supports an alternative to 'dig', namely 'unbound-host'. The 'unbound-host' equivalent of the query presented above would be as follows:
[root@localhost ~]# unbound-host -vDr example.nl example.nl has address 94.198.159.35 (secure) example.nl has IPv6 address 2a00:d78:0:712:94:198:159:35 (secure) example.nl mail is handled by 0 . (secure)
However, it is important to note that, if you don't include the 'D' option (check DNSSEC), the result will always be insecure (although it doesn't have to be).
Unfortunately, the 'unbound-host' command doesn't support specification of the DNS server to be used (whereas you can do that with the 'dig' '@' option).
15 Unbound configuration options
The configuration file '/etc/unbound/unbound.conf' consists of name-value pairs, divided into various sections. The file supplied with the Unbound package for RHEL/CentOS/Fedora serves as a good starting point for a standard set-up. As you will see if you look at the file, default values are used in most cases. The configuration as supplied includes a lot of explanatory material, but few enabled options.
The main (server) configuration options are set out in the table below.
Option | Details |
---|---|
server: | |
num-threads: <number> | Number of threads to be started |
num-queries-per-thread: <number> | Maximum number of queries per thread |
port: <port number> (default: 53) | Number of the port to be used for access |
interface: <IP address> (default: localhost) | Network interface to be used for access |
outgoing-interface: <IP address/range> | Network interface to be used for outgoing queries |
outgoing-range: <number of ports> | Number of ports to be opened per thread |
outgoing-port-permit: <port number/range> | Series of outgoing ports to be used |
outgoing-port-avoid: <port number/range> | Series of outgoing ports NOT to be used |
cache-max-ttl: <seconds> (default: 1 day) | Maximum TTL for items in the cache |
cache-min-ttl: <seconds> | Minimum TTL for items in the cache (default: 0; the TTL of the upstream DNS server is used) |
cache-max-negative-ttl: <seconds> | Maximum TTL for NXDOMAIN entries in cache |
do-ip4: <yes|no> (default: yes) | Use IPv4 for service and own queries |
do-ip6: <yes|no> (default: yes) | Use IPv6 for service and own queries |
prefer-ip6: <yes|no> (default: no) | Use IPv6 wherever possible |
do-daemonize: <yes|no> (default: yes) | start unbound als daemon (default: yes) |
access-control: <IP netblock> <action> | Access control for service; per address block; block, permit, recursive/non-recursive (default: localhost may do everything); access control settings can be defined in groups (e.g. for various local zones) using tags, which are then linked to actions |
chroot: <directory> | The complete path to the Unbound configuration file; required only if unbound is running in a chroot environment |
username: <username> (default: unbound) | Reduce user rights after start-up daemon |
directory: <directory> | Working directory |
pidfile: <path> | Location of the PID file |
trust-anchor-signaling: <yes|no> | Send key IDs of active trust anchors in accordance with RFC 8145 |
trust-anchor-file: <path> | File(s) with static trust anchors (as DS/DNSKEY records) |
trust-anchor: <key record> | Static trust anchor (as DS/DNSKEY record) |
trusted-keys-file: <path> | File(s) with static trust anchors (in BIND 'trusted-keys' format) ("/etc/unbound/keys.d/*.key") |
auto-trust-anchor-file: <path> | Storage location for the (dynamic) trust anchors (obtained according to RFC 5011) ("/var/lib/unbound/root.key") |
domain-insecure: <domain> | Negative trust anchor (domain is treated as insecure) |
add-holddown: <seconds> | Delay before a new public key (obtained according to RFC 5011) may be used as a trust anchor |
del-holddown: <seconds> | Delay before deletion of a new public key (obtained according to RFC 5011) that has ceased to be visible |
keep-missing: <seconds> | Delay before deletion of a trust anchor (obtained according to RFC 5011) that has ceased to be visible |
remote-control: | |
control-enable: <yes|no> | Enable the management portal for 'unbound-control' |
control-interface: <IP address> | Network interface via which the management portal is to be made available |
control-port: <port number> (default: 8953) | Number of the port on which the management portal is to be made available |
server-key-file: <path> | Locations of the certificates and keys for Unbound and 'unbound-control' |
server-cert-file: <path> | |
control-key-file: <path> | |
control-cert-file: <path> |
The Unbound configuration contains dozens of options in addition to those listed above. For example, you can tweak the settings for the buffers and caches to optimise the performance of your set-up (speed, scalability and efficiency). There are also options for logging and a variety of protocol-specific switches. Interested readers are advised to refer to the man page for unbound.conf and the explanatory material in the configuration file itself.
Although all options are summarised and provided with comments in the '/etc/unbound/unbound.conf' file itself, the fine-tuning settings in particular are quite opaque for non-specialists. For that reason, Unbound has sometimes been described as more of an intellectual exercise than a practical DNS resolver. However, with the standard settings and defaults working properly, Unbound can be quickly and easily deployed as a validating resolver for large installations or standalone computers.
The configuration file can be verified using the command 'unbound-checkconf'. That command is also automatically run (prior to) start-up of the 'unbound.service'.
16 Focus points for deployment
Unbound supports chroot (via the 'chroot' configuration option), but you have to set up the environment yourself. A chroot version of BIND named is included as a standard package (bind-chroot) in all RHEL/CentOS/Fedora repositories.
Unbound normally does its own recursive resolving and validation from the root. However, you can also configure Unbound to use other caching resolvers for Forward Zones (for the DNS records, not for the validation itself). If you want that setting to apply to all queries, you need to specify a 'forward-host' or 'forward-addr' for the root zone.
Stub Zones are similar to Forward Zones, but point to (local) authoritative servers, for which validation is not required.
Similarly, you can specify Authority Zones and refer to a zone file (or master server), for which Unbound then acts as an authoritative server.
A further, related possibility is to define specific records for certain zones by means of 'local-zone' and 'local-data' statements, and to link various actions to them.
Although such special zones can be specified in the configuration file itself, the (include) directory '/etc/unbound/local.d/' is the best location.
17 Network configuration
By default, Unbound listens only to the loopback interface (localhost), making the resolver directly deployable on a PC. If you want the daemon to listen to all interfaces, you need to include the following statements in the configuration:
server: interface: 0.0.0.0 interface: ::0
If the Unbound installation will be serving only the local network, the configuration will be something like the following:
server: interface: 192.0.2.5 interface: 2001:db8::2:5
With that configuration, unwanted/unexpected traffic can be blocked as follows:
server: access-control: 192.0.2.0/24 allow access-control: 2001:db8::/64 allow
The interface for the management portal is configured in a similar way:
remote-control: control-enable: yes control-interface: 127.0.0.1 control-interface: ::1 control-port: 8953
The Unix permissions on the certificate and key files can then be used to regulate access (client and server use the same files).
18 Firewall
All that remains is then to open the firewall for port 53 (TCP and UDP). On RHEL/CentOS/Fedora, the easiest way to do that is to open the Firewall Configuration: System -> Administration -> Firewall, and then tick the Service 'dns' for the Zones for which you want this system to act as the DNS server.
![](http://images.ctfassets.net/yj8364fopk6s/z-s_EkwRV8C-o3BhKKAogw/95a234e34012f2207cb5235c431fdfce/FirewallConfiguration-dns1a.png?w=900&fl=progressive)
When doing so, don't forget to save your configuration to Permanent (as opposed to Runtime only).
19 Statistics
You can compile statistics for your Unbound setup using Munin or Cacti. Use the following link for configuration information. A ready-to-use package ('unbound-munin') is part of the Fedora-repository. For RHEL/CentOS version 7, a package is available from the Ghettoforge Plus and Psychotic Ninja Plus repositories mentioned earlier.
Part II — DNSSEC-Trigger
So far, we have focused mainly on the configuration of Unbound as a general-purpose validating resolver in a static environment — in other words, as a DNS server in a local network or as an operationally independent, validating replacement for the stub resolver on a server or fixed workstation. The remainder of this article is probably less relevant to network and system operators. In this final part of the article, we consider DNSSEC-Trigger, which can reconfigure Unbound automatically (via the management portal) whenever the system's network settings change. That enables Unbound to be used for end-to-end validation on a laptop that connects to a variety of Wi-Fi networks.
Like Unbound, DNSSEC-Trigger has been developed as open-source software by NLnet Labs. Packages are available for Linux, Windows and macOS. The packages for the latter two operating systems include Unbound as standard.
For the RHEL/CentOS distributions, only older versions of DNSSEC-Trigger are immediately available: release 0.11 for RHEL/CentOS 7 and 0.10 for RHEL/CentOS 6 (from the EPEL repository). Fedora versions 27, 28 and 29 are all provided with release 0.15. At the time of writing, release 0.16 is the most recent version, but has yet to be added to the above-mentioned repositories.
20 How it works
DNSSEC-Trigger has been developed specifically to enable the Unbound validator to operate without interruption. That involves the 'dnssec-triggerd' daemon making appropriate modifications to the Unbound configuration on the fly (by means of the 'unbound-control' command) whenever the network configuration changes.
To that end, DNSSEC-Trigger performs successive tests (probes) via the local host, until a functional — i.e. validating — configuration is established:
DNSSEC-Trigger first tries to use the (caching) resolvers supplied by DHCP. When doing so, DNSSEC-Trigger does not rely on the AD flags sent with the DNS responses, but validates the responses and associated signatures itself. As a result, you have end-to-end validation, but you also have the benefit of a shared external cache.
If that attempt doesn't work, Unbound is set up to perform the entire recursion from the root via the authoritative name servers itself.
If outgoing communication via port 53 proves impossible — typically because the system in question is running behind a firewall — Unbound sends its DNS queries to a public DNS service using port 80.
If that doesn't work either, the same thing is attempted using HTTPS on port 443.
In that case, success implies DNSSEC-Trigger being able to reach a particular web page. The default page specified in the configuration is one maintained by NLnet Labs. Similarly, the public DNS service accessed on ports 80 and 443 is, by default, a service that is provided by NLnet Labs (and, of course, based on Unbound).
If DNSSEC-Trigger cannot reach the special web page, the user is asked whether a redirect log-in is required to activate the network. Redirect set-ups are often used for public hotspots, with the user having to see an advert or accept the network's conditions of use before accessing the internet. If that isn't the problem either, DNSSEC-Trigger gives up and, in a pop-up, invites the user to temporarily disable DNSSEC validation.
21 Structure
![](http://images.ctfassets.net/yj8364fopk6s/NNmLZUHSV4a0kN4jCxvcnA/54a8fb37d10c90254394503c0b746ba3/DNSSECtrigger-applet1.png?w=400&fl=progressive)
Structurally, DNSSEC-Trigger is similar to Unbound. The RPM package installs three systemd services on your system:
dnssec-triggerd-resolvconf-handle.service: This service invokes a script that saves the old '/etc/resolv.conf' file when DNSSEC-Trigger is started up, and reinstates it when the program is shut down.
dnssec-triggerd-keygen.service: Like the 'unbound-keygen.service', the dnssec-triggerd Control Key And Certificate Generator is called up once, in this case to carry out the 'dnssec-trigger-control-setup' command. This script creates four files with certificates and keys in the directory '/etc/dnssec-trigger/'. They are then used to access the 'dnssec-triggerd' daemon using the 'dnssec-trigger-control' command.
dnssec-triggerd.service: The DNSSEC-Trigger daemon 'dnssec-triggerd' is started in response to start-up of the Unbound service.
The connection between DNSSEC-Trigger and NetworkManager is realised by the script '/etc/NetworkManager/dispatcher.d/01-dnssec-trigger-hook'. The script is run every time a network event is detected.
Although DNSSEC-Trigger can be managed from the command line using the 'dnssec-trigger-control' command, it is easier to do so by means of the status icon (the 'dnssec-trigger-panel' applet). The applet connects to 'dnssec-trigger-control' via a TLS-secured connection. The DNSSEC-Trigger status icon is an anchor, which can be found in the Notification Area.
22 Installation and start-up
Where installation and configuration are concerned, DNSSEC-Trigger is again similar to Unbound. The commands for installing, starting up, maintaining and checking DNSSEC-Trigger are as follows:
yum/dnf install dnssec-trigger
systemctl start dnssec-triggerd.service dnssec-triggerd-resolvconf-handle.service dnssec-triggerd-keygen.service
[root@localhost ~]# systemctl enable dnssec-triggerd.service dnssec-triggerd-resolvconf-handle.service dnssec-triggerd-keygen.service Created symlink from /etc/systemd/system/dnssec-trigger.service to /usr/lib/systemd/system/dnssec-triggerd.service. Created symlink from /etc/systemd/system/multi-user.target.wants/dnssec-triggerd.service to /usr/lib/systemd/system/dnssec-triggerd.service. Created symlink from /etc/systemd/system/multi-user.target.wants/dnssec-triggerd-keygen.service to /usr/lib/systemd/system/dnssec-triggerd-keygen.service.
[root@localhost ~]# systemctl status dnssec-triggerd.service dnssec-triggerd-keygen.service dnssec-triggerd-resolvconf-handle.service ● dnssec-triggerd.service - Reconfigure local DNS(SEC) resolver on network change Loaded: loaded (/usr/lib/systemd/system/dnssec-triggerd.service; enabled; vendor preset: disabled) Active: active (running) since Thu 2018-09-20 13:46:21 CEST; 6s ago Process: 8615 ExecStopPost=/usr/bin/chattr -i /etc/resolv.conf (code=exited, status=0/SUCCESS) Process: 8850 ExecStartPost=/etc/NetworkManager/dispatcher.d/01-dnssec-trigger-hook (code=exited, status=0/SUCCESS) Main PID: 8849 (dnssec-triggerd) CGroup: /system.slice/dnssec-triggerd.service └─8849 /usr/sbin/dnssec-triggerd -d Sep 20 13:46:21 localhost.localdomain 01-dnssec-trigger-hook[8850]: Global forwarders added: 127.0.0.1 1.1.1.1 1.0.0.1...001 Sep 20 13:46:21 localhost.localdomain systemd[1]: Started Reconfigure local DNS(SEC) resolver on network change. Sep 20 13:46:22 localhost.localdomain dnssec-triggerd[8849]: ok ● dnssec-triggerd-keygen.service - dnssec-triggerd Control Key And Certificate Generator Loaded: loaded (/usr/lib/systemd/system/dnssec-triggerd-keygen.service; enabled; vendor preset: disabled) Active: active (exited) since Thu 2018-09-20 13:46:21 CEST; 7s ago Process: 8847 ExecStart=/sbin/restorecon /etc/dnssec-trigger/* (code=exited, status=0/SUCCESS) Process: 8826 ExecStart=/usr/sbin/dnssec-trigger-control-setup -d /etc/dnssec-trigger/ (code=exited, status=0/SUCCESS) Main PID: 8847 (code=exited, status=0/SUCCESS) CGroup: /system.slice/dnssec-triggerd-keygen.service Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: subject=/CN=dnssec-trigger-control Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: Getting CA Private Key Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: unable to write 'random state' Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: Setup success. Certificates created. Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: run this script again with -i to: Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: - enable remote-control in unbound.conf Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: - start unbound-control-setup Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: - add root trust anchor to unbound.conf Sep 20 13:46:21 localhost.localdomain dnssec-trigger-control-setup[8826]: if you have not done this already Sep 20 13:46:21 localhost.localdomain systemd[1]: Started dnssec-triggerd Control Key And Certificate Generator. ● dnssec-triggerd-resolvconf-handle.service - Backups and restores /etc/resolv.conf after dnssec-trigger starts/stops Loaded: loaded (/usr/lib/systemd/system/dnssec-triggerd-resolvconf-handle.service; static; vendor preset: disabled) Active: active (exited) since Thu 2018-09-20 13:46:21 CEST; 7s ago Process: 8617 ExecStop=/usr/libexec/dnssec-triggerd-resolvconf-handle.sh restore (code=exited, status=0/SUCCESS) Process: 8827 ExecStart=/usr/libexec/dnssec-triggerd-resolvconf-handle.sh backup (code=exited, status=0/SUCCESS) Main PID: 8827 (code=exited, status=0/SUCCESS) CGroup: /system.slice/dnssec-triggerd-resolvconf-handle.service Sep 20 13:46:21 localhost.localdomain systemd[1]: Starting Backups and restores /etc/resolv.conf after dnssec-trigge...ps... Sep 20 13:46:21 localhost.localdomain systemd[1]: Started Backups and restores /etc/resolv.conf after dnssec-trigger...tops. Hint: Some lines were ellipsized, use -l to show in full.
23 Results
Once the commands set out above have been executed, the 'dnssec-triggerd-keygen.service' will have generated new keys and certificates for 'dnssec-trigger-control' in the directory '/etc/dnssec-trigger/':
-rw-r--r--. 1 root root 1277 Sep 20 13:46 dnssec_trigger_control.key -rw-r--r--. 1 root root 822 Sep 20 13:46 dnssec_trigger_control.pem -rw-r-----. 1 root root 1281 Sep 20 13:46 dnssec_trigger_server.key -rw-r--r--. 1 root root 810 Sep 20 13:46 dnssec_trigger_server.pem
The content of the file '/etc/resolv.conf' previously generated by the NetworkManager has now been replaced by a DNSSEC-Trigger configuration:
# Generated by dnssec-trigger 0.11 nameserver 127.0.0.1
We can also see that the DNSSEC-Trigger daemon is indeed active:
[root@localhost ~]# ps aux |grep dnssec root 30380 0.0 0.0 41156 3548 ? Ss 22:37 0:00 /usr/sbin/dnssec-triggerd -d
Finally, the following tells us that, for both IPv4 as IPv6, DNSSEC-Trigger is listening to TCP port 8955, the management portal for 'dnssec-trigger-control':
[root@localhost dnssec-trigger]# ss -nlput | grep dnssec tcp LISTEN 0 15 127.0.0.1:8955 *:* users:(("dnssec-triggerd",pid=8849,fd=3)) tcp LISTEN 0 15 ::1:8955 :::* users:(("dnssec-triggerd",pid=8849,fd=3))
24 Configuration files
The configuration of DNSSEC-Trigger is contained in two files. The first is '/etc/dnssec.conf', which specifies whether the Forward Zones mentioned above should be validated. It can be used to set up a local caching resolver that makes use of an upstream resolver not only for RRsets and signatures, but also for validation.
The file '/etc/dnssec-trigger/dnssec-trigger.conf' contains the settings for the DNSSEC-Trigger daemon, which relate to aspects such as logging, the PID file, the chroot directory, the paths for various scripts and other configuration files, and the probes. The file is similar to the Unbound configuration file in its format, but is much smaller.
In both cases, the default settings are a good starting point for immediate use.
Conclusions
Unbound is a validating resolver with an immediately usable default configuration, at least for Linux. The software's main options are considered in this article. Information about Unbound's other features — of which there are many — are given in the detailed documentation on the website. Various installation and set-up manuals are also available there. Red Hat has a more generic documentation page with information about DNSSEC and the installation/configuration of Unbound and DNSSEC-Trigger.
In combination with DNSSEC-Trigger, Unbound also serves as a very straightforward solution for end-to-end validation for mobile users.