DNS ad sinkhole on a Raspberry Pi 3 with FreeBSD
20 October 2021
This is similar in concept to a Pi-Hole, only much lighter weight. We'll be using:
- FreeBSD 13 RPI image:
-
local-unbound(8)
- It's in the base install of FreeBSD
-
adblock-unbound
- https://github.com/lepiaf/adblock-unbound
- It's a 20-line shell script.
- git
- wget
- doas
Start by grabbing the FreeBSD-13.0-RELEASE-arm64-aarch64-RPI.img.xz
image from the link above and
verifying the SHA512 checksum. Then, uncompress it and write it to the microSD card you'll be using in
your RPI3. I won't go over any of this here because the exact process depends on your OS.
Once your RPI3 is booted up, SSH in with user freebsd
and password freebsd
. The first thing
you should do is change the root password and the password for the default user (freebsd
). The
default root
password is root
.
$ passwd
$ su
# passwd
I also suggest using keys for SSH instead of passwords, but you can find tons of guides on setting that up elsewhere, so I won't go into it.
Let's set up a static IP address and change the hostname, and also enable ntpd
for time.
You don't want the IP to change, because then everything will explode.
Open up /etc/rc.conf
# vi /etc/rc.conf
Either add these lines if they're missing or change them if they already exist and say something different.
You'll want to set defaultrouter
to the IP of your router/gateway, and then set the value after
inet
on the ifconfig_ue0
line to something outside of your router/gateway's DHCP pool.
Be sure to comment out that DHCP line.
hostname="spongebob"
ntpd_enable="YES"
ntpd_sync_on_start="YES"
defaultrouter="192.168.1.1"
#ifconfig_ue0="DHCP"
ifconfig_ue0="inet 192.168.1.69 netmask 255.255.255.0"
Now, we need to install all the fun latest patches and whatnot. This will take a minute, and you'll be prompted
to respond a couple of times. Then, reboot the pi after it's completed. When it comes back up, ssh
back in (to the IP you put above) and su
back to the root user.
# freebsd-update fetch install
Once we're back up and you're root
again, install wget
, git
, and doas
.
It's going to prompt you to install pkg
, say y
and let it do its thing.
# pkg install wget git doas
To set up doas
, which is an alternative to sudo
developed by the OpenBSD project, we need to create
doas.conf
and just add two lines to it.
# vi /usr/local/etc/doas.conf
permit nopass keepenv root
permit persist keepenv freebsd
Now we're going to grab the adblock-unbound
sources, then move it to where it's going to live.
We don't really need --depth=1
here because there's a whopping 5 commits in the
adblock-unbound
repo, but I do it out of habit when I'm just grabbing something and won't be
actually working on it (and don't need the full history).
# git clone --depth=1 https://github.com/lepiaf/adblock-unbound.git
# chmod +x adblock-unbound/entrypoint.sh
# mv adblock-unbound /usr/local/bin/
Okay, time to set up local-unbound
. First, we're going to switch to local-unbound
's
config directory. Next, we'll grab the root hints file named.cache
. Then we'll grab the root key
so we can deal with DNSSEC.
# cd /var/unbound
# ftp ftp://ftp.internic.net/domain/named.cache
# local-unbound-anchor -a root.key
Time to add the actual configuration for local-unbound
.
# vi unbound.conf
There's a lot of options here, basically we're telling it to hide info about our server here, send the minimum
amount of info with DNS queries, and only allow connections from the local network. We're also telling it
where the blocklist will live (we'll get to that next). Put this in the file and save it. If you want more info
about what everything means, you can check man unbound.conf
(or look at the documentation linked
at the bottom of this post).
server:
username: unbound
directory: /var/unbound
chroot: /var/unbound
pidfile: /var/run/local_unbound.pid
auto-trust-anchor-file: /var/unbound/root.key
root-hints: /var/unbound/named.cache
access-control: 0.0.0.0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 192.168.1.0/24 allow
logfile: /var/unbound/unbound.log
interface: 0.0.0.0
port: 53
do-ip4: yes
do-ip6: yes
do-tcp: yes
do-udp: yes
hide-identity: yes
hide-version: yes
qname-minimisation: yes
unblock-lan-zones: yes
insecure-lan-zones: yes
include: /var/unbound/conf.d/ads.conf
remote-control:
control-enable: yes
control-interface: /var/run/local_unbound.ctl
control-use-cert: no
We need to fetch the initial blocklist. Run these three commands:
# mkdir conf.d
# RECIPE=master WITH_IPV6=false /usr/local/bin/adblock-unbound/entrypoint.sh > /var/unbound/conf.d/ads.conf
# chown -R unbound:unbound conf.d
Let's add the script that cron will call to update our block list. I know it doesn't really need to be
executable because we can always just call it with sh
. Add the lines to the file and save it.
# touch adblock-refresh.sh
# chmod +x adblock-refresh.sh
# chown unbound:unbound adblock-refresh.sh
# vi adblock-refresh.sh
RECIPE=master WITH_IPV6=false /usr/local/bin/adblock-unbound/entrypoint.sh > /var/unbound/conf.d/ads.conf
local-unbound-control reload
Now let's set local_unbound
to start at boot and actually start it up.
# sysrc local_unbound_enable="YES"
# service local_unbound start
Now let's try to see if it works. Run this from your personal computer, not the Pi.
$ dig @192.168.1.69 +dnssec gbmor.dev
Did it work? Cool. Now we need to tell cron to update our fun little block list. I have it set to run at 10:00
UTC which is currently 6AM for me here on the east coast of the US, since we're in DST. Crack open a cold one the crontab,
then add the magic line to it and save.
# doas -u unbound crontab -e
0 10 * * * /bin/sh /var/unbound/adblock-refresh.sh >/dev/null 2>&1
And, uh, yeah. We're basically done with the setup on the Pi. Now you need to go into your router's admin panel, go to the DHCP settings, and set the IP of your Pi, such as 192.168.1.69, as the DNS server to advertise to connected clients. I use an Asus RT-AC66U, if you use the same or similar, log in to the admin panel and then:
Advanced Settings > LAN
DHCP Server tab
DNS and WINS Server Setting
DNS Server --> 192.168.1.69
Apply
Then disconnect whatever devices you have and reconnect them to the network.
Boom.