Why not Pi-hole?
Pi-hole shouldn’t need an introduction; it has been a homelab staple for years. However, this popularity has also snuck it into places where it is not needed. If your goal is to self-host a recursive DNS resolver with ad blocking, you’ll be running Unbound anyway. So why not skip the cute web interface and simplify your network by leveraging Response Policy Zones (RPZ) instead? On top of that, Pi-hole is only available on Linux.
This won’t be for everyone, especially if you are unfamiliar with the command line. My goal is just to show a better integrated alternative to Pi-Hole for OpenBSD routers.
Configuring Unbound
Since Unbound is included in base, we do not need to install anything. The configuration takes place in /var/unbound/etc/unbound.conf because it runs in a chroot for extra separation. No Docker needed.
Modify the config to something like:
remote-control:
control-enable: yes
control-interface: /var/run/unbound.sock
server:
interface: 127.0.0.1
interface: ::1
hide-identity: yes
hide-version: yes
root-hints: "/var/unbound/db/named.cache"
prefetch: yes
auto-trust-anchor-file: "/var/unbound/db/root.key"
prefetch-key: yes
# The order of these matters!
module-config: "respip validator iterator"
define-tag: "ads nsfw"
# You might want to change these to your subnets
access-control-tag: 0.0.0.0/0 "ads nsfw"
access-control-tag: ::/0 "ads nsfw"
rpz:
name: oisd-ads
url: https://small.oisd.nl/rpz
tags: "ads"
rpz:
name: oisd-nsfw
url: https://nsfw-small.oisd.nl/rpz
tags: "nsfw"
I’m not going to break this down line by line. If you need help understanding the config, start with the man page.
Validate the config with unbound-checkconf, because rcctl
configtest unbound doesn’t work for whatever reason.
Add the root hints to the chroot
Recursive resolvers need to know where to start looking for DNS records. The root hints file provides the IP addresses of the 13 authoritative root nameservers that anchor the global DNS hierarchy.
cd /var/unbound/db/
ftp https://www.internic.net/domain/named.cache
Starting Unbound
Enable the service and increase the startup timeout. Larger RPZ files will increase the time it takes for the daemon to initialize, so we tell rc that the wait is expected.
rcctl set unbound timeout 60
rcctl enable unbound
rcctl start unbound
Redirect all DNS traffic on your network to Unbound
Then add this little trick to /etc/pf.conf so that all incoming
local traffic on port 53 is routed to unbound. This doesn’t include
the router itself.
pass in on !egress inet proto { udp tcp } from any to self \
port domain rdr-to 127.0.0.1
pass in on !egress inet6 proto { udp tcp } from any to self \
port domain rdr-to ::1
Now run pfctl -f /etc/pf.conf and you should be done!
Or maybe just advertise the name server in dhcpd.conf and rad.conf like a normal person.
Testing
Modern web browsers often try to handle DNS internally (DoH), which can make command-line tests deceiving. So I recommend web-based tests to check if DNSSEC and the RPZ for ad blocking are working.
If these tests fail, check your browser’s “Secure DNS” settings; it might be bypassing the system resolver.
RPZ files
If you have the available memory, I suggest using the big lists from OISD.nl. Another source I’ve found is hagezi/dns-blocklists. Writing your own RPZ should also be self explanatory after you look at the syntax of an already existing zone.
Additional notes
- BIND supports RPZ too but I’ve never used it
- If you want remote access take a look at wg
- YouTube video of Paul Vixie explaining the current state of DNS
- This article was inspired by this guy’s use of Pi-hole
- I found the pf trick on Tedu’s blog