PiVPN has been discontinued. This guide should still work for now, but I will be updating the guide to use Wireguard instead ASAP.
- Introduction
- Prerequisites
- Initial Server Setup
- Install Pi-Hole
- Install PiVPN
- Configure Pi-Hole and PiVPN
- Let's Encrypt
- Sources
The purpose of this guide is to document the steps I take to set up Pi-Hole and PiVPN on a VPS, from companies such as DigitalOcean or Vultr. The ultimate goal is to have an ad-blocker that will work both on my home network and on any device connected to the VPN.
Almost every tutorial I found was focused on installing Pi-Hole and PiVPN on a local Raspberry Pi instead of on a VPS. The steps are mostly the same but there are some extra steps involved in securing the VPS to deny access from bad actors.
After completing this tutorial, you will have:
- A Pi-Hole accessible from anywhere
- A VPN that will provide an encrypted connection when using public Wi-Fi, via PiVPN
In order to follow this tutorial you will need to have a VPS with at least 512 MB of memory, although I would personally recommend at least 1 GB if you plan on having a large number of blocklists. This guide assumes that you are using Ubuntu 18.04 and Pi-Hole Version 4.2. Other distros will mostly likely work, but I have only tested the steps covered in this tutorial on Ubuntu 18.04.
Companies like DigitalOcean provide tutorials for creating a VPS on their servers.
We will be using ssh
to remotely log into the VPS and configure it. If you are on a Unix-based operating system, it should already be installed. If you are Windows, you will need to install PuTTY. Make sure you know your server's IP address and login credentials.
This section essentially covers all of the steps from DigitalOcean's tutorial for setting up Ubuntu, but with a few differences. We will be creating a specific user, pi
, that will use for logging into our VPS and handling the .ovpn
files generated by PiVPN.
When you have your server's IP address and root passphrase, log into the server as the root
user
ssh root@your_server_ip
You will be asked to create a new passphrase. Although we will be disabling password authentication, be sure to create or generate a secure passphrase anyway.
Create new user pi
adduser pi
Grant root privileges to pi
usermod -aG sudo pi
Public Key Authentication provides an alternative method of identifying yourselve to a remote server and increases the overall security of your server.
If you do not already have an SSH key, you will need to create one on your local computer
ssh-keygen
Save your key in the default file (where $user
is your user)
Enter file in which to save the key (/Users/$user/.ssh/id_rsa):
Create a secure passphrase. You will need to enter this passphrase each time you utilize your SSH key
Copy the public key from your local machine to your remote server with ssh-copy-id
ssh-copy-id pi@your_server_ip
- If you opted to add SSH during the server creation process anyway, this method will not work.
You should repeat these steps for each device you want to access the server, including desktops, laptops, tablets, and mobile phones.
Once you have added SSH keys from all of your devices, we can disable passphrase authentication.
Log into your server as root
, if you are not already logged in
ssh root@your_server_ip
Open the SSH daemon configuration file
sudo nano /etc/ssh/ssdh_config
- Find the line containing
PasswordAuthentication
and uncomment it by deleting the preceeding#
. Change it's value to no - Find the line containing
PubkeyAuthentication
and ensure it's value is set to yes - Find the line containing
ChallengeResponseAuthentication
and ensure it's value is set to no
Save your changes and close the file
CTRL + X
Y
ENTER
While still logged in as root
, open a new terminal window and test logging in as pi
and verify that the public key authentication works
ssh pi@your_server_ip
Mosh, or mobile shell, is a remote terminal application that allows roaming and intermittent connectivity. It's intended as a replacement for SSH but both can be used on the same server.
# Update your sources, if necessary
sudo apt update
# Install mosh
sudo apt install mosh
We will set up a basic firewall, ufw
, that will restrict access to certain services on the VPS. Specifically, we want to ensure that only ports needed for SSH, Pi-Hole, and PiVPN are open. Additional ports can be opened later depending on your specific needs.
We will be opening ports for secure FTP so that .ovpn
files needed for connecting to our VPN later can be retrieved via a FTP application such as Filezilla or Transmit.
To set up ufw
, enter the following commands:
# Apply basic defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Open ports for OpenSSH
sudo ufw allow OpenSSH
# Optionally, allow all access from your IP Address
sudo ufw allow from $yourIPAddress
# Open ports for secure FTP
sudo ufw allow sftp
# Open ports for Mosh if you installed it
sudo ufw allow mosh
Now that our server has been set up and is secure, we will now install the Pi-Hole software. The installation is fairly simple and requires a small amount of configuration on our part.
Please note that on a Raspberry Pi we would be asked to set a static IP address. This is important because we do not want the IP address of a DNS server to be constantly changing. However, since we are using a VPS, the static IP address has already been set for us. The networking interface will also be automatically selected as well since only one interface, eth0
, will be available to us at the time of installation.
Run the offical Pi-Hole installer:
curl -sSL https://install.pi-hole.net | bash
- When asked about which protocols to use for blocking ads, select both
IPv4
andIPv6
, even if you cannot useIPv6
yet on your home network. The justification is that more ads are now being served viaIPv6
and we want to ensure all ads are blocked - On the very last screen, you will be presented various information about your new Pi-Hole installation. Your Pi-Hole's IP address should match your server's IP address.
Once you have completed the Pi-Hole installation script, you should change the passphrase to the admin panel:
pihole -a -p myawesomepassphrase
Pi-Hole allows you to customize what websites you want to block and allows to you whitelist any false positives (e.g., unblocking Netflix or Facebook). Pi-Hole developer WaLLy3K provides a popular collection of blocklists that you can add to your own blocklists. Another blocklist collection is provided by the Block List Project.
I would also recommend checking out this GitHub repository that will load commonly whitelisted domains (e.g., Facebook, Instagram, XBox Live) into your Pi-Hole.
Finally, I would suggest following this guide from the official Pi-Hole documentation to set up unbound as your own recursive DNS server (rather than using a public DNS server such as Google DNS or Cloudflare). This will help to further increase the privacy of your DNS queries.
Installing PiVPN will be just as easy as installing Pi-Hole, although there is a bit more configuration required on our part for PiVPN. PiVPN automatically installs an OpenVPN server for us as well as any additional required software. The script will also automatically open ports in ufw
so that an OpenVPN client can communicate with our VPS.
Please note that on a Raspberry Pi, we would be asked to select a network interface, but since we are on a VPS the only available interface is eth0
and that is automatically selected for us as well as the static IP address.
Start by running the PiVPN installer
curl -L https://install.pivpn.io | bash
- When asked to choose a local user to hold your
.ovpn
configuration files, select the userpi
- When asked about enabling
UnattendedUpgrades
, pick yes - When asked to select the protocol, pick
UDP
- When asked to select the port, either accept the default
1194
or enter a random port (e.g.,11948
) - When asked to set the size of your encryption key, select
2048
- Generating the encryption key will take a few minutes
- When asked to select a Public IP or DNS, select your server's IP address
- When asked to select a DNS provider, select the
custom
option and enter the IP address of your server
Once the installer is finished, allow it to reboot your VPS
Now that both Pi-Hole and PiVPN are installed, there are a couple of critical steps we must take before we can start generating .ovpn
configuration files and connecting to our VPS. Specifically we want to ensure that PiVPN uses Pi-Hole as it's DNS server and that we can connect using an OpenVPN client.
First we will create a configuration file for dnsmasq
, the DNS service that powers Pi-Hole
Log into your server as pi
if you are not logged in already:
ssh pi@your_server_ip
Create a new configuration file called 02-pivpn.conf
:
sudo nano /etc/02-pivpn.conf
Add the following line to the file:
listen-address=127.0.0.1, your_server_ip, 10.8.0.1
Save and exit the file, and restart Pi-Hole's FTL service:
pihole restartdns
Second we will need to adjust rules in ufw
to allow OpenVPN to correctly route connections. This section covers Step 8 from DigitalOcean's guide to setting up OpenVPN on a VPS.
We will start by modifying the sysctl.conf
file to allow IP forwarding:
sudo nano /etc/sysctl.conf
Look for the line that contains net.ipv4.ip_forward
. If there is a #
character prepended, remove it to uncomment the line. Ensure that it is set to 1
and not 0
.
net.ipv4.ip_forward=1
Save and close the file. Then instruct sysctl
to reload it.
sudo sysctl -p
Then we will modify ufw
to allow masquerading of client connections. Before we can modify any rules, we need to find the public network interface of our VPS:
ip route | grep default
Your public interface will follow the word "dev
" in the output. For example:
default via 203.0.113.1 dev eth0 proto static metric 600
If your public interface is not eth0
, make note of what it is. We will be using that interface to modify a ufw
file that loads rules before regular rules are loaded. We will be adding a rule that will masquerade any traffic comming in from the VPN.
Open the before.rules
file:
sudo nano /etc/ufw/before.rules
Towards the top of before.rules
add the following text, starting with # START OPENVPN RULES
:
#
# rules.before
#
# Rules that should be run before the ufw command line added rules. Custom
# rules should be added to one of these chains:
# ufw-before-input
# ufw-before-output
# ufw-before-forward
#
# START OPENVPN RULES
# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
# Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!)
-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE
COMMIT
# END OPENVPN RULES
Save and close the before.rules
.
Finally, we need to tell ufw
to allow forward packets by default. Open the /etc/default/ufw
file:
sudo nano /etc/default/ufw
Fine the line containing DEFAULT_FORWARD_POLICY
. Change the value to ACCEPT
if necessary:
DEFAULT_FORWARD_POLICY="ACCEPT"
Save and close /etc/default/ufw
.
Enter the following commands to restart ufw
and OpenVPN:
# Restart ufw
sudo ufw disable
sudo ufw enable
# Restart OpenVPN
sudo service openvpn reload
The following section is optional and requires you to have your own domain name, but it will configure your Pi-Hole's web interface to use https
courtesy of Let's Encrypt and Certbot. It can be considered overkill just for Pi-Hole, but it certainly doesn't hurt. First we will acquire the certificate and then we will configure lighttdp
to automatically redirect any http
requests to https
. The steps are based on this reddit post.
-
Log into your remote server again either as
root
or with root privileges. -
Go to this Certbot page (for Ubuntu 18.04) and following the Install commands to install Certbot on your server.
-
Perform a dry run to acquire a certificate for your domain. For example:
certbot certonly --webroot -w /var/www/html -d example.com --dry-run
-
If acquiring the certificate was successful, run the same command again without
--dry-run
. For example:certbot certonly --webroot -w /var/www/html -d example.com
-
Edit the file
/etc/lighttpd/conf-available/10-ssl.conf
. Replaceexample.com
with your own domain name:ssl.pemfile = "/etc/letsencrypt/live/example.com/combined.pem" ssl.ca-file = "/etc/letsencrypt/live/example.com/chain.pem"
-
Run the following commands, replacing
example.com
with your domain name:ln -s /etc/lighttpd/conf-available/10-ssl.conf /etc/lighttpd/conf-enabled/10-ssl.conf cd /etc/letsencrypt/live/example.com/ cat privkey.pem cert.pem > combined.pem
-
Restart
lighttpd
:sudo systemctl restart lighttpd
https
should now be enabled on your web interface. -
Add a cron job to automatically renew the certificate every 90 days. Open
/etc/crontab
and add the following line:47 5 * * * root certbot renew --quiet --no-self-upgrade --renew-hook "cat $RENEWED_LINEAGE/privkey.pem $RENEWED_LINEAGE/cert.pem > $RENEWED_LINEAGE/combined.pem;systemctl reload-or-try-restart lighttpd"
Open the lighttpd
configuration file, /etc/lighttpd/lighttpd.conf
, and add the following block of code:
compress.cache-dir = "/var/cache/lighttpd/compress/"
compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
# [add after the syntax above]
# Redirect HTTP to HTTPS
$HTTP["scheme"] == "http" {
$HTTP["host"] =~ ".*" {
url.redirect = (".*" => "https://%0$0")
}
}
Restart lighttpd
again:
sudo systemctl restart lighttpd
Your web interface should now automatically redirect any http
requests to https
.
- Pi-Hole
- PiVPN
- Digital Ocean: Initial Server Setup with Ubuntu 16.04
- Secure Password Generator
- Using public keys for SSH authentication
- Mosh
- Debian Wiki: Uncomplicated Firewall
- FileZilla
- Transmit
- Static vs. dynamic IP addresses
- The Big Blocklist Collection
- Pi-Hole: Commonly Whitelisted Domains
- OpenVPN
- How To Set Up an OpenVPN Server on Ubuntu 16.04
- https://itchy.nl/raspberry-pi-3-with-openvpn-pihole-dnscrypt