------------------------------------------- * **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 [[https://unbound.docs.nlnetlabs.nl/en/latest/index.html|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 <& 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 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 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: [[https://tech.haacksnetworking.org/2024/09/28/openwrt-on-two-gl-inet-mt6000s/|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: {{ :computing:screenshot_from_2025-09-20_12-30-26.png?400 |}} {{ :computing:screenshot_from_2025-09-20_12-28-46.png?400 |}} {{ :computing:screenshot_from_2025-09-20_12-23-05.png?400 |}} 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. --- //[[alerts@haacksnetworking.org|oemb1905]] 2025/09/20 18:31//