Docker - Pihole Container
This blog post will detail how to create a custom pihole container with a pre-loaded script to update our block and allow lists automatically.
To have this custom container we will have to create our own custom docker image.
First let's create our custom Dockerfile
:
# Use the official pihole image as a base.
FROM pihole/pihole:latest
# Install custom software.
RUN apt-get update && apt-get install -Vy php-cli php-sqlite3 php-intl php-curl python3 vim
# Install the scripts to update our pihole automatically.
RUN wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | bash
The Dockerfile
above uses the base pihole image as base and a few custom software are added. I have installed python3
and vim
to help with future automation and manipulation of config files inside the container.
Let's create our image :
root@home-svr1:~/docker-projects/pihole# docker build --no-cache --pull --network=macvlan10 -t "pihole:Dockerfile" .
Sending build context to Docker daemon 30.46MB
Step 1/3 : FROM pihole/pihole:latest
latest: Pulling from pihole/pihole
Digest: sha256:3a39992f3e0879a4705d87d0b059513af0749e6ea2579744653fe54ceae360a0
Status: Image is up to date for pihole/pihole:latest
---> eb777ee00e0c
Step 2/3 : RUN apt update && apt install -Vy php-cli php-sqlite3 php-intl php-curl python3 vim
---> Running in 53c8cf3d6772
---> 5c1a0664a6ad
Step 3/3 : RUN wget -O - https://raw.githubusercontent.com/jacklul/pihole-updatelists/master/install.sh | bash
---> Running in 1e83d9d23a6e
Removing intermediate container 1e83d9d23a6e
---> 45d9d94fd232
Successfully built 45d9d94fd232
Successfully tagged pihole:Dockerfile
root@home-svr1:~/docker-projects/pihole# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
pihole Dockerfile 45d9d94fd232 15 minutes ago 432MB
pihole/pihole latest eb777ee00e0c 6 weeks ago 333MB
I have struggled badly to have the image connecting to the internet and pulling the updates. After hours of research, I have finally stumbled upon this article and with the help of the docker's documentation, the image has finally been created.
We need to point a working network to the docker build command for RUN to work.
We can now create our docker container configuration file :
root@home-svr1:~/docker-projects/pihole# touch docker-compose.yml
And, add the below configuration to it :
Two options that are interesting to be aware of are :
image:
This option tells the docker-compose which image to use and in this case, we are point to our newly created image.
network:
In the service section we defined the name of the network to be used as long with the IP address we want our container to use and in the global configuration, we used the external
flag to tell docker-compose to use an already created network.
dns:
This option had to be added otherwise my container was trying to use the macvlan10 gateway as its DNS server. It seems that if not specified docker is going to use the host DNS server instead of the macvlan
gateway.
Before running docker-compose we are going to test our config for any errors with the command :
root@home-svr1:~/docker-projects/pihole# docker-compose config
networks:
macvlan6:
external: true
services:
pihole:
cap_add:
- NET_ADMIN
container_name: pihole
dns:
- 192.168.6.1
environment:
TZ: Europe/London
hostname: pihole
image: pihole/pihole:latest
networks:
macvlan6:
ipv4_address: 192.168.6.10
ports:
- 53:53/tcp
- 53:53/udp
- 67:67/udp
- 80:80/tcp
- 443:443/tcp
restart: unless-stopped
volumes:
- /root/docker-projects/pihole/etc-pihole:/etc/pihole:rw
- /root/docker-projects/pihole/etc-dnsmasq.d:/etc/dnsmasq.d:rw
- /root/docker-projects/pihole/etc-pihole-updatelists:/etc/pihole-updatelists:rw
version: '3.0'
root@home-svr1:~/docker-projects/pihole#
Our configuration passed without any errors and we are ready to create our container.
root@home-svr1:~/docker-projects/pihole# docker-compose up --detach
Starting pihole ... done
We shall see our container running :
root@home-svr1:~/docker-projects/pihole# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2ec52935049b pihole:Dockerfile "/s6-init" About a minute ago Up About a minute (healthy) pihole
Let's connect to it and confirm that the update script is running as expected and change the default generated password :
root@home-svr1:~/docker-projects/pihole# docker exec -ti pihole bash
root@pihole:/# pihole-updatelists
Pi-hole's Lists Updater by Jack'lul
https://github.com/jacklul/pihole-updatelists
Opened gravity database: /etc/pihole/gravity.db (4.36 MB)
No remote lists are set in the configuration - this is required for the script to do it's job!
See README for instructions.
Updating Pi-hole's gravity...
[i] Neutrino emissions detected...
[✓] Pulling blocklist source list into range
[✓] Preparing new gravity database
[i] Using libz compression
[i] Target: https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
[✓] Status: Retrieval successful
[i] Received 77608 domains
[✓] Storing downloaded domains in new gravity database
[✓] Building tree
[✓] Swapping databases
[i] Number of gravity domains: 77608 (77608 unique domains)
[i] Number of exact blacklisted domains: 0
[i] Number of regex blacklist filters: 0
[i] Number of exact whitelisted domains: 0
[i] Number of regex whitelist filters: 0
[✓] Cleaning up stray matter
[✓] DNS service is listening
[✓] UDP (IPv4)
[✓] TCP (IPv4)
[✓] UDP (IPv6)
[✓] TCP (IPv6)
[✓] Pi-hole blocking is enabled
Finished with 1 error(s) in 3.35 seconds.
root@pihole:/# sudo pihole -a -p
Enter New Password (Blank for no password):
Confirm Password:
[✓] New password set
root@pihole:/#
The update script is working, however, it seems that there is a small bug with it. Instead of using the mapped folder, it is using the configuration file at /etc/pihole-updatelists.conf as seen below :
root@pihole:/etc/cron.d# cat /usr/local/sbin/pihole-updatelists | grep lists.conf
'CONFIG_FILE' => '/etc/pihole-updatelists.conf',
I have changed the line as seen on the image above and a bug report is going to be opened with the developer.
As you might have noticed we now have three new folders inside our docker project directory. These folders are mapped to our pihole container and should be used to back up our configs before an upgrade and to edit the lists and configurations of the script running in our container.
root@home-svr1:~/docker-projects/pihole# ll
total 28
drwxr-xr-x 5 root root 4096 Apr 2 11:24 ./
drwxr-xr-x 3 root root 4096 Mar 31 21:30 ../
-rw-r--r-- 1 root root 640 Apr 2 11:23 docker-compose.yml
-rw-r--r-- 1 root root 212 Apr 2 00:08 Dockerfile
drwxr-xr-x 2 root root 4096 Apr 2 11:24 etc-dnsmasq.d/
drwxrwxr-x 3 systemd-coredump systemd-coredump 4096 Apr 2 11:43 etc-pihole/
drwxr-xr-x 2 root root 4096 Apr 2 11:43 etc-pihole-updatelists/
The last step is going to be to set up our custom lists editing the file pihole-updatelists.conf inside our container mapped folder as below :
; Remote list URL containing list of adlists to import
ADLISTS_URL="https://v.firebog.net/hosts/lists.php?type=tick"
; Remote list URL containing exact domains to whitelist
WHITELIST_URL="https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt"
; Remote list URL containing regex rules for blacklisting
REGEX_BLACKLIST_URL="https://raw.githubusercontent.com/mmotti/pihole-regex/master/regex.list"
; Comment string used to know which entries were created by the script
; You can still add your own comments to individual entries as long
; you keep this string intact
COMMENT="Managed by pihole-updatelists"
; Maximum time in seconds one list download can take before giving up
; You should increase this when downloads fail because of timeout
DOWNLOAD_TIMEOUT=180
I am using a conservative set of blocking rules, but you can check the external links at the end of the article for more aggressive blocking lists.
Let's apply our lists :
root@home-svr1:~/docker-projects/pihole# docker exec -ti pihole bash
root@pihole:/# pihole-updatelists
Pi-hole's Lists Updater by Jack'lul
https://github.com/jacklul/pihole-updatelists
Opened gravity database: /etc/pihole/gravity.db (4.36 MB)
Fetching ADLISTS from 'https://v.firebog.net/hosts/lists.php?type=tick'... done (30 entries)
Processing... 30 inserted
Fetching WHITELIST from 'https://raw.githubusercontent.com/anudeepND/whitelist/master/domains/whitelist.txt'... done (191 entries)
Processing... 191 inserted
Fetching REGEX_BLACKLIST from 'https://raw.githubusercontent.com/mmotti/pihole-regex/master/regex.list'... done (14 entries)
Processing... 14 inserted
Updating Pi-hole's gravity...
[✓] Storing downloaded domains in new gravity database
[✓] Building tree
[✓] Swapping databases
[i] Number of gravity domains: 373804 (271031 unique domains)
[i] Number of exact blacklisted domains: 0
[i] Number of regex blacklist filters: 14
[i] Number of exact whitelisted domains: 191
[i] Number of regex whitelist filters: 0
[✓] Cleaning up stray matter
[✓] DNS service is listening
[✓] UDP (IPv4)
[✓] TCP (IPv4)
[✓] UDP (IPv6)
[✓] TCP (IPv6)
[✓] Pi-hole blocking is enabled
Finished successfully in 24.22 seconds.
Before the conclusion of our set up let's change the upstream DNS servers pihole is using to resolve the queries. It comes set with google by default, but I prefer to use the more privacy-focused quad9 servers.
Although our pihole is serving DNS to our network if you are not "hijacking" the DNS traffic and forwarding it to your pihole hosts inside your network can bypass your pihole and go to the internet to query its servers.
Let's imagine our smart TV trying to query a domain to serve ads and it failing with your network's DNS then a hardcoded DNS server is queried instead like google and since your network is not redirecting DNS traffic to your pihole the ad is going to be served and of course that's not the behaviour that we expect from the devices inside our network, for this reason in a future article we are going to be forwarding all DNS traffic to our pihole preventing hosts bypassing it.
At this point, your pihole should be serving DNS to your network like mine is. I hope that my guide helps you with your set up also I would like to thank my colleague Geraldo for pointing me in the right direction when selecting blocking lists.
Below there are a set of handful links used as the source for this article :