Setting up DNS Sinkholes with Pi-hole using Gravity Sync and DNS over HTTPS

This guide outlines setting up a secure, ad-free network using Pi-hole, Gravity Sync, and DNS over HTTPS on two Debian VMs, enhancing online privacy and security with open-source tools.

Let's unpack that title and explain what each component does:

  • DNS Sinkhole (Pi-hole): A DNS sinkhole is a mechanism used to filter unwanted or malicious internet traffic. In the context of Pi-hole, it's specifically used for blocking advertisements, tracking systems, and malicious domains at the network level. Pi-hole acts as a DNS server in your local network. When a device on your network makes a DNS request (i.e., asks for the IP address associated with a domain name), Pi-hole intercepts it. If the request is for a domain on Pi-hole's blocklist, it returns a 'null' response, effectively blocking the content. This blocklist is usually made up of known ad-serving domains, trackers, and potentially malicious sites.
  • Gravity Sync: Gravity Sync is a tool used in conjunction with Pi-hole to synchronize the blocklists between multiple Pi-hole instances. This is particularly useful if you have more than one Pi-hole setup and want to ensure a consistent blocking experience across all of them. Gravity Sync automates the process of keeping the blocklists on different Pi-holes in sync with each other.
  • DoH (DNS over HTTPS): DNS over HTTPS (DoH) is a protocol for performing remote DNS resolution via the HTTPS protocol. This means that DNS requests are encrypted and sent over HTTPS, making them secure and less susceptible to interception or manipulation. This is particularly important for privacy and security, as traditional DNS requests are unencrypted and can be easily seen by ISPs or potential attackers. Implementing DoH in a network setup with Pi-hole means that not only are the DNS requests filtered for ads and trackers, but they are also encrypted, enhancing overall network security and privacy.

This project will focus on setting up two Pi-holes to block unwanted content on your network. Gravity Sync will manage multiple Pi-hole instances more efficiently, and by enabling DNS over HTTPS, you add an extra layer of security and privacy to your DNS queries. The project does not utilise Pi-hole's DHCP server.


Requirements

The following is required for this project:

  • 2 virtual machines running Debian
    • The hardware requirements are lightweight with the official requirements stating 512MB RAM and 2GB storage per instance. This project leaves extra legroom by using 1GB RAM and 4GB storage per instance.
    • For redundancy, it's recommended to run the two Pi-holes on separate machines. This ensures that if one of the servers fails, the other Pi-hole will continue to route and block traffic without interruption.
  • local network accessible by all endpoints
    • Ensure that all endpoints on the network can access port 53 on the VMs. This can be achieved by setting up a segmented management VLAN that provides access to the VMs, or running the VMs on the same network.
    • Use a wired network for the VMs for more stability.

High-level Overview

Outlining the process:

  1. Set up and harden a Linux machine.
  2. Install and configure Pi-hole.
  3. Set default DNS resolution to Pi-hole.
  4. Configure DoH.
  5. Repeat the same steps on the second machine.
  6. Set up Gravity Sync.

Preparing the Host

  • Install Debian
    • Install the latest version of Debian with the default settings. Debian is used instead of Ubuntu because it is more lightweight and takes less resources.
    • Set the hostname to something descriptive, such as 'DNS_1'
    • Set a static IP for the VM during installation or use DHCP and reserve the IP in your router.
    • Do not install a desktop environment and limit the additional modules to SSH only.
  • Generate an SSH keypair
    • Install PuTTY on your local machine and launch the PuTTY Key Generator (PuTTYGen.exe)
    • Generate a new key pair with a strong passphrase, then save both private and public keys on your local machine.
  • Create a new user and set up access
    • Access the server as the new user.
    • Append the public SSH key for the new user.
    • Paste in the public key from PuTTY, then save and close the authorized_keys file with Ctrl+X.
    • Switch to root and install sudo.
    • Update package repositories and upgrade packages.
    • Install firewall.
mkdir ~/.ssh
chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
# Paste in the public key, then save and exit the editor.
su root
apt-get install sudo
sudo apt-get update && sudo apt-get upgrade -y
sudo apt install ufw
sudo apt install curl
  • Change the default SSH port, disable root login and force key authentication
    • Edit sshd_config with nano.
    • Change and uncomment the SSH port. In this case, port 22041 will be used.
    • Change and uncomment PermitRootLogin to no.
    • Change UsePAM to no.
    • Close and save with Ctrl+X
sudo nano /etc/ssh/sshd_config
  Port 22 #uncomment change it to 22041 
  PermitRootLogin yes #uncomment and change it to no
  UsePAM yes #change it to no
  PasswordAuthentication no
# Save and exit the editor.
  • Configure the firewall
    • Set the default policies in UFW by denying all incoming traffic and allowing all outgoing traffic.
    • Update UFW to allow traffic on the new SSH port, the DNS port and on port 80 for the Pi-Hole admin portal.
    • Restart SSH and UFW to apply the new settings. Port 22 will drop the connection.
    • Start a new SSH session with the private key on port 22041, authenticate as the new user account, then switch to root.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22041/tcp
sudo ufw allow 53
sudo ufw allow 80
sudo systemctl restart ssh
sudo ufw enable
sudo ufw reload

Hardening the Host

  • Updates and Essentials
    • Install Fail2Ban.
    • Enable Automatic Updates.
sudo apt-get install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
  • Optional Security Settings
    • Install CrowdSec. This requires a CrowdSec account. CrowdSec is an open-source security monitoring and response tool that analyzes user behavior and traffic on your server to identify and block potential security threats.
    • Configure Fail2Ban.
curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash
apt install crowdsec
sudo cscli console enroll <your ID> --overwrite #get the ID from your CrowdSec console's Security Engine

sudo nano /etc/fail2ban/jail.local
  bantime = 1h #update to 2h
  ignoreip = 127.0.0.1/8 ::1 your_client_ip #whitelist your static client IP address (if your IP is static) by replacing 'your_client_ip' with it to avoid potential login bans due to failed attempts
  maxretry = 5 #update to 3
  enable jails by adding enabled = true to each jail defined by '[]'

Installing Pi-hole and adding blocklists

  • Install Pi-hole
curl -sSL https://install.pi-hole.net | bash
  • Access Pi-hole
    • Copy the admin password from the terminal and access the admin panel on your local machine.
http://<your.debian.ip>/admin
  • Add a blocklist
    • Open Adlists on the left side menu.
    • Get a block list from GitHub, in this example we will use ads.txt.
      • Add as many blocklists as you'd like, but a word of warning: blocking more domains increases the risk of disrupting website functionality.
    • Copy the link for the raw URL ending with the .txt extension and paste it in the "Address" field of your Adlist group management page.
    • Click Add.
    • Open Tools > Update Gravity on the left side menu and click Update.

Testing DNS resolution

  • Test on a single endpoint
    • Update the DNS servers on one device for now to see the behaviour. In this example, an Android phone is used. The exact steps will depend on your device's make and model, but the goal is to find the DNS settings for your WiFi network and update the primary DNS server to the IP of the Pi-hole.
      • Navigate to Settings > Network & Internet > Internet > Select your SSID > Edit > Advanced Options > IP Settings > Static > change DNS 1 to the IP of your Pi-hole VM.
      • Visit a website with ads (not hard to find one), the ads should now be blocked completely.

Updating network-wide DNS resolution

  • Update DNS server assignment
    • The assignment of network-wide DNS servers is managed by your router.
    • Updating this setting will depend on your router's make and model. For this example, a UDM Pro is used, but Google how to change the DNS assignment for your specific router.
      • Navigate to Settings > Network > Select your VLAN > Expand DHCP Service Management > Update DNS Server 1 to the IP address of the Pi-Hole. Save and allow some time for the new settings to propagate.

Configuring DNS Over HTTPS

  • Set up Cloudflared
    • Cludflared uses 1.1.1.1, the DNS service operated by Cloudflare integrated with DoH.
    • Install Cloudflared.
    • Create a new user for the Cloudflared daemon.
    • Create a new configuration file.
    • Update access.
    • Create a new service to run Cloudflared on startup.
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo apt-get install ./cloudflared-linux-amd64.deb
cloudflared -v

sudo useradd -s /usr/sbin/nologin -r -M cloudflared
sudo nano /etc/default/cloudflared

#paste in the following:
CLOUDFLARED_OPTS=--port 5053 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query
#save and exit

sudo chown cloudflared:cloudflared /etc/default/cloudflared
sudo chown cloudflared:cloudflared /usr/local/bin/cloudflared
sudo nano /etc/systemd/system/cloudflared.service

#paste in the following:
[Unit]
Description=cloudflared DoH
After=syslog.target network-online.target

[Service]
Type=simple
User=cloudflared
EnvironmentFile=/etc/default/cloudflared
ExecStart=/usr/local/bin/cloudflared proxy-dns $CLOUDFLARED_OPTS
Restart=on-failure
RestartSec=10
KillMode=process

[Install]
WantedBy=multi-user.target
#save and exit

sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared
  • Configure Pi-hole
    • Point the Pi-hole's DNS upstream server to the daemon loopback address. This will essentially forward all DNS queries from your Pi-hole to Cloudflared with DoH.
    • Open Settings > DNS on the left side menu and enable Custom 1 (IPv4).
    • Update the address to the following and save the configuration:
127.0.0.1#5053
    • Click the link below to check if your DoH is set up properly. DNS over HTTPS should show Yes.
1.1.1.1 — the Internet’s Fastest, Privacy-First DNS Resolver
✌️✌️ Browse a faster, more private internet.
  • (Optional) Set up automatic updates for Cloudflared and Pi-hole
    • Warning: Keeping Pi-hole automatically updated is NOT recommended by the Pi-hole team as the release notes should be reviewed for each version. With that said, setting up automatic updates depends on your risk appetite. If my Pi-hole breaks, I will roll it back from a backup, although this never happened to me. This step is completely optional, and I will also list the manual update steps below.
    • Create a weekly cron job that contains the update command for both Pi-hole and Cloudflared.
/etc/cron.weekly/cloudflared-updater

#paste in the following

#!/bin/bash
sudo rm -f cloudflared-linux-amd64.deb
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo apt-get install ./cloudflared-linux-amd64.deb
pihole -up

#save and exit

sudo chmod +x /etc/cron.weekly/cloudflared-updater
sudo chown root:root /etc/cron.weekly/cloudflared-updater
  • (Optional) Install updates manually for Cloudflared and Pi-hole
    • To update Pi-hole:
pihole -up
    • To update Cloudflared:
sudo rm -f cloudflared-linux-amd64.deb
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo apt-get install ./cloudflared-linux-amd64.deb

Rinse and Repeat

  • Repeat the same steps for the second Pi-hole
    • This can be done either manually, by going through each of the abovelisted steps, or by backing up the primary Pi-hole VM and restoring it to a new machine as a snapshot.
    • Don't forget to:
      • Change the IP of the secondary Pi-hole.
      • Change the hostname of the secondary Pi-hole to 'DNS_2'.
      • Update the secondary DNS server in your router's configuration to the IP address of the second Pi-hole instance.

Setting up Gravity Sync

  • Access both Pi-hole instances and run the installation script on both
    • When prompted, skip the configuration.
curl -sSL https://raw.githubusercontent.com/vmstan/gs-install/main/gs-install.sh | bash
  • Configure Gravity Sync
    • The configuration requires the remote SSH port of the target Pi-hole instance. In this case, port 22041 was used as the custom SSH port.
    • Run the configuration command on both instances, it will walk through the setup instructions.:
gravity-sync config 22041
  • Enable and Test Gravity Sync
    • Dry run test on both machines:
gravity-sync compare
    • If the dry run completed without an error, proceed to the first sync from the authoritative Pi-hole (the first Pi-hole VM):
gravity-sync push
    • If all went well, you should see the blocklist replicate from DNS_1 to DNS_2. Proceed with running the sync automatically with:
gravity-sync auto

Conclusion

This guide has led us through establishing a network with Pi-hole, Gravity Sync, and DNS over HTTPS, aiming for enhanced privacy and reduced internet clutter. Pi-hole acts as a shield against unwanted ads and trackers, and Gravity Sync keeps Pi-hole instances in sync, ensuring a consistent network experience. DNS over HTTPS adds a layer of security by encrypting DNS queries. Ultimately, this project exemplifies the power of open-source tools in creating a more private, secure, and better online experience.