How to Set Up Local DNS Resolver with Unbound on Ubuntu 22.04
Unbound is free and open-source DNS server software that can be used for validating, recursive, and caching DNS resolvers. It’s a feature-rich DNS server that supports DNS-over-TLS (DoT), DNS-over-HTTPS (DoH), Query Name Minimisation, the Aggressive Use of DNSSEC-Validated Cache, and support for authority zones. Unbound is focused on the privacy and security of DNS, but without sacrificing the speed and performance.
Unbound is primarily developed by NLnet Labs and distributed under the BSD license, and it supports modern features on open standards of DNS Server. Unbound has been rigorously audited, and it can be run on Linux, BSD, and macOS. Unbound is available for most of these OSs and can be installed via the system package manager.
In this tutorial, you will install Unbound on Ubuntu 22.04 server and set it up as a Local DNS Server with some features enabled, such as DNSSEC, DNS cache, local domain names and sub-domains, and also DNS-over-TLS (DoT). You’ll also set up Unbound logging via Rsyslog and logrotate and set up an Ubuntu client machine to verify your Unbound installation.
To complete this tutorial, you must have the following requirements:
- An Ubuntu 22.04 server — This example uses an Ubuntu server with the hostname ‘unbound-server’ and IP address ‘192.168.5.100’.
- A non-root user with sudo/root administrator privileges.
That’s it. You’re now ready to proceed with the Unound installation.
Installing Unbound DNS Server
By default, the Ubuntu repository provides an Unbound package that you can easily install via APT. Before you start Unbound installation, issue the following apt command to update and refresh your Ubuntu package index.
sudo apt update
Now check the details unbound package via the following command.
sudo apt info unbound
At the time of this writing, the default Ubuntu repository provides Unbound 1.13.
Next, install Unbound using the following apt command. When prompted, input y to confirm and press ENTER to proceed.
sudo apt install unbound
Once Unbound is installed, run the below systemctl command to verify Unbound service.
sudo systemctl is-enabled unbound
sudo systemctl status unbound
The output ‘enabled’ confirms that the Unbound is enabled and will start automatically upon the system startup. And the output ‘active (running)’ confirms that the Unbound is running.
Configuring Unbound as Local DNS Server
The default Unbound configuration is located at ‘/etc/unbound/unbound.conf’. In this step, you’ll modify the main Unbound config file ‘/etc/unbound/unbound.conf’ via your preferred editor.
You’ll now learn about the basic configuration of an Unbound DNS server, enabling the DNS cache, setup local domain names and sub-domains, set up Unbound as DNS resolver with DoT (DNS-over-TLS) enabled.
Open the default Unbound config file ‘/etc/unbound/unbound.conf’ using your preferred editor. This example uses nano for editing the config file ‘/etc/unbound/unbound.conf’.
sudo nano /etc/unbound/unbound.conf
Add the following lines to the file. The ‘server’ section lets you set up basic Unbound configurations. In this example, you’ll run Unbound on the local IP address ‘192.168.5.100’ with the default port 53. Also, you’ll set up logging to Syslog messages and disable IPv6. Lastly, you will set up Unbound to recursively query any hostname from the root DNS servers via the ‘root-hints’ file.
#Adding DNS-Over-TLS support
- use-syslog: enable logging to Syslog messages.
- username: run as user unbound, which is the default user.
- directory: the default working directory for Unbound is the ‘/etc/unbound’ directory.
- tls-cert-bundle: Certificates used to authenticate connections made upstream. On Debian-based distribution, the cert file is located at ‘/etc/ssl/certs/ca-certificates.crt’.
- do-ip6: use ‘yes’ to run Unbound with IPv6 or set ‘no’ to disable IPv6.
- interface: network interface or IP address that unbound will be running. You can use an IP address or the interface name such as ‘eth0’. Also, you can run in a specific port by adding a format like this ‘IP-ADDRESS@PORT’.
- port: specify the port that Unbound will be running, and this port will handle the client’s connections. The default DNS port is 53.
- prefetch: set to ‘yes’ to enable prefetching of almost expired message cache entries.
- root-hints: a file that contains root DNS server details. The file ‘/usr/share/dns/root.hints’ is provided by the ‘dns-root-data’ package. And also can download the root-hints file from here ‘https://www.internic.net/domain/named.cache'.
- harden-dnssec-stripped: set it to ‘yes’ to harden against receiving dnssec-stripped data.
Enable DNS Cache
Next, add the following lines to enable the DNS cache query on your Unbound installation.
- cache-max-ttl: TTL or Time To Live for RRSets and messages in DNS cache. The format is in seconds.
- cache-min-ttl: minimal Time To Live for the cache. The default is 0, but you can change this to your flavor such as ‘11000’ seconds. Do not set this for more than 1 hour or you will get into trouble due to stale data.
Unbound Privacy and Security
Now you can add the following lines to set up basic privacy and security for Unbound.
- aggressive-nsec: set to ‘yes’ to enable aggressive NSEC to use the DNSSEC NSEC chain to synthesize NXDOMAIN and other denials. Check the IETF web page about ‘NSEC’ https://www.ietf.org/archive/id/draft-ietf-dnsop-nsec-ttl-00.html.
- hide-identity: set to yes to disable answers from bind queries about id.server or hostname.bind.
- hide-version: set to yes to disable version.server and version.bind queries.
- use-caps-for-id: set to yes to enable the use of ‘0x100-encoded’ in the query to foil spoof attempts.
Define Private Network and Access Control Lists (ACLs)
Next, you need to define your networks’ private-address and the ACLs (Access Control Lists). Be sure to change the local subnet in the below lines with your current network environment.
#control which clients are allowed to make (recursive) queries
access-control: 127.0.0.1/32 allow_snoop
access-control: ::1 allow_snoop
access-control: 127.0.0.0/8 allow
access-control: 192.168.5.0/24 allow
- private-address: define private network subnets on your infrastructure. Only ‘private-domain’ and ‘local-data’ names are allowed to have these private addresses.
- access-control: define access control in which clients are allowed to make (recursive) queries to the Unbound server. The parameter ‘allow’ will enable recursive, while the ‘allow_snoop’ will enable both recursive and non-recursive.
Setup Local Domain
After configuring private address and access control lists, you’ll define your domain name’s local zone. This is very useful, especially if you have multiple self-hosted applications on your local network. You can easily define your domain name or sub-domains and point to the specific target IP address.
This example will create the zone for the domain ‘home.lan’ with the type ‘static’, then you’ll create multiple sub-domains via the ‘local-data’ parameter. Each sub-domain will be pointed to a specific IP address, and also you’ll create PTR records via the ‘local-data-ptr’ parameter.
# local zone
local-zone: "home.lan." static
local-data: "firewall.home.lan. IN A 10.0.0.1"
local-data: "vault.home.lan. IN A 10.0.0.2"
local-data: "media.home.lan. IN A 10.0.0.3"
local-data: "docs.home.lan. IN A 10.0.0.4"
local-data: "wiki.home.lan. IN A 10.0.0.5"
local-data-ptr: "10.0.0.1 firewall.home.lan"
local-data-ptr: "10.0.0.2 vault.home.lan"
local-data-ptr: "10.0.0.3 media.home.lan"
local-data-ptr: "10.0.0.4 docs.home.lan"
local-data-ptr: "10.0.0.5 wiki.home.lan"
- local-zone: define the local domain here.
- local-data: define A record for sub-domains and which local IP address will be resolved.
- local-data-ptr: define the ptr record for your sub-domains.
Unbound Performance Tuning and Tweak
Add the following lines to get more performance. You can adjust the below parameters with your current environment.
- num-threads: the number of threads that will be created. The value should match with server CPU cores.
- msg-cache-slabs: the number of slabs to use for the message cache. Set it to 8 to optimize Unbound to use more memory for caching.
- rrset-cache-slabs: the number of slabs to use for the RRset cache. Set it to 8 to optimize Unbound to use more memory for the RRSet cache.
- infra-cache-slabs: the number of slabs to use for the Infrastructure cache. Set it to 8 to optimize Unbound to use more memory for the Infrastructure cache.
- key-cache-slabs: the number of slabs to use for the key cache. Set it to 8 to optimize Unbound to use more memory for the key cache.
- rrset-cache-size: specify the amount of memory for the RRSet cache. This example uses 256MB, with the default is only 4MB.
- msg-cache-size: specify the amount of memory for the message cache. This example uses 128MB, with the default is only 4MB.
- so-rcvbuf: set up buffer size for DNS port 53/udp to 8 MB. On the Ubuntu system, you also must set up a higher value for the kernel parameter ‘net.core.rmem_max’.
Setup Unbound as a DNS Resolver with DNS-over-TLS (DoT)
Lastly, add a new section ‘forward-zone’ to set up Unbound as a DNS resolver for your local networks. This example uses Quad9 DNS servers with DoT (DNS-over-TLS) enabled.
## Also add IBM IPv6 Quad9 over TLS
- forward-zone: define forward zone for Unbound.
- name: set to “.” to forward all DNS queries.
- forward-addr: use a specific forwarder to forward all DNS queries. This example uses Quad9 DNS with DNS-over-TLS (DoT) enabled.
Save and exit the file ‘/etc/unbound/unbound.conf’ when finished. With the Unbound config file modified, you can now restart the Unbound service and apply the changes.
Run the below command check and verify the Unbound configuration. If successful, you should get an output such as ‘unbound-checkconf: no errors in /etc/unbound/unbound.conf’.
Next, run the below command to increase your system’s default ‘net.core.rmem_max’ via the ‘/etc/sysctl.conf’ file. Then, apply the changes via the ‘sysctl’ command.
echo "net.core.rmem_max= 8388608" >> /etc/sysctl.conf
sudo sysctl -p
After that, run the below systemctl command to restart the Unbound service and apply the changes.
sudo systemctl restart unbound
With this, the Unbound service should be running with the new configuration on IP address 192.168.5.100 on port 53.
Verify the list of open ports on your system via the ss command below.
You will receive an output like this — The default DNS udp port 53 is used by the Unbound service.
Now that you’ve finished Unbound configurations, you’ll set up the UFW firewall and open the default DNS port 53.
Setting up UFW Firewall
On Ubuntu, the default firewall that is installed is UFW. It’s installed, but still inactive. In this step, you’ll set up the UFW firewall and open the UDP port for Unbound.
Run the below command to open the OpenSSH service on UFW via the below command. Then, you can add the DNS port 53/udp to the UFW firewall.
sudo ufw allow OpenSSH
sudo ufw allow 53/udp
Next, run the below command to start and enable the UFW firewall service. When prompted, input y to confirm and press ENTER to proceed.
sudo ufw enable
The output ‘Firewall is active and enabled on system startup’ confirms that the UFW firewall is running and it’s enabled, which means the UFW firewall will start automatically on system startup.
Now run the below ufw command to verify the status of the UFW firewall. You should receive an output that the UFW status is ‘active’ with the OpenSSH service and the DNS port 53/udp enabled.
sudo ufw status
Setting up Unbound Log via Rsyslog and Logrotate
After configuring the UFW firewall, you’ll now set up a log file for Unbound via rsyslog and logrotate. The rsyslog service will create a specific log file for Unbound and the logrotate will rotate the Unbound log file in a certain time.
Run the below command to add a new Rsyslog configuration ‘/etc/rsyslog.d/unbound.conf’ for the Unbound service. With this, Unbound logs will be stored at ‘/var/log/unbound.log’.
cat <# Log messages generated by unbound application
if $programname == 'unbound' then /var/log/unbound.log
# stop processing it further
Next, run the below command to add the logrotate configuration ‘/etc/logrotate.d/unbound’ for the Unbound service. This will create log rotation for the Unbound log file ‘/var/log/unbound.log’ on a daily basis.
create 0640 root adm
Now run the below systemctl command to restart the Rsyslog and Logrotate services. This will apply the changes that you’ve made to both services.
sudo systemctl restart rsyslog logrotate
Lastly, you can verify the log file by restarting the Unbound service using the below command.
With this, messages that are generated by the Unbound service during the restart process will be stored in the log file ‘/var/log/unbound.log’. Run the cat command to show the content of the log file ‘/var/log/unbound.log’.
sudo systemctl restart unbound
Setting up DNS Resolver on Client
As for the client side, you need to set up the DNS resolver and use the Unbound as a default resolver on the client system. For Ubuntu distribution, you can use NetworkManager, systemd-resolved service, or set up a static file for ‘/etc/resolv.conf’.
In this step, you’ll learn how to set up DNS resolver on Ubuntu Desktop and Ubuntu Server.
For Ubuntu Desktop
The NetworkManager service handles the default networking for the Ubuntu Desktop version. So you can easily set up DNS resolver via the NetworkManager, which can be done via command-line GUI, or by editing the config file for each network interface.
To set up DNS resolver via the command line, you can use the nmcli. Run the below command to set up DNS resolver for the specific network interface. You can replace the interface name eth0.
sudo nmcli connection modify eth0 ipv4.dns "192.168.5.100"
Every interface managed by NetworkManager has a specific config file that is stored in the ‘/etc/NetworkManager/system-connections’ directory with the format ‘.nmconnection’.
You can modify the interface configuration with your preferred text editor and add the following lines to the ‘[ipv4]’ section.
If you prefer using a GUI application, open the NetworkManager application on your machine and edit the interface name that you want to modify. Click the ‘IPv4 Settings’ tab and input your local DNS server. Then, click Save to confirm.
For Generic Ubuntu Server
For generic Ubuntu server machines, the networking is handled by netplan with the backend systemd-networkd service. And for DNS resolver configuration, the systemd-networkd is used the systemd-resolved. So, to set up DNS resolver on a generic Ubuntu server, you can achieve this via the systemd-resolved’ service.
Open The systemd-resolved config file using your preferred editor. This example uses a nano editor.
sudo nano /etc/systemd/resolved.conf
On the ‘[Resolve]’ section, uncomment the ‘DNS’ parameter and input the IP address of your local DNS server.
Save and exit the file when finished.
Now run the below command to restart the systemd-resolved service and apply the changes. Then, you can verify the status of the DNS resolver via the resolvectl command as below.
sudo systemctl restart systemd-resolved
sudo resolvectl status
If successful, you should see an output like this — The default DNS resolver is changed to the Unbound local DNS server IP address 192.168.5.100.
Testing Unbound DNS Server
To ensure that the Unbound DNS is working as a DNS resolver, run the dig command below from the Ubuntu client machine. The parameter ‘@192.168.5.100’ ensures that you’re using an Unbound DNS server that runs on IP address ‘192.168.5.100’.
When successful, you receive an answer from the root DNS server like the below output. Also, you’ll notice the ‘ad’ (authentic data) flag in the header output, which means the DNSSEC is enabled.
Next, run the below command to ensure that clients can access domain names on the internet.
When successful, you should receive an output details DNS record for the domain ‘github.com’ and ‘duckduckgo.com’. You can see that the DNS resolver that answers the query is ‘127.0.0.53#53’, the systemd-resolved that uses Unbound as the default resolver. Also, you can see the ‘Query time’ for each query, the ‘Query time’ to domain ‘github.com’ is ‘1748’ and to ‘duckduckgo.com’ is ‘999’.
If you rerun the dig command on top, the ‘Query time’ should be reduced. And this confirms that your queries have been cached, and the DNS cache is working.
Access Github after cache stored:
Access duckduckgo after cache stored:
Next, verify the local domain or sub-domain via the dig command below. If successful, each sub-domain will be pointed to the correct IP address configured on the Unbound config file ‘/etc/unbound/unbound.conf’.
dig firewall.home.lan +short
dig vault.home.lan +short
dig media.home.lan +short
Now run the below dig command to ensure that PTR records are pointed to the correct domain name.
dig -x 10.0.0.1 +short
dig -x 10.0.0.2 +short
dig -x 10.0.0.3 +short
You can also verify DoT (DNS over TLS) via tcpdump. Install the ‘tcpdump’ package to your Unbound server.
sudo apt install tcpdump
Input y when prompted and press ENTER to proceed.
Now run the below tcpdump command to monitor traffics on the interface ‘eth0’ with DoT port 853. In this example, the Unbound DNS is running on IP address ‘192.168.5.100’ with the interface ‘eth0’.
tcpdump -vv -x -X -s 1500 -i eth0 'port 853'
Move to the client machine and run the below command to access external/internet domain names via the dig command below.
After that, move back to the Unbound server and you should now get an output similar to this on the tcpdump output.
With this, you’ve now installed and configured Local DNS Server via Unbound on the Ubuntu server. Also, you’ve configured a DNS resolver on Ubuntu Desktops and Servers via NetworkManager and systemd-resolved.
In this guide, you’ve installed Unbound Local DNS Server on a Ubuntu 22.04 server. You’ve enabled DNS cache, DNSSEC (enabled by default), configure private-address and ACLs, added local domain via local-zone, then configured Unbound as DNS resolver with DoT (DNS-over-TLS).
In addition, you’ve configured basic DNS privacy and security, optimized Unbound, and configured Unbound logs via rsyslog and logrotate.
To the end of this guide, you’ve also learned how to set up a DNS resolver on Ubuntu Desktops and Server via NetworkManager and systemd-resolved. And also learned the basic usage of the dig command for checking the DNS server.