DNSSEC validation using Unbound and DNSSEC-Trigger

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.

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.

NetworkManager1

NetworkManager-DNS1

NetworkManager-DNS2a

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.

NetworkManager3

NetworkManager-DNS3a

NetworkManager-DNS4a
  # 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. 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 Root KSK rollover

Correct installation of the current trust anchors warrants particular attention at the present time. The reason being that the root zone'sKSK pair has recently been rolled over. In other words, ICANN, the root zone's administrator, has replaced the previous (very first) cryptographic key pair at the base of the entire DNSSEC infrastructure (KSK-2010) with a new key pair (KSK-2017).

Originally, the rollover was supposed to take place a year ago. However, shortly before the scheduled rollover date, ICANN postponed it, due to fears that many internet users would encounter problems if the rollover went ahead on the planned day. The rollover proper was rescheduled for 11 October 2018, exactly a year after the original date.

10 KSK-2017 trust anchor

The KSK-2017 trust anchor 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 included 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.

Although Unbound has supported RFC 5011 since version 1.4.0, you can no longer use the RFC 5011 mechanism to install the KSK-2017 trust anchor. That is because, for security reasons, the protocol requires a new public key to be retained (and visible) for thirty days before being adopted as an active trust anchor (see the 'add-holddown' option).

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.

11 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.

12 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.

13 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.

14 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

15 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).

16 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'.

17 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.

18 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).

19 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.

FirewallConfiguration-dns1a

When doing so, don't forget to save your configuration to Permanent (as opposed to Runtime only).

20 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.

21 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.

22 Structure

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.

DNSSECtrigger-applet1

23 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.

24 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))

25 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.

Comments

  • Tuesday 25 September 2018

    .nl domain name

    ACM becomes first government agency that can order SIDN to take down domain names

    Thumb-ACM

    We welcome the change

    Read more
  • Wednesday 25 April 2018

    DNSSEC

    Two new DNSSEC-validating DNS services launched

    Thumb+DNSSEC+news

    In recent weeks, two new DNS services for the general public have been launched, and both support DNSSEC validation.

    Read more
  • Monday 25 March 2019

    About SIDN

    Register for IDnext 2019

    Thumb-logo-IDnext

    The number-one digital identities event

    Read more

Sorry

Your browser is too old to optimally experience this website. Upgrade your browser to improve your experience.