User Tools

Site Tools


computing:unbounddns

  • unbounddns
  • Jonathan Haack
  • Haack's Networking
  • netcmnd@jonathanhaack.com

unbounddns


This tutorial is for users of Debian GNU/Linux who want to run their own recursive DNS server using the Unbound project. In this scenario, I am using GL.iNet MT6000 router and a separate AP. The router handles all dhcp/dns for the LAN / private subnet. In the openWRT config on the router's dhcp server, I specify two custom DNS servers in Interfaces / LAN / DHCP Server / Advanced / 6,10.1.1.100,10.1.1.101. These DNS servers are Debian VMs on two different production servers in the home office space; each of them is running a pihole server. The pihole-FTL takes care of adblocking and DNS sinkhole duties. If left with default settings, it uses your specified third-party DNS servers for upstream requests (Level 3, Cloudflare, etc.). This tutorial is how to replace those third-party DNS servers with Unbound, running locally on each pihole and on port 5335 instead of port 53, which is already used by pihole-FTL.

sudo apt install unbound
sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf # I use for lan-side pihole+unbound installations
sudo nano /etc/unbound/unbound.conf #I use this for lan- or wan-side non-pihole installations

In that file, enter something like the following, adjusting as necessary for your use-case.

                                                                                                                                                                                                                    
server:
  # Logging (minimal)
  use-syslog: yes
  verbosity: 1
  directory: "/etc/unbound"
  username: unbound
  # Bind to all interfaces, non-standard port
  interface: 0.0.0.0
  interface: ::0
  port: 5335
  do-ip4: yes
  do-ip6: yes
  prefer-ip6: no
  do-udp: yes
  do-tcp: yes
  # Module configuration
  module-config: "validator iterator"
  # Security and DNSSEC
  harden-glue: yes
  harden-dnssec-stripped: yes
  use-caps-for-id: no
  aggressive-nsec: yes
  hide-identity: yes
  hide-version: yes
  qname-minimisation: yes
  harden-large-queries: yes
  # Cache settings
  cache-max-ttl: 86400
  cache-min-ttl: 3600
  rrset-cache-size: 256m
  msg-cache-size: 128m
  key-cache-size: 64m
  neg-cache-size: 16m
  # Performance tweaks
  num-threads: 8
  msg-cache-slabs: 8
  rrset-cache-slabs: 8
  infra-cache-slabs: 8
  key-cache-slabs: 8
  outgoing-range: 4096
  num-queries-per-thread: 1024
  infra-cache-numhosts: 10000
  prefetch: yes
  prefetch-key: yes
  serve-expired: yes
  serve-expired-ttl: 3600
  so-reuseport: yes
  edns-buffer-size: 4096
  # Block private address ranges (excluding guest subnet)
  private-address: 172.0.0.0/8
  private-address: 169.254.0.0/16
  private-address: 10.0.0.0/8
  private-address: fd00::/8
  private-address: fe80::/10
  # Access control for guest subnet
  access-control: 127.0.0.1/32 allow
  access-control: ::1 allow
  access-control: 192.168.1.0/24 allow
  

If using unbound with a pihole, let /etc/resolv.conf on the pihole host be populated by your router. It will fill in with something like:

domain domain.com
search domain.com
nameserver 192.168.1.254

In my case, I prefer traditional rotated logs with rsyslog, so I do the following:

sudo apt install rsyslog
sudo nano /etc/rsyslog.d/unbound.conf
<if $programname == 'unbound' then /var/log/unbound/unbound.log>
<& stop>
nano /etc/logrotate.d/unbound

In the log rotate file, enter the following:

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

Additionally, some Debian systems have resolvconf installed, so many install recipes recommend disabling that service so that it does not overwrite the DNS settings we are making here.

systemctl disable --now unbound-resolvconf.service
sed -Ei 's/^unbound_conf=/#unbound_conf=/' /etc/resolvconf.conf
rm /etc/unbound/unbound.conf.d/resolvconf_resolvers.conf

To make sure logs are working properly:

nano /etc/apparmor.d/local/usr.sbin.unbound
</var/log/unbound/unbound.log rw,>
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.unbound
sudo service apparmor restart
sudo mkdir -p /var/log/unbound
sudo touch /var/log/unbound/unbound.log
sudo chown unbound /var/log/unbound/unbound.log

Enforce edns settings specified in config (pihole-only installations):

nano /etc/dnsmasq.d/99-edns.conf
<edns-packet-max=1232>

The last step is configuring the unbound server in the pihole GUI. Go to DNS > Custom and then add 127.0.0.1#5335 as a server and disable all the rest. Additionally, don't forget to forward requests to the pihole within openWRT. If you do everything in this tutorial: openWRT on GL.Inet MT6000, you can easily add pihole support to both ipv4 and ipv6. Below, please find pics of the pihole custom DNS, and ipv4 and ipv6 settings for pihole in openWRT:

Okay, that concludes the steps for setting up pihole+unbound within an openWRT environment. However, one might also want to leverage unbound for public-facing machines. For those use-cases, I enter the following config in /etc/unbound/unbound.conf:

server:
  # Bind to localhost only
  interface: 127.0.0.1
  interface: ::1
  port: 53
  do-ip4: yes
  do-ip6: yes
  prefer-ip6: yes
  access-control: 127.0.0.0/8 allow
  access-control: 0.0.0.0/0 refuse
  access-control: ::0/0 refuse
  # Optimize for 8 cores
  num-threads: 4
  msg-cache-slabs: 4
  rrset-cache-slabs: 4
  infra-cache-slabs: 4
  key-cache-slabs: 4
  # Cache settings for high query volume
  cache-max-ttl: 86400
  cache-min-ttl: 3600
  rrset-cache-size: 128m
  msg-cache-size: 64m
  key-cache-size: 32m
  neg-cache-size: 8m
  # Enable prefetch and expired responses
  prefetch: yes
  prefetch-key: yes
  serve-expired: yes
  serve-expired-ttl: 3600
  # DNSSEC validation for DANE
  #do-dnssec: yes
  harden-dnssec-stripped: yes
  harden-referral-path: yes
  harden-below-nxdomain: yes
  harden-algo-downgrade: no
  # Performance tweaks
  #so-rcvbuf: 4m
  #so-sndbuf: 4m
  edns-buffer-size: 1232
  outgoing-range: 4096
  num-queries-per-thread: 1024
  jostle-timeout: 200
  #low-resolver-mem: no
  # Logging (minimal)
  verbosity: 1
  log-queries: no
  log-replies: no
  use-syslog: yes
  # Security and privacy
  hide-identity: yes
  hide-version: yes
  use-caps-for-id: yes
  qname-minimisation: yes
  harden-large-queries: yes
  harden-glue: yes
  aggressive-nsec: yes
  # Protocol settings
  do-tcp: yes
  do-udp: yes
  # Enable full recursion - no longer needed, retained for history
  # do-not-query-localhost: no
  # root-hints: "/usr/share/dns/root.hints"
  # Disable subnetcache
  module-config: "validator iterator"
# Forward to upstream resolvers
# forward-zone:
#    name: "."
#    forward-addr: 1.1.1.1  # Cloudflare
#    forward-addr: 8.8.8.8  # Google

After that, I navigate to /etc/resolv.conf and enter the following:

nameserver ::1
nameserver 127.0.0.1

And, as we already discussed, if you are using unbound without a pihole per se, you would simply adapt the lan-side configuration above and tweak where necessary.

oemb1905 2025/09/20 18:31

computing/unbounddns.txt · Last modified: by oemb1905