Building Pi-Holes Automatically
I saw some people online writing about how they’ve been using a “Pi-Hole” (https://pi-hole.net/) to block adverts online.
It looked like a neat idea, it works like an Adblocker you’d install in your browser but at a DNS level running on a Raspberry Pi.
The basic idea is that:
- Your browser needs to make a DNS request to lookup the IP address of the advertising networks to request content from them to display
- So you configure your devices to use your Raspberry Pi running Pi-Hole as a DNS server to perform DNS lookups
- If the Pi-Hole receives a DNS request for a known advertising network it won't resolve it to the real IP, so your browser will never be able to load and render the advert
For non advertising domains that you actually want to resolve you can configure the Pi-Hole to use any upstream DNS resolver you like.
Cloudflare have started offering a DNS resolver that implements DNS Over HTTPS (https://developers.cloudflare.com/1.1.1.1/dns-over-https/) and provide a proxy called Cloudflared that sorts out all of the low level plumbing for interacting with it. So I decided to use that as the upstream resolver.
You can just download the binaries, configure them following the various instructions available online and it all works.
However I wanted to:
- Run them inside of Docker containers
- Automate setting them up as if the Pi-Hole broke my entire home network would stop working and I’d need to rebuild one as quickly as possible
The first one was easy, both projects provide official Docker images that I could use and I can plumb them together using Docker Compose.
This just left the setup automation, I wanted to use Chef but there is no Chef client package for ARM.
A quick Google suggested that if I installed a new enough version of Ruby I could just install the Chef gem.
Chef 14 requires Ruby 2.5 which wasn’t available to install via apt
but fortunately you can just build it yourself from source, it just takes around 20 minutes.
I found a few examples online of people doing this and adapted them to make a script that I could run on a fresh installation of Raspbian to install Chef and its dependencies.
Then with Chef installed I just needed to specify some config options in a client.rb
file and provide it with a list of cookbooks to run in bootstrap.json
All the scripts to bootstrap Chef onto a Pi are in this repo here — https://github.com/AaronKalair/pi_chef_bootstrap
Next I just needed to write a cookbook to configure everything.
The cookbook:
- Installs Docker using the community cookbook — https://github.com/chef-cookbooks/docker
- Install Python and Pip with Poise Python — https://github.com/poise/poise-python so that I can
pip install docker-compose
- Configures some directories on the host that are then mounted inside the container for storing persistent data between container restarts
- Setups a SystemD service to run the
docker-compose
project so that it is launched on system boot and restarted shoulddocker-compose
crash. - Writes the IP address of the Pi to a file that SystemD uses to set an environment variable for the service as Pi-Hole requires knowledge of it’s IP address to function
- Drops a
docker-compose
configuration file onto the Pi that runs the Pi-Hole and Cloudflared containers with static ips so that we can reference Cloudflared as the upstream DNS provider in Pi-Hole
The only remaining question was then how to run it on the Pi without having to setup a centralised Chef server.
Berks
can vendor the cookbook and all of its requirements into a folder, this gets transferred onto the Pi with scp
and then Chef is ran in local mode which doesn’t require a centralised server:
sudo chef-client -z -j bootstrap.json -c client.rb
And that’s it, I can now take a Pi with a fresh installation of Raspbian, bootstrap Chef on it and then run Chef cookbooks to configure Pi-Hole in a consistent, replicable and automated way!