Configure a Wireguard VPN with PiHole

9th Aug 2022 • Marco Cetica

cover.webp

In this guide, we will outline the process of installing and configuring a Wireguard VPN integrated with Pi-Hole for DNS level ad blocking. The resulting network will effectively block advertisements, which is particularly useful on mobile phones or on any platform where ad blockers cannot be traditionally installed.

Before proceeding, be sure to have an updated installation of Debian and some familiarity with UNIX systems.

Installation

Let's start by installing the wireguard package which contains the tools needed to configure the VPN:

root@server:~ apt install wireguard

Server configuration

We can now start configuring the server by generating a key pair and a configuration file under the /etc/wireguard directory:

root@server:~ mkdir -p /etc/wireguard/ root@server:~ cd /etc/wireguard root@server:~ wg genkey | tee privkey | wg pubkey > pubkey root@server:~ 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@server:~ cat privkey +LoX/Rrh2VR6nFiExOweXR37HluHdOhjBiFu7jqK7mo=

After that, let's copy the content of the privkey file and add it to the VPN configuration file (wg0.conf):

root@server:~ cat wg0.conf [Interface] PrivateKey = +LoX/Rrh2VR6nFiExOweXR37HluHdOhjBiFu7jqK7mo= Address = 192.168.2.1/24 ListenPort = 48965

Where:

  1. PrivateKey contains the content of the privkey file;
  2. Address is the CIDR of the VPN (i.e., from 192.168.2.1 to 192.168.2.254);
  3. ListenPort is the UDP port where the Wireguard server will listen to.

configure the firewall

The next step is to configure the firewall to allow incoming traffic on the selected UDP port and to set up network address translation (NAT) for the Wireguard interface (wg0). To keep the configuration as straightforward as possible, we will use nftables; however, the same result can be achieved with virtually any netfilter front-end of your choice. By default, on Debian systems, an empty nftables ruleset appears as follows:

flush ruleset table inet filter { chain input { type filter hook input priority filter; } chain forward { type filter hook forward priority filter; } chain output { type filter hook output priority filter; } }

Let's modify it as follows:

flush ruleset table inet filter { chain input { # Drop all packets by default type filter hook input priority filter; policy drop; # Accept traffic from loopback device iifname "lo" accept comment "Accept loopback interface" # Accept established and related packets ct state established,related counter accept ct state invalid counter drop # Allow ICMP requests icmp type echo-request counter accept # Allow inbound HTTP and HTTPS traffic tcp dport { 80, 443 } counter accept # Allow inbound UDP traffic for Wireguard udp dport { 48965 } counter accept } chain forward { type filter hook forward priority filter; policy accept; } chain output { type filter hook output priority filter; policy accept; } } # Wireguard NAT add table wireguard-nat table ip wireguard-nat { chain prerouting { type nat hook prerouting priority -100; policy accept; } chain postrouting { type nat hook postrouting priority 100; policy accept; oifname "ens3" masquerade # Replace 'ens3' with your network interface } }

After that, reload the rulset by issuing the following command:

root@debian:~ systemctl restart nftables

Enable IP forwarding

After having configured the firewall, we can now proceed to enabling the kernel feature known as ip forwarding:

root@debian:~ sysctl -w net.ipv4.ip_forward=1 net.ipv4.ip_forward = 1

To make it permanent across reboots, add the following line to the /etc/sysctl.d/99-sysctl.conf configuration file:

net.ipv4.ip_forward=1

Configure the client

Let's now add a new client to the network. Create a new configuration file on your client with 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 = :48965 PersistentKeepalive = 15

Be sure to replace the following fields according to your configuration:

  1. PrivateKey: replace it with the client private key;
  2. PublicKey: replace it with the server public key;
  3. PresharedKey: optional field, you can generate a pre-shared key with wg genpsk;
  4. Endpoint: replace it with the IP address of the VPN with its UDP port.

Add the client to the server

We are now ready to add the client to the server configuration file. To do so, let's edit again the /etc/wireguard/wg0.conf (on the server) by adding the following content:

[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:

  1. PublicKey is the public key of the client;
  2. PresharedKey is the preshared key we've generated before;
  3. AllowedIPs is the client IP address.

After that, let's start the VPN server:

root@server:~ systemctl enable wg-quick@wg0 --now root@server:~ 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 interface on your client as well. Keep in mind that you won't be able to reach most of the Internet, though. This is because the DNS server we've specified in the configuration file (192.168.2.1) is not yet active. We will configure it in the next section.

Configure PiHole

PiHole is an internet tracking blocking system that acts as a DNS sinkhole. It is designed primarily for the RaspberryPi but it can also be easily installed in any other Linux-based distribution. Since PiHole creates quite a lot of configuration files (such as the DNS resolver, the lighttpd web server and the AdminLTE dashboard) and since we don't want to pollute our system, we will install it through Docker. This approach offers the additional advantage of simplifying the update process

Installing Docker on Debian is outside the scope of this guide and therefore will not be covered here. If you need help, refer to the official documentation. Once you have Docker installed, create a new compose.yml file with the following content:

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: - "192.168.2.1:53:53/tcp" - "192.168.2.1:53:53/udp" - "192.168.2.1:8888:80/tcp" dns: - 127.0.0.1 - 1.1.1.1 environment: TZ: 'Europe/Rome' FTLCONF_webserver_api_password: 'BADPW' # Set your password here # Volumes store your data between container upgrades volumes: - './etc-pihole:/etc/pihole' - './etc-dnsmasq.d:/etc/dnsmasq.d' restart: unless-stopped

Be sure to replace the FTLCONF_webserver_api_password with a proper password. After that, start the container using

root@server:~ docker-compose up -d

You can then verify whether the container has started by issuing docker-compose ps:

root@server:~ docker-compose ps NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS pihole pihole/pihole:latest "start.sh" pihole 11 days ago Up 11 days (healthy) 67/udp, 192.168.2.1:53->53/tcp, 192.168.2.1:53->53/udp, 123/udp, 443/tcp, 192.168.2.1:8888->80/tcp

After that, open up your browser and go to the PiHole admin page. You should see a screen see a page like the following:

login_pihole.webp

Enter your admin password and follow these instructions:

  1. Open the "Settings" drop-down menu from the left pane;
  2. Click on "All settings';
  3. Scroll down to dns.listeningMode and select the SINGLE option;
  4. Click on the "Save & Apply" button on the bottom right corner.

config.webp

Conclusions

You should now be able to reach the Internet. You can also try whether the VPN works by visiting one of the following websites:

1.1.1.1/help

Go to this page if you're using the Cloudflare DNS. It will show you various information about the type of connection.

1.1.1.1.webp

blockads.fivefilters.org

This page checks whether the ad-blocking system is working.

blockads.webp

dnsleaktest.com

This website checks whether the network leaks your real IP address. Be sure to restart the network daemon (e.g., NetworkManger on Linux) before starting this test to avoid false-positive results.

dnsleak.webp

openresolver.com

We can use this website to check whether our DNS is vulnerable to DNS amplification attacks. This kind of vulnerabilities occur when a misconfigured DNS server acts as an open resolver; meaning that it responds to queries from any source and from any interface. DNS amplification attacks can be mitigated by properly configuring network filtering through a firewall. When everything is properly configured, you should get a screen like the one on the following picture:

dnsamp.webp