Pihole - Setup Ad-Block and Local DNS with SSL

How-To Sep 3, 2022

Pihole - Setup Ad-Block and Local DNS with SSL - HedgeDoc

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:

  1. A linux VM / machine
  2. Have Docker installed on the machine
  3. Have a Portainer container installed (this is mandatory)
  4. Some patience

A quick warning:

: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'

:warning: 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:

pihole_first_dashboard

✅ 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:

unbound_IP_address

  • Now go back to Pi-Hole admin dashboard and on the left menu click on settings and navigate to DNS
  • Unselect all the preset Upstream DNS servers

DNS_Server_unselected

  • 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:

Custom_DNS_Entry

✅ 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 too Static IP
  • Enter in an IP address, your router gateway address (it’s normally something like 192.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.

:warning: 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:

image alt

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:

  1. Only need to create one wildcard SSL certificate
  2. 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

:information_source: 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:

:warning: 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.

:warning: 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 Authority
  • new specifies creation of a new CA
  • x509 specifices the standard to use for creating the CA. x509 is the set standard for HTTPS and SSL certs
  • sha256 specifies the encryption algorithm
  • days 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 devices
  • key specifies the private key file to use, in this case ca-key.pem to create the public key
  • out specifies the name of the output public key file. This is ca.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 to Cert:\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 (classic System32 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):

pihole_local_dns

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:

NPM_Proxy_Host_config

  • 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 the cert-key.pem file

  • For Certificate make sure to upload the fullchain.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 :smile:! You did it! If you found it easy, well, good for you I guess

:arrow_right: Visit my blog
Made with immense effort and 20686 characters by KV Internet Services

This guide was made in markdown using hedgedoc

Tags

KVIS

I am the creator and publisher of this blog. Hope you found this guide useful!