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

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

server:
    logfile: "/var/log/unbound/unbound.log"
    log-time-ascii: yes
    use-syslog: yes
    directory: "/etc/unbound"
    username: unbound
    tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
    verbosity: 3
    interface: 0.0.0.0
    interface: ::0
    port: 5335
    do-ip4: yes
    do-udp: yes
    do-tcp: yes
    module-config: "validator iterator"
    do-ip6: yes
    prefer-ip6: no
    harden-glue: yes
    harden-dnssec-stripped: yes
    use-caps-for-id: no
    edns-buffer-size: 1232
    prefetch: yes
    num-threads: 8
    msg-cache-slabs: 16
    rrset-cache-slabs: 16
    infra-cache-slabs: 16
    key-cache-slabs: 16
    rrset-cache-size: 512m
    msg-cache-size: 256m
    outgoing-range: 32768
    num-queries-per-thread: 8192
    infra-cache-numhosts: 100000
    #so-rcvbuf: 1m
    #so-sndbuf: 2m
    so-reuseport: yes
    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
    #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.0.0/16 allow
    access-control: 10.0.0.0/8 allow
    access-control: 127.0.0.1/24 allow
    access-control: 2001:DB8::/64 allow
    aggressive-nsec: yes
    hide-identity: yes
    hide-version: yes
    cache-max-ttl: 14400
    cache-min-ttl: 11000

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:

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

The last step is configuring the unbound server in the pihole GUI. Alternately, you can do this without a pihole by simply specifying this address as your WAN's upstream DNS server in openWRT. Alright, and in case you don't need LAN-based DNS, but just want a public facing virtual appliance to use its own DNS, just install unbound and enter the following in /etc/unbound/unbound.conf:

server:
    interface: 127.0.0.1
    cache-max-ttl: 14400
    cache-min-ttl: 1200
    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
    #prefetch: yes
    harden-dnssec-stripped: yes
    use-syslog: yes
    aggressive-nsec: yes
    hide-identity: yes
    hide-version: yes
    use-caps-for-id: yes
    do-tcp: yes
    do-udp: yes

Then, just add nameserver 127.0.0.1 to /etc/resolv.conf. This latter step only works on classic/minimal Debian. Use netplan properly and/or resolvconf package and the correct .d directory if not using proper DNS management.The latest wan-based is:

include-toplevel: "/etc/unbound/unbound.conf.d/*.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
  #legacy
  #server:
  #    interface: 127.0.0.1
  #    cache-max-ttl: 14400
  #    cache-min-ttl: 1200
  #    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
  #    #prefetch: yes
  #    harden-dnssec-stripped: yes
  #    use-syslog: yes
  #    aggressive-nsec: yes
  #    hide-identity: yes
  #    hide-version: yes
  #    use-caps-for-id: yes
  #    do-tcp: yes
  #    do-udp: yes
  #    do-ip4: yes
  #    do-ip6: yes
  #    prefer-ip6: no

And now, the current lan-based config, in sudo nano /etc/unbound/unbound.conf.d/pi-hole.conf is:

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 own subnets)
  private-address: 192.168.0.0/16
  private-address: 169.254.0.0/16
  private-address: 172.16.0.0/12
  private-address: fd00::/8
  private-address: fe80::/10
  # Access control for LAN and VPN subnets
  access-control: 127.0.0.1/32 allow
  access-control: ::1 allow
  access-control: 10.67.67.0/24 allow
  access-control: 10.99.99.0/24 allow

oemb1905 2025/04/04 03:20

computing/unbounddns.txt · Last modified: 2025/05/04 06:01 by oemb1905