Skip to content

Instantly share code, notes, and snippets.

@magnolialogic
Last active March 5, 2024 08:04
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save magnolialogic/a83f728801e80345a73ebb7bc25391ff to your computer and use it in GitHub Desktop.
Save magnolialogic/a83f728801e80345a73ebb7bc25391ff to your computer and use it in GitHub Desktop.
DNS over HTTPS with DietPi, Pi-hole, and Cloudflare DNS

DietPi + Pi-hole + Cloudflare DNS + DNS over HTTPS

Summary

Quick and dirty setup instructions to get Pi-hole running with DoH via Cloudflare on a headless Raspberry Pi.

I was originally using Pi-hole with Quad9 as my upstream DNS provider, but noticed that my ISP (Spectrum) was still intercepting and answering some DNS queries so I've switched to Cloudflare and their Argo Tunnel client for DNS over HTTPS.

Yes, I know there are other options out there like unbound, stubby, DNS over TLS, and DNSCrypt, but as of this writing the whole encrypted DNS thing still feels really runny so I went with cloudflared because it's a single package that's easy to configure, update, and remove. This combination gets me to a place where my network is using encrypted DNS + DNSSEC and should automagically support ESNI once Safari implements it, and I could easily back out of DNS over HTTPS if DoT or DNSCrypt becomes more fully-baked before DoH does. I'll also happily switch back to Quad9 if they provide a DoH proxy package like cloudflared, since I prefer their DNS services for their malware domain filtering.

Written for macOS, using DietPi v6.22.3, Pi-hole v4.2.2, and cloudflared version 2019.4.0.

Required Hardware

  1. Raspberry Pi
  2. Class 10 MicroSD card — don't skimp on the speed or size of the card!
  3. Short Cat-5E patch cable — don't use WiFi!

Total cost: $53 + tax and shipping at time of writing, with everything rounded up to the nearest dollar

Instructions

1. Flash your MicroSD card with the DietPi image

  1. Download latest DietPi for Raspberry Pi from DietPi.com
  2. Download latest balenaEtcher from etcher.io
  3. Unzip the DietPi_RPi-ARMv6-Stretch.7z image
  4. Launch balenaEtcher and click "Select image"
  5. Select the newly-unzipped DietPi_RPi-ARMv6-Stretch.img
  6. Verify balenaEtcher has selected your MicroSD volume as its destination
  7. Click "Flash!" and wait for the process to finish

2. Configure your DietPi install

  1. Insert the newly flashed MicroSD card into your Pi and connect it to ethernet and power
  2. Get your Pi's IP address by looking for a host with the name "DietPi" in your router's "Devices" list or using the free Fing iOS app
  3. Copy your SSH keys to the Pi
    1. Open a new Terminal window
    2. Verify your public key exists by running ls ~/.ssh/id_rsa.pub
      • If ls returns "No such file or directory" you will need to create a new keypair by running ssh-keygen -t rsa and pressing Enter when prompted for a location and passphrase
    3. Copy your public key to the Pi by running ssh-copy-id root@<IPAddressFromStep2.2>
      • Type "yes" and press Enter when prompted to continue
      • Type "dietpi" and press Enter when prompted for root's password
  4. SSH in to your Pi by running ssh root@<IPAddressFromStep2.2>
    • DietPi should automatically begin its setup process
    • Press Tab followed by Enter to accept the software license and run DietPi-Update
  5. When prompted, choose whether or not to participate in DietPi-Survey
  6. Once DietPi-Update is complete, press Enter to reboot
  7. Wait 60 seconds or so for your Pi to reboot, then reconnect by running ssh root@<IPAddressFromStep2.2>
  8. When prompted, choose whether or not to change the user passwords for DietPi
    • Since we've already configured DietPi to use passwordless SSH, I recommend setting a strong password here and saving it in your password manager
  9. When prompted, select to disable the serial console
  10. Once DietPi loads DietPi-Software, scroll down to DietPi-Config and press Enter to configure your DietPi install
    1. Display Options:
      • Select "Display Resolution" and choose "Headless"
    2. Language/Regional Options:
      • Select "Locale" and choose "en_US.UTF-8"
      • Select "Timezone" and choose your local Time Zone
      • Ignore "Keyboard" since we're setting this up as a headless server
    3. Security Options:
      • If you chose not to change the Pi's passwords, you can do so here
      • Select "Hostname" and name your Pi something like "pihole" and reboot when prompted
  11. Wait 60 seconds or so for the Pi to reboot, then reconnect by running ssh root@<IPAddressFromStep2.2>
  12. Once DietPi-Software resumes, press Tab followed by Enter to select and finish setup, which should return you to a root@DietPi:~# prompt

3. Install avahi-daemon to enable Bonjour

  1. From the DietPi prompt, install avahi-daemon by running sudo apt-get install avahi-daemon
  2. When prompted to continue, enter 'y' and press Enter
  3. You should now be able to connect to your Pi by using the hostname set in step 2.10.3, e.g. pihole: ssh root@pihole.local

4. Install Pi-hole

  1. From the DietPi prompt, begin Pi-hole installation by running curl -sSL https://install.pi-hole.net | bash
  2. Wait for Pi-hole setup to download its dependencies, then press Enter to proceed past the welcome, donate, and static IP messages
  3. When prompted, select Cloudflare as your Upstream DNS Provider
  4. Choose which blocklists to enable (I left them all on) then press Tab followed by Enter
  5. Choose whether to block ads over IPv4 or IPv6 then press Tab followed by Enter
  6. When prompted, select to use your current network settings as a static address
  7. Press Enter to proceed past the warning about static IPs
  8. Press Enter to enable the web admin interface
  9. Press Enter to install lighttpd
  10. Press Enter to enable logging of queries
  11. Use the arrow keys to select the level of logging privacy you desire (I left it on "0 Show everything")
  12. Wait for installation to proceed
  13. At the "Installation Complete!" prompt, make sure you copy the login password for the web admin interface!
    • No seriously, copy this into your password manager -- you'll need it every 7 days if you want to access your Pi-hole's web admin interface

5. Install cloudflared

  1. From the DietPi prompt, download the latest stable cloudflared release by running wget https://bin.equinox.io/c/VdrWdbjqyF/cloudflared-stable-linux-arm.tgz
  2. Decompress the archive you just downloaded by running tar -xvzf cloudflared-stable-linux-arm.tgz
  3. Move cloudflared into place by running sudo cp ./cloudflared /usr/local/bin
  4. Remove the leftover files by running rm cloudflare*
  5. Make cloudflared executable by running sudo chmod +x /usr/local/bin/cloudflared
  6. Check cloudflared version to verify installation by running cloudflared -v
    • You should get a response similar to cloudflared version 2019.4.0 (built 2019-04-03-1938 UTC)
  7. Create a cloudflared user to run the daemon by running sudo useradd -s /usr/sbin/nologin -r -M cloudflared
  8. Create a default preference file for cloudflared
    1. From the DietPi prompt, run sudo nano /etc/default/cloudflared
    2. Paste the following 2 lines into nano:
    # Commandline args for cloudflared
    CLOUDFLARED_OPTS=--port 5053 --upstream https://1.1.1.1/dns-query --upstream https://1.0.0.1/dns-query
    
    1. Press Ctrl+O to save your changes
    2. Press Ctrl+X to exit nano
  9. Run the following 2 commands to update permissions for cloudflared and its newly-created preferences file:
sudo chown cloudflared:cloudflared /etc/default/cloudflared
sudo chown cloudflared:cloudflared /usr/local/bin/cloudflared
  1. Create a systemd service file
    1. From the DietPi prompt, run sudo nano /lib/systemd/system/cloudflared.service
    2. Paste the following 15 lines into nano:
    [Unit]
    Description=cloudflared DNS over HTTPS proxy
    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
    
    1. Press Ctrl+O to save your changes
    2. Press Ctrl+X to exit nano
  2. Enable the newly-created systemd service by running the following 3 commands:
sudo systemctl enable cloudflared
sudo systemctl start cloudflared
sudo systemctl status cloudflared

6. Turn it on

  1. In the Pi-hole Web Admin interface, select "Settings" from the sidebar and click on the "DNS" tab
  2. In the "Upstream DNS Providers" block, deselect all active checkboxes in the Cloudflare row, and enter 127.0.0.1#5053 in the "Custom 1 (IPv4)" block and check its checkbox to point Pi-hole at your cloudflared service
  3. This seems counterintuitive since we want DNSSEC enabled, but leave the "Use DNSSEC" checkbox in the "Advanced DNS Settings" block unchecked (cloudflared handles this for us)
  4. Scroll to the bottom of the page and click "Save"
  5. Once the Pi-hole Web Admin interface confirms that your changes have been saved, go to your router's DNS settings and enter your Pi-hole's address (from Step 2.2) and save your changes
  6. Flush your Mac's DNS cache
    1. Open a new Terminal window
    2. Run sudo dscacheutil -flushcache, enter your admin password, and press Enter

7. Test your setup

  1. Verify your Mac is pointed at your Pi-hole for its DNS server by launching System Preferences and clicking on Network
    • Your active interface's "DNS Server" should show the IP address from Step 2.2
  2. Visit Cloudflare's Browsing Experience Security Check
    • You should see green checkmarks next to "Secure DNS", "DNSSEC", and "TLS 1.3"
    • As of this writing no major browsers support Encrypted SNI so you should see a red X next that feature

And that's it! 🍾🎉

All of the devices on your LAN/WLAN should be up and running with a faster and ad-free browsing experience. Some devices may need to disconnect and reassociate with your network or be rebooted to pick up the DNS change.


Sources

DietPi setup

DNS over HTTPS with Cloudflare

@omniproc
Copy link

FYI: setting network.trr.mode to 2 will tell FF to use Cloudflare's DoH directly and thus bypass the pihole. AFAIK Pihole doesn't yet provide a DoH compliant interface and thus can't be configured in FF trr settings as a custom DoH source.

@magnolialogic
Copy link
Author

FYI: setting network.trr.mode to 2 will tell FF to use Cloudflare's DoH directly and thus bypass the pihole. AFAIK Pihole doesn't yet provide a DoH compliant interface and thus can't be configured in FF trr settings as a custom DoH source.

Interesting, thanks for that info! You're absolutely correct, I should have looked at the Pihole's log to see whether it was actually resolving DoH requests from Firefox (narrator's voice: it wasn't).

I'm pulling that portion out of the gist, but I'll keep it here along with the comment history for posterity's sake.

Thanks again!

ESNI with Firefox nightlies

  1. Download and install the latest Firefox beta build from Mozilla.org
  2. Launch Firefox and enter about:config into the address bar
  3. Once the config page loads, enter network.security.esni.enabled into the search bar
  4. Double-click on false in the the "Value" column, which should change to true
  5. Enter network.trr.mode into the search bar
  6. Double-click on the default value (0) and enter 2 and click OK
  7. Re-run the Browsing Experience Security Check in Firefox and you should see 4 green checkmarks

Source: ESNI with Firefox Nightlies

@igorkulman
Copy link

I think you missed a crucial step, setting 127.0.0.1#5053 as custom DNS in the PiHole WEB UI.

@magnolialogic
Copy link
Author

I think you missed a crucial step, setting 127.0.0.1#5053 as custom DNS in the PiHole WEB UI.

D'oh, you're absolutely right, not sure how I missed that step here. I've updated "Turn it on" step 2 with that instruction.

Thanks, @igorkulman!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment