Ever want to get rid of all those annoying internet ads? Me too. I’d been planning on using something like Pi-hole, but recently stumbled upon unbound-adblock, which is perfect for my OpenBSD home router. The setup is very well-documented there, but I provide my own, slightly-modified setup instructions here. Most notably, the cron-job runs at 6:30 AM, not midnight, and I refined the PF rule which redirects DNS requests to Google’s DNS servers.

Prepare

First, set your expectations. Get a base-line of what ads should be blocked by working through the test steps on https://ads-blocker.com/testing.

Disable any browser-based ad-blockers you might have enabled, otherwise you won’t be able to verify your setup actually blocks ads.

Configure

ftp https://www.geoghegan.ca/scripts/unbound-adblock.sh

Install the script with the appropriate permissions.

install -g bin -m 644 -o root unbound-adblock.sh /usr/local/bin/ (1)
1 The script will be owned by root, belong to the bin group, and be readable by everyone and writeable by the owner.

Create a new user, _adblock, who will not be allowed to login.

useradd -s /sbin/nologin _adblock

Install an empty adblock.conf file owned by the _adblock user which is readable by everyone but only writeable by the owner.

install -m 644 -o _adblock /dev/null /var/unbound/etc/adblock.conf

Include the adblock.conf file at the end of the server section.

/var/unbound/etc/unbound.conf
server:
	...
	# Include the unbound-adblock configuration file.
	include: /var/unbound/etc/adblock.conf

Allow the _adblock user to reload the unbound service without requiring a password.

/etc/doas.conf
# Allow the unbound-adblock user / script to reload the unbound service.
permit nopass _adblock cmd rcctl args reload unbound

Edit the _adblock users crontab file.

crontab -u _adblock -e

Add a rule to run the unbound-adblock.sh script every morning at 6:30.

/var/cron/tabs/_adblock
# use /bin/sh to run commands, no matter what /etc/passwd says
SHELL=/bin/sh

# Update unbound-adblock at 6:30 every morning.
30	6	*	*	*	/usr/local/bin/unbound-adblock.sh

Run the unbound-adblock.sh script once.

doas -u _adblock sh /usr/local/bin/unbound-adblock.sh

Restart the unbound service.

rcctl restart unbound

Add a PF rule to redirect all DNS queries bound for Google’s DNS servers, 8.8.8.8 and 8.8.4.4 to your unbound DNS server, i.e. 192.168.1.1. The example below uses a macro lan_if to represent the LAN network interface, em1.

/etc/pf.conf
# Redirect any DNS requests to Google's DNS servers to the LAN's unbound server.
lan_if = "em1"
lan_dns_server = 192.168.1.1
table <google_dns_servers> { 8.8.8.8 8.8.4.4 }
...
pass in quick on $lan_if quick inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $lan_dns_server port domain
Be careful to position this rule in a place where it is not superseded by a preceding quick rule.

My actual configuration is a bit more complex, but I have included it here for reference.

/etc/pf.conf
lan1_if = "em1"
lan2_if = "em2"
lan3_if = "em3"
lan4_if = "em4"
lan5_if = "em5"
secure_wifi_if = "vlan2"
guest_wifi_if = "vlan3"

lan1_dns_server = 192.168.1.1
lan2_dns_server = 192.168.2.1
lan3_dns_server = 192.168.3.1
lan4_dns_server = 192.168.4.1
lan5_dns_server = 192.168.5.1
secure_wifi_dns_server = 192.168.6.1
guest_wifi_dns_server = 192.168.7.1

table <google_dns_servers> { 8.8.8.8 8.8.4.4 }
...
pass in quick on $lan1_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $lan1_dns_server port domain
pass in quick on $lan2_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $lan2_dns_server port domain
pass in quick on $lan3_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $lan3_dns_server port domain
pass in quick on $lan4_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $lan4_dns_server port domain
pass in quick on $lan5_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $lan5_dns_server port domain
pass in quick on $secure_wifi_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $secure_wifi_dns_server port domain
pass in quick on $guest_wifi_if inet proto { tcp udp } to <google_dns_servers> port domain rdr-to $guest_wifi_dns_server port domain
...

Reload the updated PF rules.

pfctl -f /etc/pf.conf

Verify

Test your new-found adblocking by revisiting the test steps. Those pesky ads should have mystically vanished.