How To Set Up a Wireguard VPN Server with PiHole
2022-08-09
Introduction
In this guide we will see how to set up a VPN server with advertisement blocking capabilities using Debian, Wireguard and PiHole. To follow this guide, make sure to have an updated installation of Debian; at the time of writing the latest version available is Debian 11, but any newer version should also work. For the rest of the tutorial we will be using the
firewalld
firewall,
but you can use any other netfilter frontend of your choice.
Installation
Let us start by installing
wireguard-tools
and
firewalld
packages:
marco@vpnnode:~$ sudo apt install wireguard firewalld
and let us enable the firewall daemon at boot:
marco@vpnnode:~$ sudo systemctl enable firewalld --now
Configure Wireguard(Server)
Let us now start configuring the wireguard server by generating the keypair and the configuration file; to do so, we will create a reserved directory:
root@vpnnode:~# mkdir -p /etc/wireguard/
root@vpnnode:~# cd /etc/wireguard/
Once inside it, we can generate a new keypair with the
wg(8)
utility:
root@vpnnode:/etc/wireguard# wg genkey | tee privkey | wg pubkey > pubkey
root@vpnnode:/etc/wireguard# ls -lh
total 8.0K
-rw-r--r-- 1 root root 45 Aug 6 08:46 privkey
-rw-r--r-- 1 root root 45 Aug 6 08:46 pubkey
root@vpnnode:/etc/wireguard# cat privkey
+LoX/Rrh2VR6nFiExOweXR37HluHdOhjBiFu7jqK7mo=
at this point, copy the content of privkey file and create a new file called
wg0.conf
where:
-
PrivateKey
is equal toprivkey
; -
Address
is the CIDR mask of the VPN(i.e. from192.168.2.1
to192.168.2.254
); -
ListenPort
is the UDP port where the Wireguard server will listen to.
root@vpnnode:/etc/wireguard# cat wg0.conf
[Interface]
PrivateKey = +LoX/Rrh2VR6nFiExOweXR37HluHdOhjBiFu7jqK7mo=
Address = 192.168.2.1/24
ListenPort = 48965
Configure the firewall
The next step is to configure the firewall to listen to the selected UDP port and to create a NAT between the internal Wireguard interface(
wg0
) and the
server external interface(in my case enp1s0
).
To do that, issue the following commands:
root@vpnnode:/etc/wireguard# firewall-cmd --permanent --add-port=48965/udp
root@vpnnode:/etc/wireguard# firewall-cmd --permanent --add-masquerade
root@vpnnode:/etc/wireguard# firewall-cmd --reload
root@vpnnode:/etc/wireguard# firewall-cmd --list-all
public
target: default
icmp-block-inversion: no
interfaces:
sources:
services: dhcpv6-client ssh
ports: 48965/udp
protocols:
forward: no
masquerade: yes
forward-ports:
source-ports:
icmp-blocks:
rich rules:
After reloading the firewall rules, you should have a new udp port open and the IP masquerading
activated for the public firewall zone. Now let us do the same thing for the
wg0
interface on
the internal firewall zone:
root@vpnnode:/etc/wireguard# firewall-cmd --zone=internal --add-interface=wg0
root@vpnnode:/etc/wireguard# firewall-cmd --permanent --zone=internal --add-masquerade
root@vpnnode:/etc/wireguard# firewall-cmd --zone=internal --list-all
internal
target: default
icmp-block-inversion: no
interfaces:
sources:
services: dhcpv6-client mdns samba-client ssh
ports:
protocols:
forward: no
masquerade: yes
forward-ports:
source-ports:
icmp-blocks:
rich rules:
Routing is complete, the last thing to do is to enable ip forwarding.
Enable IP forwarding
In order to route packets between VPN's clients and a remote host, we need to enable the ip forwarding feature. To do so, type the following command:
root@vpnnode:/etc/wireguard# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1
and to make it permanent, edit the
/etc/sysctl.d/99-sysctl.conf
file and uncomment the following line:
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
Configure Wireguard(Client)
Wireguard can be installed in a wide spectrum of operating system, in this guide I will not cover the installation process; in order to install the wireguard client for your computer/table/phone, please refer to this page. After that, open up the configuration file and add the following content:
[Interface]
PrivateKey = ni16f/oyWn8G0rdsJ7YGyytjXvJSfaNzhzFSG5Bv4Gg= # <-- client private key
Address = 192.168.2.2/24
DNS = 192.168.2.1
[Peer]
PublicKey = 4wzgj/0u53Jiheq8DjwQ9GRnvnzv0qcsisKARdnrr1c= # <-- server public key
PresharedKey = PW21sz8kl+nY8WRNJEypkqWJGLARSX2A5KjbPfaEUp0= # <-- wg genpsk
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = <SERVER_IP_ADDRESS>:48965
PersistentKeepalive = 15
Be sure to replace the following fields according to your needs:
-
PrivateKey
: replace it with client's private key(you can generate a new keypair usingwg genkey | tee privkey | wg pubkey > pubkey
command if you do not use a graphical client); -
PublicKey
: replace with server's public key(i.e./etc/wireguard/pubkey
file on the VPN server); -
PresharedKey
: you can generate a preshared key withwg genpsk
(this field is optional); -
Endpoint
: the IP address of your server with the Wireguard UDP port.
Back to the server
Open the
/etc/wireguard/wg0.conf
file and add the following entry at the end:
[Interface]
PrivateKey = +LoX/Rrh2VR6nFiExOweXR37HluHdOhjBiFu7jqK7mo=
Address = 192.168.2.1/24
ListenPort = 48965
# Add this
[Peer]
PublicKey = 1+54fGF/zZlVTxDiJ3rlmrH65+5K1NMFKwxlniA/2js= # <-- Client public key
PresharedKey = PW21sz8kl+nY8WRNJEypkqWJGLARSX2A5KjbPfaEUp0=
AllowedIPs = 192.168.2.2/32
where:
-
PublicKey
is the public key of the client; -
PresharedKey
is the preshared key previously generated on the client configuration; -
AllowedIPs
is the client's IP address.
Finally, let us start the Wireguard server:
root@vpnnode:/etc/wireguard# systemctl enable wg-quick@wg0 --now
root@vpnnode:/etc/wireguard# wg
interface: wg0
public key: 4wzgj/0u53Jiheq8DjwQ9GRnvnzv0qcsisKARdnrr1c=
private key: (hidden)
listening port: 48965
peer: 1+54fGF/zZlVTxDiJ3rlmrH65+5K1NMFKwxlniA/2js=
preshared key: (hidden)
allowed ips: 192.168.2.2/32
You can now enable the VPN connection from your client. Right now, you will only be able to ping the
VPN gateway(192.168.2.1
) without being able to access the internet. This is normal because the DNS
server we have specified in the client's configuration file(i.e.
192.168.2.1
) is not yet active.
We will fix this in a second by installing PiHole.
Configuring PiHole
PiHole is an internet tracking blocking system which acts as a DNS sinkhole. It is designed primarily for embedded devices such as the RaspberryPi, but it can be easily installed on any other Linux operating system. Since PiHole makes use of many different daemons(such as a DNS server, lighttpd and the AdminLTE dashboard), we will install it using Docker. This approach allows us to avoid manual configuration and simplify update operations.
Installing Docker on Debian is outside the scope of this guide, so please refer to the official documentation.
After that, set up a password for the PiHole dashboard(i.e. the password needed to log in to the web interface) and configure the docker container with the following
docker-compose.yml
file:
version: "3"
# More info at https://github.com/pi-hole/docker-pi-hole/ and https://docs.pi-hole.net/
services:
pihole:
container_name: pihole
image: pihole/pihole:latest
# For DHCP it is recommended to remove these ports and instead add: network_mode: "host"
ports:
- "53:53/tcp"
- "53:53/udp"
- "8888:80/tcp"
dns:
- 127.0.0.1
- 1.1.1.1
environment:
TZ: 'Europe/Rome'
WEBPASSWORD: 'BADPW'
# Volumes store your data between container upgrades
volumes:
- './etc-pihole:/etc/pihole'
- './etc-dnsmasq.d:/etc/dnsmasq.d'
restart: unless-stopped
Launch it with docker-compose up -d
, you should see its status in the docker logs:
marco@vpnnode:~$ docker-compose ps
docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------
pihole /s6-init Up (healthy) 0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp, 67/udp, 0.0.0.0:8888->80/tcp
Finally, let us configure PiHole to listen to our network interface.
To do so, open up your browser and go to the
admin page(be sure to be connected to the VPN,
PiHole does not expose the port 8888/80 to the external interface). You should see a page like this:

- Go to "settings" in the left pane;
- Go to the "DNS" tab;
- Selected the "Respond only on interface eth0" radio button in the "interface settings" section.

Conclusions
At this point you should be able to reach the internet. Let us try our new VPN on some sites(apart from whatismyipaddress.com):
1.1.1.1/help
Go to this page if you are using Cloudflare's DNS:

blockads.fivefilters.org
This page checks whether our ads blocking system works(you can also try to open any newspaper website with adblock disabled…those websites are the perfect testing page for an anti-ads system):

dnsleaktest.com
This website checks whether your VPN/DNS leaks your real IP address. Be sure to restart your network daemon(i.e., NetworkManager on Linux) before starting this test to avoid false-positive results.

dnssec-tools.org
This page determines whether your DNS resolver validates DNSSEC signatures.
