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 that shows how to install, configure, and run a home server with (dockerized or virtualized) services such as Home Assistant and ownCloud.
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.
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.
The following commands install Unbound:
$ apt update $ apt install unbound
Verify that the status of the Unbound service is
$ 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
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:
server: # Listen on all interfaces interface: 0.0.0.0 interface: ::0 # Without this, DNS requests from the Wireguard/Firezone Docker container are dropped (ignored) interface-automatic: yes # 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: 18.104.22.168@853#ID.dns.nextdns.io forward-addr: 22.214.171.124@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: 126.96.36.199@853#dns.quad9.net # forward-addr: 188.8.131.52@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
$ systemctl restart unbound
See this earlier article for information on how to configure Proxmox’s firewall. Add a rule to allow DNS traffic:
- Enable: checked
Add Unbound’s configuration file as a backup source to resticprofile (more info on restic):
source: - "/etc/unbound/unbound.conf"
On the Proxmox server, switch the local resolver to Unbound by editing
/etc/resolv.conf and replacing the
nameserver line with the following:
There’s no need to restart any services. The change becomes active immediately.
Test DNS resolution on the Proxmos 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 184.108.40.206 helgeklein.com. 181 IN A 220.127.116.11 helgeklein.com. 181 IN A 18.104.22.168 ;; 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 name resolution on another machine on your network, e.g., on Windows, with the following command:
$ nslookup helgeklein.com 192.168.0.4
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.
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
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:
- Always assign unique local addresses (ULA): checked
- Set ULA prefix manually:
- Enter the ULA prefix you generated above. e.g.,
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
2 by adding the following to
# Accept IPv6 router advertisements even when forwarding is enabled net.ipv6.conf.vmbr0.accept_ra = 2
sysctl to re-read its config file with the command
sysctl -p. It should print the changed setting:
net.ipv6.conf.vmbr0.accept_ra = 2
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
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.
If you have a Fritz!Box like me, navigate to Home Network > Network > Network Settings > IP Addresses.
To change the IPv4 DNS server assigned via DHCP, navigate to IPv4 Settings > Home Network and configure the following:
- Local 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:
- Local DNSv6 server: enter your Proxmox server’s ULA (in the example above:
- Also announce DNSv6 server via router advertisement (RFC 5006): checked
Made the following changes to
- Replaced previous
access-controlentries with new ones that allow access from every IP address. The DNS server is in a private network; restrictions only cause problems.
interface-automatic: yesor Unbound would ignore/drop DNS requests from a WireGuard (Firezone) Docker container.
Made the following changes to
access-controlentries for private IPv6 networks, or the server wouldn’t answer on the IPv6 ULA address (and return
PTRrecords for the dockerized services because they all pointed to the same (host) IP address.
- Switched the
transparentto allow forwarding of queries for records not defined locally via