Configure Unbound
unbound(8) is a caching nameserver that comes as part of OpenBSD base. You can use this to provide DNS services to users on your network. There are several potential benefits:
- Helps users bypass political censorship of domains
- Speeds up DNS lookup for your local services using cached records
- Provides DNS privacy and security for users
Comparing Unwind with Unbound
Although unwind is very convenient and easy to configure, unwind(8) is not able to provide public DNS services for your users. For this reason, we recommend configuring unbound(8) for any public system (ie, anything beyond your personal workstation or laptop).
Downloading the trust anchor
We will want to configure unbound(8) so it can use DNSSEC and validate domain records. In order to do this, you will need to download a trust anchor. We will use unbound-anchor(8):
# unbound-anchor -a "/var/unbound/db/root.key"
This will download the root anchor to the file /var/unbound/db/root.key
.
Configuring Unbound
Below, we provide a simple, sample
unbound.conf(5). By default,
unbound(8) runs inside a chroot in
/var/unbound/
, so we place this conf file in
/var/unbound/etc/unbound.conf
:
server: interface: 127.0.0.1 # listen on localhost interface: ::1 # listen on localhost access-control: 0.0.0.0/0 refuse # drop all users by default access-control: 127.0.0.0/8 allow # allow localhost to use unbound access-control: ::0/0 refuse # drop all IPv6 users by default access-control: ::1 allow # allow IPv6 localhost to use unbound hide-identity: yes hide-version: yes auto-trust-anchor-file: "/var/unbound/db/root.key" val-log-level: 2 aggressive-nsec: yes remote-control: control-enable: yes control-interface: /var/run/unbound.sock
In this configuration, we define 2 interface
directives. This tells
unbound(8) to listen on the IP addresses
127.0.0.1 and ::1 (localhost on IPv4 and IPv6, respectively). By default, port
53
is used.
The next block of directives is for access-control
. This controls which
clients are allowed to query your caching nameserver. Limiting which clients
can access your nameserver is important to prevent DNS amplification attacks.
We drop all queries from all IPs by default, and send the DNS rcode REFUSED.
Then, we whitelist localhost (127.0.0.0/8
and ::1
) so that only our
system can query unbound. You can whitelist any additional users, such as users
from your local internal network or other trusted IP range.
We then hide identity and version queries.
We specify the file for the trust anchor for one zone. Make sure unbound has
write permission for this file (it does by default). Setting
val-log-level: 2
shows plenty of debug information for failed DNSSEC
validation. We turn on aggressive nsec to use DNSSEC NSEC to get NXDOMAIN
results faster.
Finally, we enable remote control with the unbound-control utility.
Built-in nameservers
In our configuration, we use the default built-in list of authoritative nameservers for the root zone (.) (root hints). When unbound receives a DNS query, it will ask the root nameservers about the top-level domain's (TLD's) nameserver, and then ask that nameserver for the domain. This recursive lookup will continue until an answer is found (or an NXDOMAIN is returned for no answer). This answer will be cached according to the time-to-live (TTL) of the record.
In order to avoid using the default root hints, you can specify forwarding servers. A sample guide for OpenNIC is provided.
Check for valid configuration
Before starting unbound(8), use unbound-checkconf(8) to ensure the configuration is valid:
# unbound-checkconf unbound-checkconf: no errors in /var/unbound/etc/unbound.conf
Note: unbound-checkconf(8) is only a basic sanity check. It does not guarantee your configuration is meaningful and valid.
Using unbound
To start unbound:
# rcctl enable unbound # rcctl start unbound
In order to have your system query unbound, you must make sure that
resolv.conf is using 127.0.0.1 as the default nameserver. Your
system checks /etc/resolv.conf
to determine which server to query. It
should look like the following:
nameserver 127.0.0.1 lookup file bind
Importantly, the one (and only) nameserver should be 127.0.0.1
.
If you have some other nameserver in resolv.conf(5), your system may not actually be using unbound(8) but some other nameserver. If this is the case, you may need to edit resolv.conf or configure the nameserver that resolvd uses.
Testing unbound
Query unbound with dig, nslookup?, or host to confirm it is working:
$ dig @127.0.0.1 google.com ; <<>> dig 9.10.8-P1 <<>> @127.0.0.1 google.com ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 53037 ;; flags: qr rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ;; QUESTION SECTION: ;google.com. IN A ;; ANSWER SECTION: google.com. 300 IN A 142.251.167.101 google.com. 300 IN A 142.251.167.102 google.com. 300 IN A 142.251.167.100 google.com. 300 IN A 142.251.167.138 google.com. 300 IN A 142.251.167.113 google.com. 300 IN A 142.251.167.139 ;; Query time: 48 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) ;; WHEN: Tue Dec 10 21:33:07 MST 2024 ;; MSG SIZE rcvd: 135
Troubleshooting
No Trust Anchor
If you see this error when running unbound-checkconf(8):
# unbound-checkconf /var/unbound/db/root.key: No such file or directory [1733812265] unbound-checkconf[86538:0] fatal error: auto-trust-anchor-file: "/var/unbound/db/root.key" does not exist in chrootdir /var/unbound
The error message indicates that
unbound(8) lacks a trust anchor at
/var/unbound/db/root.key
.
Make sure to run unbound-anchor(8) before starting unbound(8), as documented above.
Address already in use
If unbound is unable to start, run it in debug mode with increased verbosity:
# unbound -dv [1733887244] unbound[77049:0] notice: Start of unbound 1.21.0. [1733887244] unbound[77049:0] error: bind: address already in use [1733887244] unbound[77049:0] fatal error: could not open ports
This error message indicates another process is already bound to the same socket?. fstat? can quickly help you identify the process:
# fstat | grep ':53' _unwind unwind 6934 6* internet dgram udp 127.0.0.1:53 _unwind unwind 6934 7* internet6 dgram udp [::1]:53 _unwind unwind 6934 8* internet stream tcp 0xffff8000008ec070 127.0.0.1:53 _unwind unwind 6934 9* internet6 stream tcp 0xffff8000008ec9e8 [::1]:53
In this case, we forgot to stop unwind before starting unbound. This can be easily fixed:
# rcctl stop unwind unwind(ok) # rcctl start unbound unbound(ok)
Negative cache
If a DNS record fails to validate the first time, this error will get cached:
Mar 8 01:34:41 hostname unbound: [45846:0] info: validation failure <hostname.com. A IN>: key for validation . is marked as invalid because of a previous validation failure <previoushostname.com. A IN>: no DNSKEY rrset for trust anchor . while building chain of trust
The solution is to flush all negative cache with unbound-control:
# unbound-control flush_negative
(Optional) Turn off DNSSEC
You may optionally turn off DNSSEC if you are using forwarders that do not support DNSSEC.
In /var/unbound/etc/unbound.conf
, comment out these lines:
# auto-trust-anchor-file: "/var/unbound/db/root.key" # val-log-level: 2 # aggressive-nsec: no
Some public servers do not support DNSSEC, and if
unbound(8) is configured to require
DNSSEC, it may fail to build a chain of trust. You can see this in logs (by
default /var/log/daemon
):
Dec 10 22:58:56 hostname unbound: [65464:0] info: failed to prime trust anchor -- DNSKEY rrset is not secure . DNSKEY IN Dec 10 22:58:56 hostname unbound: [65464:0] info: validator operate: query example.com. A IN Dec 10 22:58:56 hostname unbound: [65464:0] info: Could not establish a chain of trust to keys for . DNSKEY IN Dec 10 22:58:56 hostname unbound: [65464:0] info: validation failure <example.com. A IN>: signature for expected key and algorithm missing from 208.99.44.76 for trust anchor . while building chain of trust
Stale hosts file
If you change your host's IP address, the hosts(5) file may be out of date. Depending on your configuration of resolv.conf, /etc/hosts may be intercepting name lookup and giving you an old IP address.
Double check /etc/hosts to ensure that it is not using an incorrect IP address.