Pihole - Setup Ad-Block and Local DNS with SSL
Pihole - Setup Ad-Block and Local DNS with SSL
Table of Contents
Overview
In this guide, I am going to walk you through how to setup Pi-Hole - a local, recursive DNS server. Below is a list of what we will get setup:
- Setup DNS-over-TLS using Unbound
- Setup a Ad-block by importing a list of domains into Pi-Hole
- Setup Local DNS records to avoid typing IP address and port
- Setup Local SSL certificates to access these local DNS records over HTTPS
Prerequisites:
You need to have the following:
- A linux VM / machine
- Have Docker installed on the machine
- Have a Portainer container installed (this is mandatory)
- Some patience
A quick warning:
From here on out things are going to get complicated, you may face random inexplicable errors and quite literally lose your mind. Be prepared before you undertake this project
Configuring Pi-Hole
Installing Pi-Hole + Unbound
Now that you are well aware of what’s ahead let’s finally proceed with the installation of Pi-Hole.
We are going to be doing this using docker compose and this is where Portainer’s Stack implementation comes in handy:
- Navigate to your portainer instance
- On the left menu, click
stack
- Then click
Add a Stack
- And copy paste the following into the text window (READ THE COMMENTS):
version: '3.3'
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
ports:
- "10.0.0.50:53:53/tcp"
- "10.0.0.50:53:53/udp"
- "10.0.0.50:8080:80/tcp"
#Ensure to change above IPs to your server's local IP address
environment:
TZ: 'Europe/London'
WEBPASSWORD: 'super_secure_password'
volumes:
- '/home/user/pihole/pihole:/etc/pihole'
- '/home/user/pihole/dnsmasq:/etc/dnsmasq.d'
cap_add:
- NET_ADMIN # Recommended
restart: unless-stopped
unbound:
container_name: my-unbound
volumes:
- '/home/user/unbound:/opt/unbound/etc/unbound/'
ports:
- '5335:53/tcp'
- '5335:53/udp'
restart: unless-stopped
image: 'mvance/unbound:latest'
DO NOT CREATE any directories. Docker will automatically create then with the right permissions. (Saves us time and avoid any confusion with folder permissions)
- Once entered, click on
Deploy Stack
and wait until the page refreshes - After the page refreshes, navigate to
http://serverip:8080/admin
and login - You should see a dashboard like this:
✅ With that you have successfully installed Pi-Hole. Scroll down to see how to configure Pi-Hole
Configuring Unbound in Pi-Hole
We now need to add the unbound container into Pi-hole’s upstream DNS as that container provides DNS-over-TLS preventing ISPs from spying on our traffic!
What you need to do:
- Navigate to your portainer instance and log in
- Note down the IP address of your
unbound
container as highlighted below:
- Now go back to Pi-Hole admin dashboard and on the left menu click on
settings
and navigate toDNS
- Unselect all the preset Upstream DNS servers
- And on right column, under Custom 1 enter this:
172.27.0.3#53
, making sure to replace IP with IP you noted down and leaving 53 as it is:
✅ And with that, you have successfully added the Unbound container as the upstream DNS server for Pi-Hole. We now need to test whether this actually works!
Testing whether this actually works
- On your android phone, navigate to your wifi settings
- Change your wifi connection settings from
DHCP
tooStatic IP
- Enter in an
IP address
, yourrouter gateway address
(it’s normally something like192.168.1.1
), and enter the IP of the server running Pi-Hole in the entries for DNS 1 and DNS 2. - Head over to your preferred browser, and try to open a website. If the website loads, we know that Pi-Hole is configured correctly!
If it doesn’t load, please do not panic. KEEP CALM. Remember what i said earlier. Look back through the steps and see where you may have gone wrong!
Configuring Blocklists within Pi-Hole
Now that the Internet is accessible and the DNS is setup correctly, we can now import our ad-lists.
Adding domain block-lists
One of the best websites to source ad-block lists is firebog. It has an extensive collection of lists along with categorisation depending upon how much they may interrupt your browsing experience. Make you to carefully read what is says on the website.
Please DO NOT add the lists that have been striked-through: they are deprecated and no longer maintained
Pi-Hole has made it very easy to copy and paste the ad-lists. Observe the GID below to see how to add them:
Once you have added your desired blocklist links, navigate to tools
on the left hand menu and click on Update Gravity
(I have no idea why it’s called that)
Then click on update gravity
button on the page and wait as pi-hole scans through the lists and adds the domains into its database. It may take some time depending on your blocklist choices.
Adding Whitelists
-
To add domain whitelists, navigate to
Group Management
and click on domains. -
Open the
txt file
link on the firebog site add copy and paste them into the text box. -
ENSURE YOU CLICK ON ADD WHITELIST
-
Once you have done, head over to portainer and restart the pihole container. You should now notice ads are being blocked on the websites you visit!
Adding Local DNS records and setting up HTTPS
Implementation options
There are two different ways this can be implemented:
- You can add local DNS records to Pi-Hole to point to IP address of services (note that you will still have to enter the port number every time you visit the website) and install the SSL certificates through the service’s Web UI
OR…
-
You can utilize a reverse proxy manager like NGINX Proxy Manager to create a setup as described below:
-
Have all local DNS records pointing to one IP which belongs to the server hosting NGINX Proxy Manager
-
Configure Proxy Hosts on NGINX Proxy Manager that forwards the domain to a specific IP and port (thus removing the need for you to enter port numbers)
-
Install one wildcard SSL Certificate on NPM to handle SSL of all local DNS record
-
My Opinion
In my personal opinion, I think that the latter is undoubtedly the easiest option for the following reasons:
- Only need to create one wildcard SSL certificate
- No need to remember port numbers
Due to aforementioned reasons, I will proceed to demonstrate how to setup the latter option.
Installing NGINX Proxy Manager
On your browser, login to Portainer and navigate to Stacks
Click on Add a Stack
and paste the following in the text window:
version: "3"
services:
app:
container_name: npm
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
volumes:
- /home/user/npm/data:/data
- /home/user/npm/letsencrypt:/etc/letsencryp
Now click Deploy Stack
Once the page refreshes, go to http://serverip:81
and login with the default credentials and change them:
username: [email protected]
password: changeme
✅ You have now successfully installed NGINX Proxy Manager
Creating your own Self-Signed SSL Certificates
An explainer into how all this works
You know how there are services like Let’s Encrypt offering SSL Certificates that work on the WAN and the World Wide Web? Well we are going become our own Certificate Authority like let’s encrypt, and we will sign our own ssl certificates with our custom CA (Certificate Authority) certificate.
This is how the system works:
On almost every device, certificates of “trusted Root Certificate Authorities” are stored, so that when you visit a HTTPS website, and the SSL it presents is signed by one of these “Trusted Root Certificate Authorities”, the device/ browser will automatically trust it and display the lock symbol.
If you want to see these “Trusted Root Certificate Authorities” for yourself, follow the steps below:
On windows:
Press on the start button, and search certificates
and you should see an app called “Manage Computer Certificates”
Click on that, and on the left menu, you should see “Trusted Root Certificate Authorities”.
Click on that and you should see a massive dropdown with names of many different companies and familiar ones such as Microsoft, Amazon, Google etc., This means that any website that presents SSLs, signed by any of these Root CAs, will be instantly trusted.
To create our own SSL certs for local domains, we need to first create a Certificate Authority file, which will then sign our generated SSLs, I will explain the process as we go ahead.
How we will implement this on our local LAN:
-
Using our local DNS resolver Pi-Hole, we will create custom domains (something like kvis.network) to point to an IP address running a particular services
-
We will create a Certificate Authority File and add it to the “Trusted Root Certificate Authority” store of the devices you will be using to visit these custom domains**
-
We will generate SSL certs that are signed by our CA file
Steps to create your own CA (Certificate Authority)
We are going to be doing using an open source implementation of the TLS and HTTPS protocol known as OpenSSL
Installing OpenSSL:
On your Ubuntu/Debian Machine, type sudo apt install openssl
, and press y
to install it.
Once that is done, run the following commands:
This is where stuff gets confusing. Please follow along carefully! I will try my best to explain it
Generating our own CA Certificate:
Let’s first create a directory where we will store the files:
sudo mkdir ssl-certs
cd ssl-certs
Even though I talked about a CA being a company and whatnot, and the end of the day, it is simply a certificate file. So here is how to create that CA file:
We first need to generate an RSA private key file for our Certificate Authority. This file is called ca-key.pem
. It is private key of the CA.
openssl genrsa -aes256 -out ca-key.pem 4096
You will be prompted to enter a passphrase.
As this is the “signer” of our SSLs, it is important to keep this file safe, and thus when you are prompted to enter a passphrase, enter a secure one, and please do not forget it
Once that is done, and you enter ls
, you should see the file.
We now need to create the public key file for our Certificate Authority. This is done by entering the following command:
openssl req -new -x509 -sha256 -days 5475 -key ca-key.pem -out ca.pem
Breakdown:
req
is to request the creation of a new Certificate Authoritynew
specifies creation of anew
CAx509
specifices the standard to use for creating the CA.x509
is the set standard for HTTPS and SSL certssha256
specifies the encryption algorithmdays 5475
specifies the duration that this CA is going to valid for. Set this to something long, as you will be installing this file in the Trusted Root Cert Store of deviceskey
specifies the private key file to use, in this caseca-key.pem
to create the public keyout
specifies the name of the output public key file. This isca.pem
You will now have two files. One private key file for your CA and one public key file for your CA. Make your you don’t lose these files.
✅ And with that, you have created your very own Certificate Authority! Proceed ahead to see how to create SSL certs that will be signed by your custom CA.
Generating self-signed SSLs:
We now need to create the private key file of our SSL certificate. To do this, enter the following:
openssl genrsa -out cert-key.pem 4096
This will output a file called cert-key.pem
, which is the private key file of our SSL certificate.
We now create what is known as a Certificate Signing Request file, so that we can generate a public key file for the SSL certificate that has been signed by the CA files. To do this enter the following command:
In "/CN-yourcn"
, enter any name you fancy, for example FirstNameInitialLastnameInitial Certificate Authority
openssl req -new -sha256 -subj "/CN=yourcn" -key cert-key.pem -out cert.csr
We now need to create a file that contains the domains / IP address we want to create the SSL Certificate for:
echo "subjectAltName=DNS:kvis.network,DNS:*.kvis.network,IP:192.168.3.250" >> extfile.cnf
You can enter as many DNS entries as you want. The *.kvis.network
is a wildcard domain so anything.kvis.network
will work. If you want something.anything.kvis.network
to be authenticated with this SSL, you need add an entry like so: DNS:*.anything.kvis.network
.
As previously mentioned, I would recommend creating wildcard SSLs, so you wouldn’t have to create a new SSL everytime you want create a SSL for a service
This is the step where you create the public key file of the SSL certificat
openssl x509 -req -sha256 -days 5475 -in cert.csr -CA ca.pem -CAkey ca-key.pem -out cert.pem -extfile extfile.cnf -CAcreateserial
You will be prompted to enter a passphrase. Enter the passphrase you used to secure your CA private key file.
This will output a file called cert.pem
which is the public key file of the SSL certificate and has also been signed by your CA.
You are not done just yet. A few more cleaning up commands and your certificate will be ready.
For the SSL cert public key file, we need to create what is known as a chain file, which is quite simply the ca.pem file joined together with the cert.pem file. To do this enter the following commands:
cat cert.pem > fullchain.pem
cat ca.pem >> fullchain.pem
✅ With that you have successfully created a SSL certificate that has been signed by your own CA!
To organise everything I recommend creating a directory for each domain you create a certficate for. Let’s say i created a certificate for *.kvis.network
, I would create a directory for that by running mkdir wildcard.kvis.network
.
Next we need move the required files into that directory. Do this by running the following line by line:
mv fullchain.pem ./wildcard.kvis.network/fullchain.pem
mv cert-key.pem ./wildcard.kvis.network/cert-key.pem
mv extfile.cnf ./wildcard.kvis.network/extfile.cnf
##the only reason i copied the extfile.cnf file is because i
## can see which domains this ssl certificate is for
We can now remove some unnecessary files by running this command:
rm cert.csr cert.pem
With that, you are actually finally done. Ugh that was painful wasn’t it? Once you get to grips with how it works you will be just fine.
Ye um, I lied we aren’t done yet. We need to add the ca.pem
file into the Trusted Root Certificate Authority Store. Soooo, here’s how to do it:
Adding your CA to Trusted Root CA store of devices
On Windows:
Assuming the path to your generated CA certificate as C:\ca.pem
, run:
Import-Certificate -FilePath "C:\ca.pem" -CertStoreLocation Cert:\LocalMachine\Root
- Set
-CertStoreLocation
toCert:\CurrentUser\Root
in case you want to trust certificates only for the logged in user.
OR
In Command Prompt, run:
certutil.exe -addstore root C:\ca.pem
certutil.exe
is a built-in tool (classicSystem32
one) and adds a system-wide trust anchor.
On Android:
The exact steps vary device-to-device, but here is a generalised guide:
1 Open Phone Settings
2 Locate Encryption and Credentials section. It is generally found under Settings > Security > Encryption and Credentials
3 Choose Install a certificate
4 Choose CA Certificate
5 Locate the certificate file ca.pem on your SD Card/Internal Storage using the file manager.
6 Select to load it.
7 Done!
On Debian & Derivatives (Ubuntu):
- Move the CA certificate (
ca.pem
) into/usr/local/share/ca-certificates
/ca.crt. - Update the Cert Store with:
sudo update-ca-certificates
Refer to the documentation here and here
On Fedora:
- Move the CA certificate (
ca.pem
) to/etc/pki/ca-trust/source/anchors/ca.pem
or/usr/share/pki/ca-trust-source/anchors/ca.pem
- Now run (with sudo if necessary):
update-ca-trust
Refer the documentation here.
On Arch
System-wide – Arch(p11-kit) (From arch wiki)
- Run (As root)
trust anchor --store myCA.crt
- The certificate will be written to /etc/ca-certificates/trust-source/myCA.p11-kit and the “legacy” directories automatically updated.
- If you get “no configured writable location” or a similar error, import the CA manually:
- Copy the certificate to the /etc/ca-certificates/trust-source/anchors directory.
- and then
update-ca-trust
wiki page here
On Firefox:
Click on the top-right Hamburger Menu, and click on settings.
On the left menu, select Privacy and Security
Scroll down until you see certifcates. Click on that and select Import CA
. This should pop up a file browser. Navigate to your ssl-certs folder and select your ca.pem
file to import your CA into firefox.
You only need to do this for firefox as it seems to ignore the windows Trusted Root CAs.
Adding Local DNS on Pi-Hole
To set local DNS records, do the following:
- Login to Pihole Admin dashboard
- On the left menu click on
Local DNS
- On the dropdown click on ‘DNS Records’
- On the main page, enter a domain (I would advise you stick with
.network
,.local
for the TLDs):
Configuring Proxy Host in NPM
- Head over to your NGINX Proxy Manager admin dashboard
- Click on
Proxy Hosts
- Click on
Add Proxy Hosts
- Fill it in like so:
-
Press
Save
-
Now navigate to the SSL Tab
-
On the top right of the page click on add SSL
-
On the dropdown menu select custom
-
In the window enter a name for the cert
-
For
Certificate Key
make sure to upload thecert-key.pem
file -
For
Certificate
make sure to upload thefullchain.pem
file -
Press save
-
If everything went well your SSL should have added and should display an expiry date
-
Now navigate back to Proxy Hosts
-
Click on
whatever.network
and navigate to SSL tab -
On the dropdown menu select the SSL you uploaded and click save.
✅ Now try visiting https://whatever.network
on your browser and you should have a lock symbol with no insecure connection
warnings displayed.
YOU ARE OFFICIALLY DONE. This project must have been literal hell so congrats ! You did it! If you found it easy, well, good for you I guess
Useful Links:
Visit my blog
Made with immense effort and 20686 characters by KV Internet Services