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.

Prerequisites

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.

show unmbound paackage

Next, install Unbound using the following apt command. When prompted, input y to confirm and press ENTER to proceed.

sudo apt install unbound

Output:

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.

verify unbound

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.

Basic Configuration

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
server:
    use-syslog: yes
    username: "unbound"
    directory: "/etc/unbound"
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    
    do-ip6: no
    interface: 192.168.5.100
    port: 53
    prefetch: yes

    root-hints: /usr/share/dns/root.hints
    harden-dnssec-stripped: yes

Detail parameters:

  • 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: 14400
    cache-min-ttl: 11000

Detail parameters:

  • 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: yes
    hide-identity: yes
    hide-version: yes
    use-caps-for-id: yes

Detail parameters:

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

    private-address: 192.168.0.0/16
    private-address: 192.168.5.0/24
    private-address: 169.254.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    private-address: fd00::/8
    private-address: fe80::/10

    #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

Detail parameters:

  • 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"

Detail parameters:

  • 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: 4
    msg-cache-slabs: 8
    rrset-cache-slabs: 8
    infra-cache-slabs: 8
    key-cache-slabs: 8
    rrset-cache-size: 256m
    msg-cache-size: 128m
    so-rcvbuf: 8m

Detail parameters:

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

forward-zone:
    name: "."
    forward-ssl-upstream: yes
    ## Also add IBM IPv6 Quad9 over TLS
    forward-addr: 9.9.9.9@853#dns.quad9.net
    forward-addr: 149.112.112.112@853#dns.quad9.net

Details parameters:

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

sudo unbound-checkconf

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

setup sysctl

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.

configure unbound

Verify the list of open ports on your system via the ss command below.

ss -tulpn

You will receive an output like this - The default DNS udp port 53 is used by the Unbound service.

check unbound port

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.

Output:

setup ufw

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

Output:

verify ufw

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 <<EOF | sudo tee /etc/rsyslog.d/unbound.conf
# Log messages generated by unbound application
if $programname == 'unbound' then /var/log/unbound.log
# stop processing it further
& stop
EOF

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.

cat <<EOF | sudo tee /etc/logrotate.d/unbound
/var/log/unbound.log {
  daily
  rotate 7
  missingok
  create 0640 root adm
  postrotate
    /usr/lib/rsyslog/rsyslog-rotate
  endscript
}
EOF

setup logging unbound

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

restart rsyslog and 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
cat /var/log/unbound.log

Output:

check unbound log

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.

[ipv4]
dns=192.168.5.100
ignore-auto-dns=true
never-default=true

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.

dns resolver guyide network manager

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.

[Resolve]
DNS=192.168.5.100

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.

dns resolver systemd resolved

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

dig @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.

verify dig dns server

Next, run the below command to ensure that clients can access domain names on the internet.

dig github.com
dig duckduckgo.com

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

Output for github.com:

dig github.com

Output for duckduckgo.com:

dig ducducgo before

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.

dig github.com
dig duckduckgo.com

Access Github after cache stored:

dns cache verify

Access duckduckgo after cache stored:

after 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

Output:

verify local domains

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

Output:

test ptr records

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.

install tcpdump

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.

dig google.com

Output:

dig google

After that, move back to the Unbound server and you should now get an output similar to this on the tcpdump output.

dns encrypted with DoT

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.

Conclusion

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.

Share this page:

3 Comment(s)