Secure Personal VPN with Ad-Blocking

4 January 2020

Privacy on the Internet has become an important topic. In recent years it has become increasingly clear that a lot of our online activity is being tracked and monitored in order to be shared with advertisers and third-parties.

A growing number of businesses claim to offer privacy-as-a-service via ad-blockers or private browsing and Virtual Private Network (VPN) services. However, if we think for a bit, it becomes unclear how sharing your traffic with another third-party (for a subscription fee) actually improves your privacy or security. Introducing an intermediary as a guarantor means just another potential weak point.

How do you know your ad-blocker isn't tracking the sites you visit? Do you trust that your VPN provider isn't monitoring your activity?

What if I could be my own Privacy-as-a-Service provider?

The solution described in this article increases your online privacy and security by filtering tracking and advertising content and hiding your network traffic from third-parties. I have personally researched the entire setup and use it on all my devices. You can configure it for yourself and use it too – at home or anywhere you travel – for a minimal cost.

Benefits

  • Reduce network traffic and increase privacy by blocking advertising content before it is downloaded
  • Increase web browsing speed and security by running your own DNS server
  • Securely access content and resources on your home network from anywhere in the world
  • Use public WiFi networks without exposing your network activity
  • Circumvent browsing restrictions of some network firewalls
  • Have full control of the entire setup and use it on multiple devices
  • It requires virtually no maintenance
  • The additional monthly cost approaches zero (amount depending on your choice of setup)

Downsides

Requires some time and effort to setup.

Prerequisites

The complete setup covers a lot of ground. Meanwhile, I have tried to offer some flexibility, so the guide can be useful to as many people as possible. It is infeasible to go into every detail, so you may need to look up some things separately. I am assuming you have familiarity with the command-line (or willingness to learn) and that you are able to make decisions when presented with on-screen instructions.

First, you will need a server exposed to the Internet. There are two options:

  1. Host your own machine at home for free
  2. Get a Virtual Private Server (VPS) for a monthly fee

The rest of the article assumes you have full access (root privileges) to a Linux machine running a variant of Debian (Ubuntu, etc). I recommend Debian stretch or Ubuntu bionic. What follows is an overview of the pros and cons of self-hosting a server versus getting a Virtual Private Server.

If you are already running a server, you can skip to the Domain Name section. If you have already set up a web server using your own domain name, go directly to Privacy Setup.

Self-Hosted Server

In some ways, hosting your server at home is the best option, since you won't rely on service providers other than your ISP. However, the reliability of this setup directly depends on the quality and throughput of your home Internet connection.

If you don't already have a Linux machine at home, you could buy a small SoC (System on a Chip) device, such as the Odroid XU4 or the RaspberryPi 4.

When the device is connected to your home router, it is normally not visible outside the local network. In order to use it as a VPN gateway, you will have to expose it to the Internet by removing the NAT restrictions (usually by setting it as a DMZ host in your home router settings). This way the router will forward all incoming traffic to your server. We will make sure that a firewall is properly configured on the machine to avoid the obvious security risks. If you only want to set up secure browsing and ad-blocking on your local network, then you don't need to put the server in DMZ, but you will not be able to set up VPN or access it outside your home.

It would be good if you can get a static IP address from your Internet Provider. You could run a VPN gateway even with a dynamic IP address, but it will require you to use a Dynamics DNS service (which is also covered).

Once you have your device set up, open the Terminal and create a user for yourself. Then, make sure to disable any SSH access to any default users (such as odroid or pi):

sudo -i
adduser -m {your username}
# create your own user making sure to pick a strong password
echo "DenyUsers pi odroid" >> /etc/ssh/sshd_config
service sshd restart

A strong password would be at least 8 characters long, containing uppercase and lowercase letters, digits and special characters.

You should now log in with your new user.

Virtual Private Server (VPS)

If your home Internet connection is unreliable or you travel a lot, it might be better to buy a VPS. You can get very good VPS deals from Contabo or OVHcloud. They both offer unlimited traffic and data centres in different locations.

You will receive instructions via email on how to log in remotely into your VPS via SSH. You most likely won't have the option to use a GUI, but that's not necessary (and should not scare you).

As an initial step, log in to your server and create a user for yourself:

ssh root@{your VPS}
# enter the provided root password when asked and you will be logged in
adduser {your username}
# create your own user making sure to pick a strong password

A strong password would be at least 8 characters long, containing uppercase and lowercase letters, digits and special characters.

You can now log in to your VPS with ssh {your username}@{your VPS}.

Domain Name

To set up VPN, your server must have a fully-qualified domain name (FQDN). It is best to buy your own, but it is not required. I recommend Namecheap as a registrar. On some TLDs you have the option to purchase additional privacy, which will hide your contact information from Whois lookup.

If you purchased a VPS, it probably came with a FQDN automatically assigned by the hosting provider. In that case, feel free to use it and skip the rest of this section.

In the control panel provided by your domain registrar, set your domain's A record to the static IPv4 address of your VPS or home network. If your home network or VPS provider supports IPv6, then you can also create an AAAA record. You could then verify that the new DNS settings have propagated:

MYDOMAIN=example.org # replace example.org with your domain name
sudo apt-get install -y dnsutils
nslookup $MYDOMAIN

If you get a similar answer without errors, you are good to go:

Server:     192.168.0.1
Address:    192.168.0.1#53

Non-authoritative answer:
Name:   example.org
Address: 123.45.67.89

If you get an error and no answer from the nameserver, get yourself a drink and come back in a little bit.

Server Configuration

Open a shell on your server (via the Terminal app or by logging in via SSH). For most of the commands you will need root privileges, so to make things smoother first become superuser:

sudo -i
# if sudo is not installed:
su -

Before continuing, update the sources list:

apt-get update

If you want to use your home server as a VPN gateway, the machine must have a FQDN assigned.

If your server has a static IP address and it has a domain pointing to it, you can proceed with the Privacy and VPN setups. You should go through the Dynamic DNS section if:

  • you have a domain name but your server does not have a static IP address; or
  • your server does not have a FQDN assigned

Dynamic DNS

If your server does not have a static IP address (usually when hosting at home), you must use a DDNS provider. I recommend DynU DDNS as they will also provide you with a FQDN for free.

  • If your server has a static IP address, you can enter it in the DynU Dynamic DNS Service settings. Then you can use your free DynU domain to access your server
  • If you have purchased your own domain, create a CNAME record in your domain registrar's control panel pointing to the domain at your DDNS provider

Verify that the new DNS settings have propagated:

MYDOMAIN=example.org # replace example.org with your domain name
apt-get install -y dnsutils
nslookup $MYDOMAIN

If you get a similar answer without errors, you are good to go:

Server:     192.168.0.1
Address:    192.168.0.1#53

Non-authoritative answer:
example.org canonical name = myddnsdomain.dynu.net.
Name:   myddnsdomain.dynu.net
Address: 123.45.67.89

If you get an error and no answer from the nameserver, get yourself a drink and come back in a little bit.

Then you should also configure your server to notify your DDNS provider of IP address changes. A small script doing frequent checks will be running on your server to automatically take care of that.

DDNS Client Configuration for DynU

You can use ddclient. First install it:

apt-get install -y ddclient libio-socket-ssl-perl
nano /etc/ddclient.conf

Then, edit the configuration file. Placeholders are indicated with curly braces:

# Configuration file for ddclient generated by debconf
#
# /etc/ddclient.conf

use=web, web=checkip.dynu.com/, web-skip='IP Address'
server=api.dynu.com
protocol=dyndns2
ssl=yes
login={your DynU login name}
password='{your DynU login password}'
{your chosen DynU domain name} # e.g. myserver.dynu.net

Save the file. Finally, restart the service and check that it is running properly:

service ddclient restart
service ddclient status

Privacy Setup

We are going to install a DNS ad-blocker and a recursive resolver. Devices on your network will then use the server in their DNS configurations.

Pi-hole blocks domains that are used for advertising or user tracking. This will greatly limit the number of ads you see on web pages and in mobile apps and also reduce your network traffic. The DNS resolver will mitigate the risk of various DNS attacks and after it builds enough cache will increase your web browsing speed. Let's do this!

Pi-hole Installation

You can simply follow the official Pi-hole installation steps. Run the script and follow the on-screen instructions. If you are already running a web server (such as Apache) you can skip installing lighttpd. You also probably want to keep the Pi-hole DHCP server disabled.

The installation script will ask you for your current primary DNS server. On your home network this is usually provided by your ISP – you can put the IP address of your home router. VPS hosting providers also run their own DNS servers (check your email). Alternatively, you could decide to use DNS services by Google or Cloudflare (1.1.1.1 or 8.8.8.8), but their response times are likely to be slower. In any case, the value you enter here is temporary until we confirm that Pi-hole is working correctly.

When the installation script finishes, check that the service is active:

service pihole-FTL status

You can now perform the Pi-hole post-installation steps so devices on your network can start using ad-blocking. After you change the DNS settings on your home router or you computer, you should be able to open the Pi-hole dashboard. To access the settings you will need to login, but first I suggest you change the default password using pihole -a -p.

Additional Block Lists

The default set of block lists is very conservative. Luckily, additional lists are maintained by users in the Pi-hole community and they can greatly decrease the number of ads. I recommend you to add all ticked lists from Firebog's block lists. Copy the list of URLs and paste it in the Pi-hole block list settings page. Then click Save and Update.

If you have free time, you could experiment with the other lists. If you notice that particular websites or mobile apps are not working properly, you might have to manually whitelist some domains from the Pi-hole dashboard. There are also browser extensions to make whitelisting easier.

Recursive DNS Installation

Pi-hole works in conjunction with a regular DNS server. Currently you rely on your ISP's DNS servers, which are usually a bit slow and in theory could be compromised by an attacker. We are going to run our own DNS server to replace them, which will be restricted only to local/VPN clients.

There are several options for running a DNS server. I recommend you follow the Pi-hole guide to set up unbound.

Finally, check that unbound is active and make sure DNS resolution is working properly:

service unbound restart
service unbound status
nslookup example.org

The answer should come from your server and you should see its local address. Until Pi-hole and unbound build enough cache, DNS queries will take a bit more time. The speed improvement will come with frequent use and in the Pi-hole dashboard you can see how many queries are answered from cache. You now have full control over ad-blocking and the security of your web browsing!

VPN Setup

Our VPN will use the IKEv2/IPSec protocol, which is widely-adopted and provides Perfect Forward Secrecy. We are going to set up the gateway using strongswan, but first you need to obtain a valid server certificate.

Let's Encrypt issue free SSL certificates that are trusted by every major operating system and can be used for our VPN gateway too. If you are running a web server and have already obtained an SSL certificate for your website, you can reuse the same one with strongswan and can skip to the Gateway Configuration section.

Server Certificate

If you don't already run a web server and you are not hosting a website, you will need to issue a new certificate for your FQDN. Install and run certbot:

apt-get install -y certbot
certbot auto certificates

Follow the on-screen instructions by entering your FQDN and choosing to run a temporary server in order to confirm your ownership of the domain. In the end, the newly-issued certificate should be located at /etc/letsencrypt/live/$MYDOMAIN.

Certbot should also automatically take care of renewing the certificate before it expires.

IKEv2/IPSec Gateway Configuration

Install strongswan and its related packages:

apt-get install -y strongswan libstrongswan-standard-plugins libcharon-extra-plugins

Get the IPv4 address of your server:

hostname -I

In some VPN configurations, DNS queries performed by clients are not tunnelled, which reduces security. In our case, clients will query the local DNS server and thus maintain complete privacy.

Edit the main configuration file /etc/ipsec.conf to add the connection settings:

config setup
    charondebug="ike 0, knl 0, cfg 0"
    uniqueids=never

conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes
    dpdaction=clear
    dpddelay=300s
    rekey=no
    left=%any
    leftid=@{your domain name} # e.g. @domain.example.org
    leftcert=fullchain.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0
    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightsourceip=10.1.1.2/24
    rightdns={machine IPv4 address} # use own DNS server
    rightsendcert=never
    eap_identity=%identity

include /var/lib/strongswan/ipsec.conf.inc

Then, create symbolic links so the IPSec daemon uses the certificate you obtained from Let's Encrypt. If you obtained your certificate differently, you will need to make sure you have the certificate itself, the certificate chain and the unencrypted private key separately, in PEM format:

ln -s /etc/letsencrypt/live/$MYDOMAIN/fullchain.pem /etc/ipsec.d/certs/fullchain.pem
ln -s /etc/letsencrypt/live/$MYDOMAIN/privkey.pem /etc/ipsec.d/private/privkey.pem
ln -s /etc/letsencrypt/live/$MYDOMAIN/chain.pem /etc/ipsec.d/cacerts/chain.pem

The SSL certificate will be automatically renewed every few months. To make sure strongswan picks up the changes, you should add a renewal hook:

nano /etc/letsencrypt/renewal-hooks/deploy/reload-strongswan.sh

Add the following script and make sure to replace $MYDOMAIN with the actual domain name that you set up in ipsec.conf:

#!/bin/sh
set -eu

for domain in $RENEWED_DOMAINS; do
    case $domain in
        $MYDOMAIN)
            service strongswan restart >/dev/null
            ;;
    esac
done

Finally, edit /etc/ipsec.secrets to create a user account for your VPN:

# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

# this file is managed with debconf and will contain the automatically created private key
include /var/lib/strongswan/ipsec.secrets.inc

: RSA privkey.pem
{your username} : EAP "{your password}"

Restart the strongswan service and make sure everything is running correctly:

service strongswan restart
service strongswan status

If there are no errors, you can try to connect from a device using your username and password. Check the pending and active connections:

ipsec statusall

Traffic Forwarding

In order to access the Internet and browse websites on your VPN connection, your server must route this traffic. First make sure IP forwarding is enabled and redirects are disabled:

echo 1 >| /proc/sys/net/ipv4/ip_forward
echo 0 >| /proc/sys/net/ipv4/conf/all/accept_redirects
echo 0 >| /proc/sys/net/ipv4/conf/all/send_redirects

Then, add rules so all traffic originating from VPN clients is forwarded to the active network interface on your server (such as eth0). Run ifconfig to double-check the network interface ID and then create the following rules:

iptables -t nat -A POSTROUTING -s 10.1.1.0/24 -o {interface ID} -m policy --dir out --pol ipsec -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.1.1.0/24 -o {interface ID} -j MASQUERADE

To persist those rules after a server restart, create this file /etc/network/if-up.d/setup-iptables and paste them in. You should now be able to access the Internet on your VPN!

Security Settings

Every server is exposed to potential attacks. Malicious hackers or bots will inevitably try to gain access to your machine by scanning for open ports or try to take advantage of known vulnerabilities. We want to mitigate those risks.

Remote Login

Following previous advice, should have created your own user with a strong password. In addition, you should disable SSH root login. Edit /etc/ssh/sshd_config.

In the configuration file, find the switches listed below, make sure they are uncommented and have the following values:

PermitRootLogin no
PubkeyAuthentication yes
AuthorizedKeysFile  .ssh/authorized_keys .ssh/authorized_keys2

Save the file. Finally, restart sshd and check that it is running correctly:

service sshd restart
service sshd status

Intrusion Prevention

We want to block suspicious connection attempts and limit the opportunities for probing your server with remote exploits. Install fail2ban, which will monitor the system logs for strange activity and block potential intruders:

apt-get install -y fail2ban

Follow the on-screen instructions to finish the installation. Then check if everything is working properly:

service fail2ban status

To check which services are on the jail list and how many clients are blocked:

fail2ban-client status

Security Test

Finally, it is good to run a security test on the server. We are going to use the Gibson Research ShieldsUp test. If the server is in DMZ on your local network, simply open the link. If you are hosted on a VPS, connect to your VPN before opening the link.

We are interested in the results from the "All service ports" test. All squares should be blue or preferably green, except for port 22 (SSH). On my installation, Pi-hole was keeping port 53 open to any client. We want to restrict the DNS server only to local/VPN clients.

Securing Open Ports

First make sure that ubound binds to specific interfaces (localhost is mandatory). Add the following lines at the end of /etc/unbound/unbound.conf.d/pi-hole.conf:

    interface: 10.1.1.1
    interface: 127.0.0.1
    access-control: 10.1.1.0/24 allow
    access-control: 127.0.0.1 allow

Save the changes and run service unbound restart.

In iptables you can add rules that essentially act as a firewall. For example, to limit Pi-hole to only serve clients within the local network (or VPN clients), add the following rules:

# Allow DNS (53) from <source IP>
iptables -A INPUT -p udp --dport 53 -s 10.1.1.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -s 10.1.1.0/24 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -s 127.0.0.1 -j ACCEPT

# Deny all other DNS requests
iptables -A INPUT -p udp --dport 53 -j DROP
iptables -A INPUT -p tcp --dport 53 -j DROP

In this example I put the 10.1.1.0/24 network which we used earlier for VPN clients. We also must allow connections from localhost and make rules for both TCP and UDP packets. Packets originating from any other network will be dropped (so to those devices it will look like no machine is running on that port).

To reapply the above rules if the system is restarted, I append them to /etc/network/if-up.d/setup-iptables. Note that the order of the rules is important. You can review the added rules with iptables -L. Finally, run the open ports test again. Use netstat -ntupl to get ideas about other ports that might be worth checking for.

Conclusion

The solution we explored provides a high level of privacy on the Web and in mobile apps by blocking most advertising and activity-tracking content. It can be set up for clients on a local network and also for remote ones. On unsecured networks, remote clients get the same level of privacy as local ones via an encrypted tunnel. The risk of DNS-related attacks is almost eliminated and the owner maintains full control of the setup. If desired, the authentication of remote clients can be further secured by issuing client-side certificates.