HOWTO setup your own caching nameserver using Unbound
The best alternative for setting up a caching nameserver on your LAN or personal machine today is Unbound. It's written with that purpose in mind which makes it faster and simpler to setup than more full-featured alternatives such as named (called "BIND") or KNOT-resolver and such.
Basic installation and configuration[edit]
Start off by installing unbound with dnf install unbound
or apt-get install unbound
depending on distribution. On Arch you also have to manually install the expat
package.
Now it's time to make a configuration file for it. Here is a most basic example of what you need:
access-control: 10.0.0.0/8 allow access-control: 192.168.0.0/16 allow access-control: 127.0.0.0/8 allow access-control: ::1/128 allow interface: :: interface: 0.0.0.0 aggressive-nsec: yes cache-max-ttl: 14400 cache-min-ttl: 300 hide-identity: yes hide-version: yes minimal-responses: yes prefetch: yes qname-minimisation: yes rrset-roundrobin: yes use-caps-for-id: yes verbosity: 0
The first few lines define who can query the server with access-control
directives. They we define what to listen on with interface
. The above example is very liberal and makes unbound listen on all interfaces.
Refer to the unbound.conf manual page for further details.
It is a good idea to run unbound-checkconf
to check if it is valid once you are happy with your configuration.
Now you need to ensure that systemd-resolved
is not occupying the DNS port. You can do this by giving it the following configuration file:
[Resolve] DNS=127.0.0.1 FallbackDNS=1.0.0.1 MulticastDNS=no DNSStubListener=no
The DNSStubListener
directive is essential to ensure it does not listen for DNS queries. You may actually want MulticastDNS
if you do not use avahi-daemon
for multicast-DNS purposes.
Restart systemd-resolved with systemctl restart systemd-resolved.service
or stop it with code>systemctl stop systemd-resolved.service - Do be aware that it doesn't really matter if you stop it or re-start it. systemd will happily start it when applications make API requests for it even if you masked it.
Then run
systemctl start unbound.service
to start it and
systemctl enable unbound.service
to make it start on every boot.
Validating your queries[edit]
Once you have a basic unbound setup you will want to add validation. First, install the bind-utils
package and copy the /etc/trusted-key.key
to where unbound can read it with cp /etc/trusted-key.key /etc/unbound
Now add this line to your unbound.conf:
trust-anchor-file: "/etc/unbound/trusted-key.key"
And restart unbound. Now you should test using this command:
unbound-host -C /etc/unbound/unbound.conf -v sigok.verteiltesysteme.net
which should output
sigok.verteiltesysteme.net has address 134.91.78.139 (secure) sigok.verteiltesysteme.net has IPv6 address 2001:638:501:8efc::139 (secure) sigok.verteiltesysteme.net has no mail handler record (secure)
and NOT
sigok.verteiltesysteme.net has address 134.91.78.139 (insecure) sigok.verteiltesysteme.net has IPv6 address 2001:638:501:8efc::139 (insecure) sigok.verteiltesysteme.net has no mail handler record (insecure)
If get "secure" as in the first output then you're good, if you get "insecure" as in the last example then it is not working.
Maintaining a root "hints" file[edit]
A DNS query will first go to the DNS root and then the nameservers responsible for the top domain (.com/.org/etc) and then the server which is responsible for the domain you are querying. Thus; you need to know where to start. Unbound comes with a built-in list of root servers. That's great, but if your Unbound version gets wildly outdated and the root servers change those hints may no longer apply. It is considered "good practice" to keep a updated local copy of the hints. A simple way to do this is to create a good old cron
file:
#!/bin/bash wget -q https://www.internic.net/domain/named.root -O /tmp/root.hints if grep -q ROOT-SERVERS /tmp/root.hints ;then mv -f /tmp/root.hints /etc/unbound/root.hints ; chmod a+r /etc/unbound/root.hints fi
make the file executable
chmod a+x /etc/cron.monthly/update-unbound-hints.sh
and run it
/etc/cron.monthly/update-unbound-hints.sh
Then add the following line to /etc/unbound/unbound.conf
:
root-hints: "/etc/unbound/root.hints
Forwarding queries[edit]
Asking a botnet DNS server ran by either CloudFlare or Google may actually be faster than doing your own recursive lookups depending on your network, location and distance to those.
If you would like to forward DNS queries to another nameserver on your LAN you need to add
forward-zone: name: "." forward-addr: 192.168.0.1@53
That is good enough on your LAN but you may want to add more if your DNS queries will be leaving your local network and going to the botnet. For this you would want to add the following:
tls-upstream: yes tls-cert-bundle: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem forward-zone: name: "." # Cloudflare DNS forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com forward-addr: 1.1.1.1@853#cloudflare-dns.com forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com forward-addr: 1.0.0.1@853#cloudflare-dns.com
Here we tell unbound to use TLS for queries leaving the LAN with tls-upstream: yes
. Then we tell it what certificate package to check against with tls-cert-bundle
. The file referred to should be installed by the ca-certificates
package which you should have gotten installed as a dependency for unbound. Next we specify a set of forward-addr
and here there is one detail one might get wrong: The names of those servers after the #
are not comments. Those are used for the TLS certificate validation and must be present.
Combining the above a complete unbound configuration file which forwards DNS queries to the CloudFlare botnet looks like this:
server: access-control: 10.0.0.0/8 allow access-control: 192.168.0.0/16 allow access-control: 127.0.0.0/8 allow access-control: ::1/128 allow interface: :: interface: 0.0.0.0 aggressive-nsec: yes cache-max-ttl: 14400 cache-min-ttl: 300 hide-identity: yes hide-version: yes minimal-responses: yes prefetch: yes qname-minimisation: yes rrset-roundrobin: yes use-caps-for-id: yes verbosity: 0 tls-upstream: yes tls-cert-bundle: /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem forward-zone: name: "." # Cloudflare DNS forward-addr: 2606:4700:4700::1111@853#cloudflare-dns.com forward-addr: 1.1.1.1@853#cloudflare-dns.com forward-addr: 2606:4700:4700::1001@853#cloudflare-dns.com forward-addr: 1.0.0.1@853#cloudflare-dns.com
Ensuring there is no systemd or networkmanager interference[edit]
This section is not for everyone. You may want to ensure that a local or LAN Unbound nameserver is always used regardless of systemd or networkmanager opinion. To accomplish this you first need to tell networkmanager to not interfere with your DNS settings, ever. This can be accomplished by changing the configuration file /etc/NetworkManager/NetworkManager.conf
to only contain the following content:
[main] dns=none systemd-resolved=false [logging]
Now NetworkManager will not touch or overwrite or destroy your /etc/resolv.conf
. By default that file is now a symbolic link on most distributions. You can remove that link and demand that only localhost, where you now have unbound, is (ab)used with these fine commands:
rm -f /etc/resolv.conf
echo 'nameserver 127.0.0.1' > /etc/resolv.conf
Note that you are now relying entirely on unbound running on localhost. That is fine if you are starting Unbound on boot and you want it to handle DNS regardless of what network you are using.
systemd-resolved will however be a pain to configure in this situation.
Enable comment auto-refresher