Pi-hole and OpenVPN for More Privacy and Security

· 6 min read

I’ve been using Pi-hole for almost two years now, and it has become one of my favorite privacy projects. In a nutshell, Pi-hole blocks unwanted content, like advertisements and trackers, at a DNS level, so they will never be loaded. Pi-hole is open source and can be installed on your own Linux hardware to be used network-wide as a DNS server. This means it will even block unwanted content on your smartphone and console without installing any client-side apps. Pi-hole blocks 10-30% of all queries in my LAN (with over 550,000 domains on the blocklist).

To benefit from Pi-hole wherever I am (traveling or working from somewhere else), I run my own OpenVPN server on the same hardware, a Raspberry Pi 3 Model B. In fact, I always use my VPN when outside of my LAN, because you really shouldn’t trust public WiFi.

The Raspberry Pi 3 has more than enough resources for both, the only limiting factor is its network interface that is hampered by the USB 2.0 bus speed. This means if you’re using the onboard LAN your VPN connection will top out at about 94.4 Mbits/sec (11.8 MB/sec) no matter your Internet upload speed. If you’ve a faster upload connection Jeff Geerling almost doubled the throughput with an USB Gigabit LAN adapter.

Install OpenVPN with PiVPN

First, if you don’t have a static Internet IP address from where you host the Raspberry Pi, map a domain to your dynamic IP address, so that you can use it in your VPN setup. Otherwise you’d need to always know your current Internet IP address to connect to your VPN, which can be hard to get when you’re offsite. I’ve used the free dynamic DNS service from ChangeIP (now I’d use CloudFlare) and ddclient to update my IP regularly. In the below example we map “vpn.yourdomain.com”.

Install ddclient:

sudo apt-get install ddclient libio-socket-ssl-perl

Edit the config: sudo vi /etc/ddclient.conf

# ddclient.conf

# tell ddclient how to get your ip address
use=web, web=ip.changeip.com

# provide server and login details
protocol=changeip
ssl=yes
server=nic.changeip.com/nic/update
login=<login>
password=<pw>

# specify the domain to update
vpn.yourdomain.com

To update the mapping regularly, add ddclient as a daemon.

Edit the config: sudo vi /etc/default/ddclient

run_daemon="true"`
daemon_interval=900` # every 15 minutes

Start the daemon afterwards with sudo service ddclient start. Its status can be checked with sudo service ddclient status.

ddclient caches the IP and only updates it on a change. However some dynamic DNS services require a daily/weekly update or the account gets closed. This is why we create a cronjob (needs root) to force an update even if the IP didn’t change: sudo crontab -e

0 4 * * * /usr/sbin/ddclient -force >/home/pi/logs/ddclient.log 2>&1

Now to the installing of OpenVPN itself. An easy way to setup your own OpenVPN server on a Raspberry Pi is PiVPN—a set of install and management shell scripts to set it up in a fraction of the time. It comes with hardened security settings by default. The install process is well explained, so there’s no point to repeat it here. Just remember to use your domain (vpn.yourdomain.com) for the IP, if you’ve mapped it before with ddclient. I use the open source OpenVPN client Tunnelblick to connect to my VPN.

Note: Don’t forget to have your router forward UDP port 1194.

Install Pi-hole with OpenVPN support

Prerequisites: It’s not a must, but I recommend to set a static IP address for your Raspberry Pi in your LAN. For this guide I assume you’ve done that.

I use the alternative install method, as I’m not a fan of curling and piping to bash:

git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
cd "Pi-hole/automated install/"
sudo bash basic-install.sh

My Pi-hole settings:

  • Network interface: eth0 (so that it also works if we aren’t connected through the VPN)
  • Enable: DNS > Never forward non-FQDNs
  • Enable: DNS > Never forward reverse lookups for private IP ranges
  • Enable: DNS > Use DNSSEC
  • Select: DNS > Listen on all interfaces
  • Select: Privacy > Level 3 privacy mode (anonymous mode) to disable logs (easier on the sd card).

If you want to add more than the default blocklists, here are mine.

Finally, replace your router’s DNS server with the static IP address of the Raspberry Pi, and all clients in the network should benefit from Pi-hole.

Add OpenVPN support

Edit /etc/dnsmasq.conf and add the static Raspberry Pi IP address and the OpenVPN IP:

listen-address=127.0.0.1, 192.168.0.100, 10.8.0.1

To use Pi-hole for VPN connections and without, create the file 99-interfaces.conf in /etc/dnsmasq.d/ with the required interfaces:

interface=tun0 # OpenVPN
interface=eth0

Restart dnsmasq:

sudo service dnsmasq force-reload

That’s it. Now you can connect from everywhere to your OpenVPN server, and Pi-hole will keep you sane from unwanted content.

Tip-ins

Hosting web services on the same hardware

Pi-hole comes with lighttpd (and global rewriting rules) build-in, while it’s possible to use the same lighttpd instance for other web services, I’ve found it easier to use and maintain nginx on a secondary IP address, isolated from Pi-hole, to host other web services on the same hardware.

To do so, bind Pi-hole’s lighttpd only to the primary static IP address of the Raspberry Pi by adding the below to /etc/lighttpd/external.conf:

# Bind pi.hole/admin to only the primary static ip
server.bind = "192.168.0.100"

Restart lighttpd:

sudo systemctl restart lighttpd

In /etc/network/interfaces.d create eth0 with the below configuration to add a second static IP address:

auto eth0
allow-hotplug eth0
iface eth0 inet static
    address 192.168.0.100 # primary static ip, used by lighttpd (Pi-hole)
    netmask 255.255.255.0
    gateway 192.168.0.1
    dns-nameservers 127.0.0.1

iface eth0 inet static
    address 192.168.0.101 # second static ip
    netmask 255.255.255.0
    dns-nameservers 127.0.0.1

The newer dhcpcd service doesn’t support multiple IP’s for one interface, so we disable it:

sudo systemctl stop dhcpcd
sudo systemctl disable dhpcd

And switch to the old networking service instead:

sudo systemctl daemon-reload
sudo systemctl restart networking

To resolve vhosts, add the file 02-lan.conf in /etc/dnsmasq.d with:

addn-hosts=/etc/pihole/lan.list`

And add the hosts with your new secondary IP address to lan.list in /etc/pihole:

192.168.0.101 project.lan project

Restart dnsmasq and Pi-hole DNS:

sudo service dnsmasq force-reload 
pihole restartdns

Continue with setting up nginx as usual.

Pi-hole API

Pi-hole has an API to disable and enable it quickly from other hosts, so that you don’t need to go through the web interface or SSH into the Raspberry Pi. For example you could add a browser bookmark or shell alias for:

http://pi.hole/admin/api.php?disable&auth=PWHASH

Where PWHASH is the value of WEBPASSWORD in /etc/pihole/setupVars.conf. Replace disable with enable to enable Pi-hole.

Combining Pi-hole with uBlock Origin

Pi-hole blocks unwanted content on a DNS level, so ads for example will never be loaded but will, depending on the websites CSS, leave their DOM footprint (the space they would normally be rendered in) behind. A client-side ad-blocker will remove them. The best I know is uBlock Origin.

OpenVPN alternatives

IPSEC and Wireguard VPN are promising alternatives to OpenVPN. Algo VPN (PiVPN equivalent) simplifies their setup.