Configure

Unbound is a caching DNS resolver that comes as a part of OpenBSD base. You can use this to provide faster as well as more secure DNS lookup for the users on your network.

Please read through the unbound(8), unbound.conf(5), unbound-checkconf(8) and nsd(8) man pages.

Here's a sample /var/unbound/etc/unbound.conf:

server:
        interface: 127.0.0.1 # listen on localhost
        interface: ::1
        #interface: 10.0.0.1 # provide DNS for users on the IPSec internal network
        #do-ip6: no

        access-control: 0.0.0.0/0 refuse # block all users by default
        access-control: 10.0.0.0/8 allow # allow users on the internal network to use unbound
        access-control: 127.0.0.0/8 allow # allow localhost to use unbound
        access-control: ::0/0 refuse # block all IPv6 users by default
        access-control: ::1 allow # allow IPv6 localhost to use unbound

        hide-identity: yes
        hide-version: yes

remote-control:
        control-enable: yes
        control-interface: /var/run/unbound.sock

forward-zone:
        name: "."
        forward-addr: 185.117.154.144
        forward-addr: 165.227.40.43
        forward-addr: 217.144.132.169
        forward-addr: 212.237.22.141
        forward-addr: 165.227.108.86

The forward-addr lines indicate which nameserver unbound will query. You can find a list of public servers on OpenNIC.

Tip: To quickly get the IPs from OpenNIC, navigate to servers.opennic.org, click on the OK button at the top, then open up your web browser's javascript console window and run this command:

document.clear();data=document.querySelectorAll("html body div#frame div#view div#srvlist div p");document.body.innerText="forward-addr: 9.9.9.9";data.forEach(line=>{
if (line.childNodes[0].childNodes[1].title == "No logs kept"){
document.body.innerHTML+=`<br>forward-addr: ${line.childNodes[2].childNodes[0].data}`;
}
});

You can also run these command: Attach:unbound.txt

If you need to store local zones, add a block to the "server" section about it:

        local-zone: "localhost." static
        local-data: "localhost. 10800 IN NS localhost."
        local-data: "localhost. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
        local-data: "localhost. 10800 IN A 127.0.0.1"
        local-data: "localhost. 10800 IN AAAA ::1"

To start unbound:

$ doas rcctl enable unbound
$ doas rcctl start unbound

For the computer that runs unbound, you'll want to make sure /etc/resolv.conf uses 127.0.0.1 as the nameserver (that is, you query unbound running on port 53). In /etc/resolv.conf:

nameserver 127.0.0.1
lookup file bind

Check to make sure /etc/resolv.conf.tail does not contain any other name servers except 127.0.0.1. All your nameservers should instead be specified in /var/unbound/etc/unbound.conf.

If the computer running unbound uses DHCP for network configuration, you will want to include this line in /etc/dhclient.conf:

ignore domain-name-servers;

This tells OpenBSD's dhclient to ignore the name server provided by the dhcp server.

If the computer running unbound is also providing a dhcp server for your local network, you will want to add this line inside your /etc/dhcpd.conf blocks:

option domain-name-servers 192.168.1.1;

Here's how to use unbound as a local caching resolver:

Edit /etc/resolv.conf so it queries localhost on port 53:

# Generated by age0 dhclient
nameserver 127.0.0.1
lookup file bind

Inside /var/unbound/etc/unbound.conf, you will see this at the top:

server:
        interface: 127.0.0.1
        interface: ::1

        # override the default "any" address to send queries; if multiple
        # addresses are available, they are used randomly to counter spoofing
        #outgoing-interface: 192.0.2.1
        #outgoing-interface: 2001:db8::53

        access-control: 0.0.0.0/0 refuse
        access-control: 127.0.0.0/8 allow
        access-control: ::0/0 refuse
        access-control: ::1 allow

Make sure you are listening on 127.0.0.1 (for localhost) so that your VPS can query localhost on port 53, and also ::1 on port 53 (for IPv6).

For access control, you want to refuse 0.0.0.0/0 (all IPv4s) but allow 127.0.0.0/8 (everything that originates locally). Again, refuse ::0/0 (all IPv6s) but allow ::1 (localhost).

Put this at the bottom of the file:

forward-zone:
        name: "."                               # use for ALL queries
forward-addr: 163.53.248.170
forward-addr: 103.236.162.119
forward-addr: 192.99.85.244
forward-addr: 31.171.251.118
forward-addr: 51.254.25.115
forward-addr: 46.101.70.183
forward-addr: 45.71.112.70
forward-addr: 87.98.175.85
forward-addr: 185.208.208.141
forward-addr: 89.35.39.64
forward-addr: 87.98.175.85
forward-addr: 172.98.193.42
forward-addr: 111.67.20.8

These are IP addresses for DNS servers which I got from https://servers.opennic.org/. However, the servers change regularly so make sure you update the list.

To start unbound:

$ doas rcctl enable unbound
$ doas rcctl start unbound

To test if unbound is working:

$ dig @127.0.0.1 google.com

You should see something like this:

;; ANSWER SECTION:
google.com.             29      IN      A       172.217.27.142

DNSSEC

In /var/unbound/etc/unbound.conf, there are these two lines:

# auto-trust-anchor-file: "/var/unbound/db/root.key"
# aggressive-nsec: no

Both should be commented to turn off DNSSEC when using forwarders that aren't known to support DNSSEC (the OpenNIC public servers suggested earlier are one example). Otherwise Unbound may refuse to serve answers to DNS queries because it fails to build a chain of trust.

Mar  31 03:27:44 hostname unbound: [26496:0] info: validation failure <example.com. A IN>: signature missing from 198.51.100.163 for trust anchor . while building chain of trust

Stale hostname

If you ever change your host's IP address, /etc/hosts may be intercepting the name lookup and giving you the old IP address.

Make sure there is no entry in the /etc/hosts files that is causing your machine to use the old IP.

Troubleshooting

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

Flush all negative cache.

# unbound-control flush_negative