How to Set Up a Local DNS Resolver with Unbound on Debian

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 system package manager.

In this guide, you will learn how to set up Private DNS Server with Unbound on a Debian 11 and Debian 12 server. You'll set up Unbound as a Local DNS Server with some features such as an authoritative DNS Server, enable DNS cache, set up local IP address and Access Control Lists (ACLs), setup local domain names, then set up Unbound as a DNS resolver with DNS-over-TLS (DoT) enabled.

In addition to that, you'll also set up logging for Unbound service via Rsyslog and Logrotate.

Prerequistes

To complete this guide, you must have the following requirements.

  • A system running a Debian 11 server or a Debian 12 server.
  • A user account with sudo/root administrator privileges.

That's it. You can now start installing Unbound as Local DNS Server.

Installing Unbound

By default, the Unbound package is available on Debian repository. You can install it via APT. In this first step, you'll install Unbound package, which includes the 'dns-root-data' to your Debian server.

Before you start, update and refresh your Debian package index via the apt command below.

sudo apt update

Now run the below apt command to check the 'unbound' package that is available on the Debian repository.

sudo apt info unbound

The output below confirms that the Unbound package is available by default on the Debian server repository with the current version 1.13.

check unbound package

Now run the below apt command to install unbound to your Debian system. 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 the Unbound service and ensure that the service is enabled and running.

sudo systemctl is-enabled unbound
sudo systemctl status unbound

The Unbound service is enabled and will start automatically upon system startup. And the status of the Unbound service is running.

verify unbound

Additional note, if you have disabled IPv6 on your Debian server, the Unbound can't start. To solve this, you can add the parameter 'do-ip6: no' to the 'server' section on the unbound config file '/etc/unbound/unbound.conf'.

With the Unbound installed, you'll next start configuring Unbound on your system.

Configuring Unbound 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.conf' via your preferred editor.

You'll now learn about the basic configuration of an Unbound DNS server.

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. You can set up basic Unbound configurations in the' server' section. In this example, you'll run Unbound on the local IP address '192.168.5.20' with the default port 53. Also, you'll set up logging to Syslog messages and disable IPv6. Lastly, you will setup 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.20
    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 the client's connections will be handled by this port. 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: 1200

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 '1200' 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, which is using 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 '0x20-encoded' in the query to foil spoof attempts.

Define Private Network and Access Control Lists (ACLs)

Next, you must define your networks' private-address and the ACLs (Access Control Lists). Change the local subnet in the below lines with your current network environment.

    private-address: 192.168.0.0/16
    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 now define the local zone for your local domain name. 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 pointed to the specific target IP address.

This example will create the zone for the domain 'garden.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: "garden.lan." static

    local-data: "firewall.garden.lan.  IN A 10.0.0.1"
    local-data: "vault.garden.lan.    IN A 10.0.0.2"
    local-data: "media.garden.lan.   IN A 10.0.0.3"
    local-data: "docs.garden.lan.       IN A 10.0.0.4"
    local-data: "wiki.garden.lan.     IN A 10.0.0.5"

    local-data-ptr: "10.0.0.1  firewall.garden.lan"
    local-data-ptr: "10.0.0.2  vault.garden.lan"
    local-data-ptr: "10.0.0.3  media.garden.lan"
    local-data-ptr: "10.0.0.4  docs.garden.lan"
    local-data-ptr: "10.0.0.5  wiki.garden.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.

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 systemctl command below to restart the Unbound service and apply the changes.

sudo systemctl restart unbound

Now that you've finished Unbound configurations, you'll next set up the UFW firewall and open the default DNS port 53.

configure unbound

Setting up UFW Firewall

In this step, you'll set up the UFW firewall on the Debian server and open the DNS port 53/udp. But before that, you must install UFW packages from the Debian repository via APT.

Run the below apt command to install UFW firewall to your Debian server. Input y when prompted and press ENTER to proceed.

sudo apt install ufw

Output:

install ufw

Once UFW is installed, you must 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

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.

Create a new Rsyslog config file '/etc/rsyslog.d/unbound.conf' using the below nano editor command.

sudo nano /etc/rsyslog.d/unbound.conf

Add the following lines to the file. With this, Unbound logs will be stored at '/var/log/unbound.log'.

# Log messages generated by unbound application 
if $programname == 'unbound' then /var/log/unbound.log
# stop processing it further
& stop

Save the file and exit the editor when finished.

Now run the below systemctl command utility to restart the 'rsyslog' service and apply the changes.

sudo systemctl restart rsyslog

Next, you will set up log rotation for the Unbound log file '/var/log/unbound.log'. And you can achieve this via the logrotate service.

Create a new logrotate config file '/etc/logrotate.d/unbound' using the below nano editor command.

sudo nano /etc/logrotate.d/unbound

Add the following lines to the file. This will create log rotation for the Unbound log file '/var/log/unbound.log' on a daily basis.

/var/log/unbound.log {
  daily
  rotate 7
  missingok
  create 0640 root adm
  postrotate
    /usr/lib/rsyslog/rsyslog-rotate
  endscript
}

Save the file and exit the editor when finished.

Now run the below systemctl command utility to restart the logrotate service and apply the changes.

sudo systemctl restart logrotate

With this, you've now successfully installed and configured Unbound DNS server and configured logging via Rsyslog and Logrotate. Unbound logs will be saved to the file '/var/unbound/unbound.log'.

setup logging unbound

Setting DNS Resolver on Linux Client

In this step, you'll learn how to set up a DNS resolver on client machines. This will show you two methods for different Linux distributions.

For Ubuntu clients: Networking on Ubuntu is managed by NetworkManager. To set up a DNS resolver, you can combine the NetworkManager with systemd-resolved as the backend.
For Debian clients: on Debian systems (minimal version), the networking is handled by the traditional config file '/etc/network/interface'. You can define the DNS resolver directory to the '/etc/network/interface' file or use the systemd-resolved service, which is available by default on the Debian server.

For Ubuntu clients with NetworkManager and Systemd-resolved

This is for Ubuntu users that use NetworkManager as the default networking configuration. You'll set up systemd-resolved as the backend for the DNS server on NetworkManager.

Open the file '/etc/NetworkManager/NetworkManager.conf' using the below nano editor command.

sudo nano /etc/NetworkManager/NetworkManager.conf

Uncomment the 'dns' parameter and add the backend as 'systemd-resolved'.

dns=systemd-resolved

Save and exit the file when finished.

Now run the below systemctl command to restart the NetworkManager service and apply the changes.

sudo systemctl restart NetworkManager

Next, you will define the Unbound Local DNS in systemd-resolved.

Open the systemd-resolved config file '/etc/systemd/resolved.conf' using the below nano editor command.

sudo nano /etc/systemd/resolved.conf

On the '[Resolve]' section, uncomment the 'DNS' parameter and input the IP address of your Unbound DNS server.

[Resolve]
DNS= 192.168.5.20

Save and exit the file when finished.

Next, run the below systemctl command to start and enable the 'systemd-resolved' service.

sudo systemctl start systemd-resolved
sudo systemctl enable systemd-resolved

Now verify the 'systemd-resolved' service status via the below command. You should see the systemd-resolved is enabled and will be run automatically on system startup. And the status of the 'systemd-resolved' service is now running.

sudo systemctl status systemd-resolved

You can also verify your DNS resolver configuration via the 'resolvectl' command below. And you should see the default resolver is your Unbound DNS server with IP address '192.168.5.20'.

resolvectl status

For Debian clients

For the Debian system, you can also use the systemd-resolved service to set up a DNS resolver.

Open the systemd-resolved config file '/etc/systemd/resolved.conf' using the below nano editor.

sudo nano /etc/systemd/resolved.conf

Add the 'DNS' parameter followed by the IP address of the Unbound server to the '[Resolver]' section.

[Resolve]
DNS=192.168.5.20

Save the file and exit the editor when finished.

Now run the below systemctl command to start and enable the systemd-resolved service.

sudo systemctl start systemd-resolved
sudo systemctl enable systemd-resolved

Output:

setup systemd-resolved

Then, verify the status of the systemd-resolved service to ensure that it's running. The output 'active (running)' confirms that the systemd-resolved is running, and the output 'loaded ../../../systemd-resolved.service; enabled;..' confirm that the service is enabled.

sudo systemctl status systemd-resolved

Output:

verify systemd-resolved

You can verify your DNS resolver configuration via the 'resolvectl' command below. And you should see the default resolver is your Unbound DNS server with IP address '192.168.5.20'.

resolvectl status

Output:

verify dns resolver

Testing Unbound DNS Server

Run the dig command below to ensure that the Unbound DNS is working as a DNS resolver. The parameter '@192.168.5.20' ensures you're using an Unbound DNS server that runs on IP address '192.168.5.20'.

dig @192.168.5.20

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 unbound dns resolver

Next, run the below command to ensure clients can access domain names online.

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 the DNS resolver that answers the query is '127.0.0.53#53', which is 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 '1367' and to 'duckduckgo.com' is '1059'.

Output for github.com:

dig to github

Output for duckduckgo.com:

dig to duckduckgo.com

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

Output:

cached dns query

dns cached duckduckgo.com

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 as configured on the Unbound config file '/etc/unbound/unbound.conf'.

dig firewall.garden.lan +short
dig vault.garden.lan +short
dig media.garden.lan +short

Output:

verify local domain

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:

verify ptr record

Lastly, 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 runs on IP address '192.168.5.20' 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:

verify dot

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

query traffics encrypted

With this, you've now installed and configured Local DNS Server via Unbound. Also, you've configured a DNS resolver on Ubuntu clients via systemd-resolved and NetworkManager, and Debian clients via systemd-resolved.

Conclusion

In this guide, you've installed Unbound Local DNS Server on a Debian 11 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 to that, 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 and Debian machines via NetworkManager and systemd-resolved. And also learned the basic usage of the dig command for checking the DNS server.

Share this page:

0 Comment(s)