by: Helge, published: Jun 5, 2023, updated: Sep 14, 2024, in

Unbound DNS Server Configuration & Static IPv6 Address on Proxmox

This article explains how to set up the Unbound DNS server as the resolver for your home network. It also shows how to generate and assign a static IPv6 address to your Proxmox server. This post is part of my series on home automation, networking & self-hosting that shows how to install, configure, and run a home server with (dockerized or virtualized) services such as Home Assistant and ownCloud.

What is Unbound?

Unbound is a fast, lean, and secure DNS server that has supplanted BIND as the default name server in FreeBSD and OpenBSD. Unbound supports DNSSEC and can use DNS over TLS (DoT) when forwarding queries to upstream DNS servers.

Unbound Installation & Configuration on Proxmox

Dockerized or Natively Installed?

DNS is an essential service that needs to be available even when Docker is not running (e.g., during backups). For that reason, I installed Unbound natively on my Proxmox home server.

Unbound Installation

The following commands install Unbound:

$ apt update
$ apt install unbound

Verify that the status of the Unbound service is active (running):

$ systemctl status unbound
● unbound.service - Unbound DNS server
     Loaded: loaded (/lib/systemd/system/unbound.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2023-06-03 01:18:09 CEST; 1min 42s ago

Unbound Configuration

After the installation, Unbound uses its sensible built-in default configuration (see the config docs). We only need to change a subset of settings by replacing the contents of nano /etc/unbound/unbound.conf with the following.

Note: please see this article if you want to bind Unbound to (Docker) interfaces that may not yet exist when Unbound starts.

server:

   # Specify the IP addresses to listen on
   # IMPORTANT:
   # - Unbound won't start if these interfaces don't exist.
   interface: 192.168.0.4
   interface: lo
   interface: ::0

   # Root hints file
   root-hints: "/usr/share/dns/root.hints"
   
   # Location of the trust anchor file that enables DNSSEC
   auto-trust-anchor-file: "/var/lib/unbound/root.key"   

   # Certificates used to authenticate connections made upstream
   tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
   
   # Enable prefetching of almost expired message cache entries
   prefetch: yes
   
   # Refuse id.server and hostname.bind queries
   hide-identity: yes

   # Refuse version.server and version.bind queries
   hide-version: yes

   # Performance optimization
   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

   # Private addresses are not allowed to be returned for public internet names.
   # Any occurrence of such addresses is removed from DNS answers. This protects against DNS rebinding.
   private-address: 192.168.0.0/16
   private-address: 172.16.0.0/12
   private-address: 10.0.0.0/8
   private-address: 169.254.0.0/16
   private-address: fd00::/8
   private-address: fe80::/10

   # Which clients may make queries to this server: everybody
   access-control: 0.0.0.0/0 allow
   access-control: ::/0 allow

   # Local DNS domain
   # REPLACE with your actual domain
   local-zone: "home.yourdomain.com." transparent

   # A records in the local DNS domain
   # REPLACE with your actual domain and IP address
   local-data: "auth.home.yourdomain.com.       IN	A	192.168.0.4"
   local-data: "lldap.home.yourdomain.com.      IN	A	192.168.0.4"
   local-data: "owncloud.home.yourdomain.com.   IN	A	192.168.0.4"
   local-data: "portainer.home.yourdomain.com.  IN	A	192.168.0.4"
   local-data: "px1.home.yourdomain.com.        IN	A	192.168.0.4"
   local-data: "whoami.home.yourdomain.com.     IN	A	192.168.0.4"

   # PTR records in the local DNS domain
   # REPLACE with your actual domain and IP address
   local-data-ptr: "192.168.0.4   px1.home.yourdomain.com"

   # Act as DNS resolver by forwarding queries that cannot be answered locally via DNS-over-TLS (DoT)
   forward-zone:
      name: "."
      forward-ssl-upstream: yes
      # NextDNS (REPLACE ID with your actual ID)
      forward-addr: 45.90.28.194@853#ID.dns.nextdns.io
      forward-addr: 45.90.30.194@853#ID.dns.nextdns.io
      forward-addr: 2a07:a8c0::ID@853#ID.dns.nextdns.io
      forward-addr: 2a07:a8c1::ID@853#ID.dns.nextdns.io
      # Quad9
      # forward-addr: 9.9.9.9@853#dns.quad9.net
      # forward-addr: 149.112.112.112@853#dns.quad9.net
      # forward-addr: 2620:fe::fe@853#dns.quad9.net
      # forward-addr: 2620:fe::9@853#dns.quad9.net

Check your new configuration by running the command unbound-checkconf. The output should look similar to the following:

unbound-checkconf: no errors in /etc/unbound/unbound.conf

Restart Unbound:

$ systemctl restart unbound

Firewall Rule for DNS

See this earlier article for information on how to configure Proxmox’s firewall. Add a rule to allow DNS traffic:

  • Direction: in
  • Action: ACCEPT
  • Macro: DNS
  • Enable: checked

Unbound Configuration Backup

Add Unbound’s configuration file as a backup source to resticprofile (more info on restic):

    source:
      - "/etc/unbound/unbound.conf"

Switch the Local Resolver

On the Proxmox server, switch the local resolver to Unbound by editing /etc/resolv.conf and replacing the nameserver line with the following:

nameserver 127.0.0.1

There’s no need to restart any services. The change becomes active immediately.

Test DNS Resolution

Test on the DNS Server

Test DNS resolution on the Proxmox server with the dig command. The output should show the server as 127.0.0.1, as in the following example:

$ dig helgeklein.com

; <<>> DiG 9.16.37-Debian <<>> helgeklein.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 1893
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;helgeklein.com.                        IN      A

;; ANSWER SECTION:
helgeklein.com.         181     IN      A       104.26.10.91
helgeklein.com.         181     IN      A       172.67.74.112
helgeklein.com.         181     IN      A       104.26.11.91

;; Query time: 144 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Sat Jun 03 02:44:33 CEST 2023
;; MSG SIZE  rcvd: 91

Test on Another Machine on Your Network

Test name resolution on another machine on your network, e.g., on Windows, with the following command:

$ nslookup helgeklein.com 192.168.0.4

Assigning a Private IPv6 Address to Proxmox

The machine on which we installed the Unbound DNS server needs static IPv4 and IPv6 addresses. If you followed my Proxmox guide, your server already has a static IPv4 address. We’ll add a static IPv6 address now.

Generate Your IPv6 Unique Local Address (ULA) Prefix

IPv6 unique local addresses (ULAs) are used similarly to private IPv4 addresses, such as 192.168.0.0/16. Create your own ULA prefix at unique-local-ipv6.com. You’ll get something like fd7b:a864:8b59::/48.

Assign the IPv6 ULA Prefix to Your Router

Configure your router to always assign unique local addresses (ULAs) and use the ULA prefix you generated above.

If you have a Fritz!Box like me, navigate to Home Network > Network > Network Settings > IP Addresses > IPv6 Settings > Unique Local Addresses, and configure the following:

  1. Always assign unique local addresses (ULA): checked
  2. Set ULA prefix manually:
    1. Checked
    2. Enter the ULA prefix you generated above. e.g., fd7b:a864:8b59::/64.

Enable IPv6 Router Advertisements on Proxmox

Apparently, Linux ignores router advertisements when forwarding is enabled. Check with the following command if you’re affected:

$ cat /proc/sys/net/ipv6/conf/vmbr0/accept_ra

If it prints 1, you need to switch the accept_ra setting to 2 by adding the following to the iface vmbr0 inet static entry in /etc/network/interfaces:

# Accept IPv6 router advertisements even when forwarding is enabled
accept_ra 2

Reboot and verify the changed setting has been applied.

Obtain Your Proxmox Server’s IPv6 Address

Reboot your Proxmox server. List all IPv6 addresses with the command ip -6 addr. The output should look like the following:

3: vmbr0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UP qlen 1000
    inet6 fd7b:a864:8b59:0:12:34:56:78/64 scope global dynamic mngtmpaddr
       valid_lft 7195sec preferred_lft 3595sec
    inet6 REDACTED scope global dynamic mngtmpaddr
       valid_lft 7195sec preferred_lft 3595sec
    inet6 fe80::REDACTED/64 scope link
       valid_lft forever preferred_lft forever

In the output above, the Proxmox server’s unique local address (ULA) is fd7b:a864:8b59:0:12:34:56:78.

Switch Your Network’s DNS Server

Now that your new DNS resolver is fully operational, it’s time to configure your home network’s devices to use it. How that is done depends on your setup.

Fritz!Box

If you have a Fritz!Box like me, navigate to Home Network > Network > Network Settings > IP Addresses.

IPv4 DNS Server

To change the IPv4 DNS server assigned via DHCP, navigate to IPv4 Settings > Home Network and configure the following:

  1. Local DNS Server: 192.168.0.4

IPv6 DNS Server

To change the IPv6 DNS server assigned via DHCPv6 and router assignments, navigate to IPv6 Settings > DNSv6 Server in the Home Network and configure the following:

  1. Local DNSv6 server: enter your Proxmox server’s ULA (in the example above: fd7b:a864:8b59:0:12:34:56:78)
  2. Also announce DNSv6 server via router advertisement (RFC 5006): checked

Changelog

2024-05-31

  • More specific interface bindings in unbound.conf to enable other DNS servers on individual IP addresses.

2023-10-21

  • Fixed incorrect description of how to set accept_ra in Enable IPv6 Router Advertisements on Proxmox.

2023-06-12

Made the following changes to /etc/unbound/unbound.conf:

  • Replaced previous access-control entries with new ones that allow access from every IP address. The DNS server is in a private network; restrictions only cause problems.
  • Added interface-automatic: yes or Unbound would ignore/drop DNS requests from a WireGuard (Firezone) Docker container.

2023-06-08

Made the following changes to /etc/unbound/unbound.conf:

  • Added access-control entries for private IPv6 networks, or the server wouldn’t answer on the IPv6 ULA address (and return refused instead).
  • Removed PTR records for the dockerized services because they all pointed to the same (host) IP address.
  • Switched the local-zone from static to transparent to allow forwarding of queries for records not defined locally via local-data.

Previous Article Portainer Setup Guide With Automatic HTTPS & OAuth SSO via Authelia
Next Article Firezone: WireGuard VPN With User Self-Service Portal & SSO