STFN

WireGuard and PiHole for secure ad blocking on your smartphone

30 minutes

Caveats

Let’s start with some caveats. I am not an expert in VPNs, network configuration, nor network security. The solution that I am presenting is working for me, I am using it everyday, and so far it has caused me no problems. Then again, there is a real possibility I have made a glaring mistake or omission. I am hoping that someone much smarter than I am will read this post and point out any issues. If you are such a person, please contact me, you can catch me on Mastodon or via email, links in the footer.

Introduction

I write blog posts like this to report on something I implemented for myself to fill my specific needs, or to share something I learnt recently. This project covers both. I wanted to learn how to create and configure VPNs and other advanced network configuration, and also I wanted a way to block ads and trackers on my phone. It is possible that what I achieved here can be reached with simpler means, but I like overcomplicating things when learning, as it gives more knowledge on a wider set of tools.

The outcome of this project can be summarized as such:

I am now able to use PiHole to block ads and trackers on all of my devices, no matter to which network I am connected, be it my private LAN, a public WiFi, or even cellular network. This is achieved by running a VPS (Virtual Private Server) with PiHole, which is an advertisement blocking software.

PiHole blocks ads by working as a DNS server. In a very tl;dr description, a DNS server is a server that tells your computer how to find a way to certain places on the Internet. You want to go to Wikipedia, your computer asks a DNS server how to get there, and the DNS server replies “first left, then right, and twice straight ahead”. Actually, the server replies with an IP address, but that’s the general idea. When PiHole is being used a DNS server, it gives directions to proper content, but when your computer asks for directions to get something like advert.malware.com, it will say nothing, and the ad will not be fetched.

For security reasons, we are connecting to our new DNS server over a VPN (VIrtual Private Network), so that only our machines can connect to it and no one else.

Theoretically the VPN part can be omitted, but that is highly undesirable. A publicly available DNS servers can be used by malicious third parties in DDOS attacks . Please do not leave your DNS server unsecured.

This post is laid out in several steps

Why just not use uBlock Origin?

But I am! My main browser on both my laptop and my phone is Firefox with uBlock Origin installed, but especially on the phone, the browser is not everything. Apps also connect to the Internet. Google Discover opens its links in Google Chrome which soon will not allow using uBlock Origin. Basically everything on your phone can send and receive traffic, traffic that is often advertisements or tracking. And PiHole will be able to block it all.

Getting a VPS

The first thing that you will need is a computer that can be reached from the Internet. One solution is to expose a device in your home network, but that is less safe, and not available for everyone, people who are, for example, behind a CGNAT do not have an easy means to expose their home devices to the public. So in this project, I am using a VPS (Virtual Private Server) from a cloud provider.

The provider of my choice is RackNerd. While less known than the large players like AWS or GCP, in my opinion they deliver a solid service and often have much cheaper prices for the low end boxes, which we will need for this project. BTW, I am using their server to host my blog. I learnt about them from Low End Box, which I also recommend to get good deals on cheap servers. Here’s an example of RackNerd good deal.

If you are interested in renting a VPS from RackNerd, here is my affiliate link. When you use this link and order a server, a small percent of your payment will go towards my account. It’s a simple way to support my blog. Thanks!

My RackNerd affiliate link

You can order any server, for our needs even the cheapest and smallest ones will work. As for the OS, I am using Debian on all of my servers, but most probably any other terminal Linux OS will be just fine. This tutorial assumes using an OS that has apt as the package manager and systemd for managing background processes.

Once you have a server running, it is basically required to do an initial configuration and hardening to secure it from external threats. Initial Server Setup with Ubuntu is a great article on how to do this initial config, I really suggest you go through it and implement each step. I know the title says “Ubuntu”, but the things suggested there are universal.

Setting up WireGuard

Alright then. You have a server to which you can SSH safely, and which has a UFW firewall running. Now it’s time to set up a VPN (Virtual Private Network). Having a VPN will give you a secure way of connecting to your server that no one else is able to replicate.

This part is largely based on the two following articles, with some parts advanced, unneeded parts omitted.

How to set up WireGuard on Ubuntu 20.04

Access Your Home Network From Anywhere with WireGuard VPN

In short, WireGuard is based on exchanging keys, the server and every client has a public and a private key which are used to authenticate the connection. Without knowing those keys, other computers will not be able to join the VPN. From a OS perspective, WireGuards creates a new network interface, usually named wg0, using which you connect to the server and other peers in the VPN network. Yes, VPN network, I also use LED diodes :)

Server configuration

The part below needs to be done on the VPS, which will become the WireGuard server.

Start with installing WireGuard

sudo apt install wireguard

Next, generate a private key:

wg genkey | sudo tee /etc/wireguard/private.key

Generate a public key from the private key:

sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key

Create the server config file:

sudo nano /etc/wireguard/wg0.conf

And paste the text below into the file:

[Interface]
PrivateKey = <server_private_key_goes_here>
Address = 10.8.0.1/24
ListenPort = 51820
SaveConfig = true

The server will have an IP address of 10.8.0.1, and the whole VPN network will be in the 10.8.XXX.XXX range.

Add rule to firewall to enable communication on WireGuard’s port 51820.

sudo ufw allow 51820/udp

Enable and start the wireguard service

sudo systemctl enable wg-quick@wg0.service
sudo systemctl start wg-quick@wg0.service

The server configuration is almost done, time to move to the client. We’ll start with configuring a Linux machine, if you want to have WG only on your phone, skip this part.

Linux Client configuration

As in the server part, install WireGuard and setup public and private keys:

sudo apt install wireguard
wg genkey | sudo tee /etc/wireguard/private.key
sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key

The client config file looks a bit different, as it also needs information about the server.

sudo nano /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <client_private_key>
Address = 10.8.0.2/24
 
[Peer]
PublicKey = <server_public_key>
AllowedIPs = 10.8.0.0/24
Endpoint = server_public_ip:51820

Fill in the private key of the client, the public key of the server, and its public IP address. Within the VPN network, the IP address of the server will be 10.8.0.1, and the client will be 10.8.0.2.

Connecting client to server

To connect the VPN client to a server, we need to inform the server of the client’s key. Run this command on the server:

sudo wg set wg0 peer <client_public_key> allowed-ips 10.8.0.2

Finally, start the WG service on the client’s machine:

sudo wg-quick up wg0

Now you should be able to SSH into the server from the client, using the server’s VPN IP address:

ssh 10.8.0.1

For advanced usage, like tunnelling all traffic from the client through the server, consult the two articles I have linked above.

Setting up Pihole

The next step is setting up PiHole on the same server that has the WireGuard VPN server.

PiHole installation instructions can be found here. I would recommend installing Pihole as a service, and not using Docker. We will be hiding PiHole behind the UFW Firewall, and there is a known problem of UFW not blocking traffic to and from containers . It can be fixed, but the easiest solution here is just install PiHole not with Docker.

In the installation wizard one step is very important. There is a screen where PiHole asks you which interfaces should it use. By default it’s all interfaces. Change it to wg0 only. It will be another means of defense against abuse of the DNS services on your server.

A screenshot from the PiHole installation wizard showing the list of
available interfaces. Wg0 is the chosen one.

Finally, update the UFW rules to allow incoming traffic on port 53, but only through the wg0 interface:

ufw allow in on wg0 to any port 53

Making sure it all works

At this point we have WireGuard and PiHole running on our server, now it’s time to make sure that we can use them. We will test by using our VPN client machine to ask the server to resolve a DNS query, tell us the IP address of the Wikipedia. The simplest way is to use the nslookup command. If you do not have it on your machine, install the dns-utils package using your package manager.

On the client machine run:

nslookup en.wikipedia.org 10.8.0.1

The response should be similar to

Server:         10.8.0.1
Address:        10.8.0.1#53

Non-authoritative answer:
en.wkipedia.org canonical name = ncredir-lb.wikimedia.org.
Name:   ncredir-lb.wikimedia.org
Address: 208.80.154.232
Name:   ncredir-lb.wikimedia.org
Address: 2620:0:861:ed1a::9

If you are getting an error, make sure WireGuard is running on both server and client, that PiHole is running, and if the firewall rules are correct.

And now run:

nslookup en.wikipedia.org <public ip of the server>

The connection should time out. This means that the server’s DNS service cannot be used from the public Internet. This is exactly what we want. Good work!

Now we need to make the Linux machine use our new DNS server whenever it’s doing DNS queries, so basically when you are browsing the Internet. The base two ways to do it is to set in the terminal, or in the network settings widget.

Here’s how to do change it using the command line.

To change it using the Network Settings widget, open your network settings, edit the current connection, and set in the IPv4 tab, set the DNS servers field to 10.8.0.1. Disconnect and reconnect to the network. Done!

A screenshot from the network settings window. This one is from KDE but
from what I've seen, it'pretty universal between desktop environments.

Smartphone configuration

And now, after that rather long introduction, it’s time for the main topic of this post, setting up WireGuard and PiHole on the smartphone!

To use WireGuard on your smartphone, there is a WireGuard application available on the Play Store or the App Store. Install it and add a new interface using the plus sign and select “create from scratch”.

Name is whatever you want, wg0 is fine. Generate the private and public keys. The addresses field will be the VPN IP address of your smartphone. It can be the next free IP address in the 10.8.0.XXX range (the same as the server). For me its 10.8.0.3. For the DNS servers field, set it to 10.8.0.1.

Now select “Add peer” at the bottom. The peer public key is the server public key. Endpoint is the server public IP address. Allowed IPs is the range of the IP addresses in our VPN, in our case 10.8.0.0/24.

A screenshot from the WireGuard mobile application.

Save the new interface and enable it. If you have Termux or Termius, you can test the new connection by SSHing into the server at 10.8.0.1, it should work now.

And again, using Termux, you can test if your phone can resolve DNS queries with our DNS server using the same nslookup command as in the previous section.

A screenshot from the Termux terminal emulator running on a smartphone,
showing the output of the nslookup command

Because you have set 10.8.0.1 as the DNS server IP in the WireGuard app settings, your phone should now use PiHole everytime the WireGuard VPN is running on it!

The final step is letting the server know that the phone can be trusted.

Login to your server and run this command

sudo wg set wg0 peer <public_key_from_smartphone> allowed-ips 10.8.0.3

That’s it! Your phone’s web traffic is now filtered with PiHole, and it does not only apple to the web browser’s traffic, but also to all of the applications that are on it. Using your phone will now be a much lighter and more private experience :)

It took us a while to get here, but I do believe the gains are worth the hassle. When logged in to the Pihole admin interface on the server you will see how much malicious traffic is blocked from your phone (mind, this link will only work if you went and applied all of the steps of this post). Here’s an example of what PiHole blocks from my phone:

A screenshot from the PiHole admin showing thousands of blocked queries
from advert and tracking servers.

Thanks for reading! This was one of the longest and most complicated posts I’ve written so far. I hope it will be as useful to you as it is for me. Please, I’d love to hear any kind of feedback from you. You can write me an email, leave a comment below, or catch me on Mastodon. Links in the footer.

If you enjoyed this post, please consider helping me make new projects by supporting me on the following crowdfunding sites: