Introduction

In my previous article/tutorial, I’ve explained how to setup your own DNS-over-HTTPS (DoH) server using Nginx, Certbot, dnscrypt-proxy and dns-over-https. In this article I’ll explain to you how to add Pi-Hole into the mix to block the unwanted advertising.

Pi-Hole

Pi-Hole is made of 2 components: a PHP web interface and a DNS server. Both are open-source. The web interface let you add blacklist, whitelist and configure the DNS server. The DNS server is the one doing the heavy lifting, responding to the queries according to its configuration generated by the interface.

The name comes from the lightness of the application, it can run without problem on a raspberry pi in your own network.

Install

Installing Pi-Hole couldn’t be easier, you need to download their installer and run it.

This installation has been tested on Ubuntu 20.04.

Check the screenshots before running the command, or simply, be sure to disable lighttpd.

sudo apt install dialog
curl -sSL https://install.pi-hole.net | bash

If you don’t want to use their installer, you try their docker image.

Configuration

This section will mostly contain screenshot showing you what to do to configure your Pi-Hole for it to works with the currently installed dnscrypt-proxy.

Steps

First select the Custom server, we’ll set directly the IP of our dnscrypt-proxy.

Input the IP of the dnscrypt-proxy socket: 127.0.2.1

Choose which blacklist you want. You’ll be able to add your own in the UI later.

You can keep this as default. If your machine doesn’t have IPv6, Pi-Hole will detect it.

To make things easier, I advise you install the Web Admin interface.

We don’t want Lighttpd, since we already have Nginx. We’ll configure Nginx to serve the admin website.

Continue the installation until the last screen where it shows you the full config and admin password.

If you’re using Google Cloud Platform like me, the IP it shows is not the public one. You’ll need to go in your console in instance to get the public IP.

Before connecting to the UI, we’ll run a configuration command to set the password to what we want.

sudo pihole -a -p

This will ask you to set a new password.

Groups

We need to add the group pihole to the www-data since it will be needed by the interface to work with the database.

Also need to change the default www.conf php-fpm pool file to reflect the need of the new group.

Lastly, restart php-fpm to take the change into account.

sudo usermod -a -G pihole www-data
sudo sed -i 's/group = www-data/group = pihole/g' /etc/php/7.4/fpm/pool.d/www.conf
sudo systemctl restart php7.4-fpm

Nginx

Since we don’t want to use lighttpd as webserver, we’ll have to configure Nginx.

Install

First we need to install PHP-fpm to take care of running the PHP website.

sudo apt install php7.4-fpm php7.4-zip php7.4-sqlite3

Configuration

Put the content of this file into /etc/nginx/sites-available/pihole.

Don’t forget to change the server_name for your domain name.

server {
        listen 80;
        listen [::]:80;

        root /var/www/html;
        server_name blocker.example.com;
        autoindex off;

        index pihole/index.php index.php index.html index.htm;

        location / {
                expires max;
                try_files $uri $uri/ =404;
        }

        location ~ \.php$ {
                include snippets/fastcgi-php.conf;
                fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        }

        location /*.js {
                index pihole/index.js;
        }

        location /admin {
                root /var/www/html;
                index index.php index.html index.htm;
        }

        location ~ /\.ht {
                deny all;
        }
}

Then do a symlink to the enabled folder. Ask nginx to check that to configuration works, and reload nginx.

sudo ln -s /etc/nginx/sites-available/pihole /etc/nginx/sites-enabled/pihole 
sudo nginx -t
sudo systemctl reload nginx

Adjustment Pi-Hole

Check the configuration file /etc/pihole/setupVars.conf

Be sure that IPV4_ADDRESS point to the public IP (WAN) of your machine, not the LAN one.

To easily get the public IP of your machine, do on that machine:

curl ifconfig.me

Then edit the setupVars.conf file.

WEBPASSWORD=e2982d4991ab003425cd1b556d84c052f1a6cd170028d265082bca9cce52cef9
PIHOLE_INTERFACE=lo
IPV4_ADDRESS=MY_PUBLIC_IP_GOES_HERE/32
IPV6_ADDRESS=
PIHOLE_DNS_1=127.0.2.1
PIHOLE_DNS_2=
QUERY_LOGGING=true
INSTALL_WEB_SERVER=false
INSTALL_WEB_INTERFACE=true
LIGHTTPD_ENABLED=false

This is done because the interface check if you’re accessing it from an accepted source.

sudo systemctl restart pihole-FTL

Certbot

As before, let’s make this domain an HTTPS one.

sudo certbot --nginx -d blocker.example.com

And Voila, the full UI is HTTPS and accessible with Nginx.

UI

There are 2 UI, a public one containing all the stats of the server and the Admin one letting you administrate your Pi-Hole.

Accessible at https://blocker.example.com/admin. (Of course, the domain name need to be replaced by the one you are using).

Public

This is the basic UI. Right now everybody can see this but only the Admin can add new blacklist/whitelist and configure the whole solution.

Admin

Once you click on Login, you’ll see something different.

You have more detailed statistics about the different requests done on your DNS Server.

As you can see, I’ve just spawn the instance on GCP and I’m already getting scanned for DNS Server. There is a section about firewalling later in this guide. Basically we’re going to block the public port 53, since we don’t need everybody to do request to our Pi-Hole, only doh-server.

Double check the settings to see if the installer did select the right DNS server.

This what it supposed to look like. If it’s not the case, you can correct it.

Test

You can easily test your Pi-Hole server using dig.

dig test.com @127.0.0.1
; <<>> DiG 9.11.3-1ubuntu1.1-Ubuntu <<>> test.com @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18636
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;test.com.                      IN      A
;; ANSWER SECTION:
test.com.               2       IN      A       0.0.0.0
;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Oct 03 17:32:34 UTC 2018
;; MSG SIZE  rcvd: 53

This well check for the domain test.com which is in one of the default blacklist.

As you can see the answer for it is 0.0.0.0 which is the default answer for blocked domains.

Blocked Answer Type

By default, Pi-Hole will return 0.0.0.0 for blocked domains. You can change this behaviour as documented on the official website.

Personally I prefer to return NXDOMAIN for blocked domains, making the DNS clients think the domain doesn’t exists.

If you want NXDOMAIN, do this command.

echo "BLOCKINGMODE=NXDOMAIN" | sudo tee /etc/pihole/pihole-FTL.conf
sudo systemctl restart pihole-FTL

DoH-Server

Now that you have your Pi-Hole working, we need to reconfigure DoH-Server to use Pi-Hole as a source of DNS instead of dnscrypt-proxy.

# HTTP listen port
listen = [
    "127.0.0.1:8053",
    "[::1]:8053",
]

# TLS certification file
# If left empty, plain-text HTTP will be used.
# You are recommended to leave empty and to use a server load balancer (e.g.
# Caddy, Nginx) and set up TLS there, because this program does not do OCSP
# Stapling, which is necessary for client bootstrapping in a network
# environment with completely no traditional DNS service.
cert = ""

# TLS private key file
key = ""

# HTTP path for resolve application
path = "/dns-query"

# Upstream DNS resolver
# If multiple servers are specified, a random one will be chosen each time.
upstream = [
    "127.0.0.1:53",
]

# Upstream timeout
timeout = 60

# Number of tries if upstream DNS fails
tries = 10

# Only use TCP for DNS query
tcp_only = false

# Enable logging
verbose = false

Change the upstream servers to 127.0.0.1:53.

Then restart the service.

sudo systemctl restart doh-server

There you go you now have a DoH server with Pi-Hole.

Firewalling

This is great, you have a fully working DoH server. But Pi-Hole like to listen on all interfaces. This is normal since it’s made to be installed on a Raspberry Pi in your own network with only a LAN interface.

But with a server on internet, that can be more than problematic. Your server could be used to DNS Flood other servers. We don’t want that.

UFW

We’re going to configure UFW to take care of the firewalling.

HTTP(s)

First we want all the HTTP and HTTPS traffic to be open on our machine. HTTP is used for by Certbot when validating that the domain belongs to you.

sudo ufw allow http
sudo ufw allow https

SSH

Obviously we want to be able to administrate the server.

sudo ufw allow ssh

Default

And now everything else should be blocked on Inbound.

sudo ufw default deny incoming

Final Touches

Enabling the firewall

sudo ufw enable

Checking that the rules are set correctly

sudo ufw status verbose

Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To                         Action      From
--                         ------      ----
80/tcp                     ALLOW IN    Anywhere                  
443/tcp                    ALLOW IN    Anywhere                  
22/tcp                     ALLOW IN    Anywhere                  
80/tcp (v6)                ALLOW IN    Anywhere (v6)             
443/tcp (v6)               ALLOW IN    Anywhere (v6)             
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Now your server is firewalled correctly. Only HTTP, HTTPS and SSH traffic is accepted.

Conclusion

You have now a DoH server that blocks advertisings. You can use the admin interface of the Pi-Hole to change the whitelist and the blacklists. There are a lot of tutorial online on how to add new blacklist, etc …