PIA on a Pi
Jun 26, 2017
Here’s a short guide to setting up a VPN on Linux — specifically Private Internet Access on a Raspberry Pi, because I’m mostly writing this as a reminder to myself — with a kill switch to prevent non-VPN traffic. I’m targeting a Pi 3 with Raspbian Jessie here, but the procedure should be similar for other machines and distros as long as packages like OpenVPN and UFW are available.
# apt-get install openvpn
Download the VPN provider’s OpenVPN configuration files. For PIA, this usually means this one.
$ curl -O https://www.privateinternetaccess.com/openvpn/openvpn.zip $ unzip -d openvpn openvpn.zip
OpenVPN on Linux uses
.conf for config files instead of
.ovpn, so rename them accordingly.
$ rename 's/ovpn/conf/' openvpn/*.ovpn
To enable automatic authentication when connecting, put your VPN username and password in a file next to the configs.
$ echo 'CoolGuy123 s3cr3t' > openvpn/auth.txt
It’s not very secure, so add a little protection by limiting access to the owner.
$ chmod 600 openvpn/auth.txt
Next, make the configurations refer to this file by appending some directives at the end of each. Here I’ll also throw in a couple of quality tweaks such as keepalive, logging to make troubleshooting easier, and automatic execution of a script called
update-resolv-conf, which might be necessary for DNS resolution to work correctly when turning the VPN on and off. On Debian, this script is included in the OpenVPN install.
$ echo 'auth-user-pass auth.txt keepalive 10 60 log-append /var/log/openvpn.log script-security 2 up /etc/openvpn/update-resolv-conf down /etc/openvpn/update-resolv-conf' | tee -a openvpn/*.conf
From here, it might make sense to continue as
root. Move everything into place by copying the auth file, configurations, certificate and key file to
# cp openvpn/* /etc/openvpn
To make OpenVPN automatically connect with a certain configuration, set the
AUTOSTART directive in
/etc/default/openvpn to the configuration filename without the extension.
# echo 'AUTOSTART="Germany"' >> /etc/default/openvpn
Note 2020-03-14: since this post was first published, there’s a better way to run OpenVPN as a service. Instead of using
/etc/default/openvpn, move all the configuration files one level down into
/etc/openvpn/clientand enable the service by calling
systemctl enable openvpn-client@Germany. I’ve tested this on Raspbian Buster, but I’m unsure if it works on older versions as well.
It should now be possible to start up OpenVPN and have it connect.
# systemctl daemon-reload # systemctl restart openvpn
To verify this, wait a few seconds and run
ifconfig. There should be an interface called
tun0 or similar (for tunnel), which is the VPN interface. To test connectivity, call an IP echo service such as ipinfo.io, which should report an IP and a country matching your configuration.
$ curl ipinfo.io
UFW kill switch
At this point, if the VPN connection drops or OpenVPN stops for some reason, your machine will talk freely over your regular, non-VPN interface. Depending on the use case, it might be preferable for it to stop communicating completely instead, like if you’re
downloading warez concerned about your privacy.
To ensure this, we’re going to set up a firewall to deny everything but the VPN handshake on the regular interfaces
wlan0 while placing no restrictions on
tun0. I’m going to use UFW because it’s fairly intuitive compared to iptables.
# apt-get install ufw
Next we fill in the firewall rules. First, allow everything on OpenVPN’s network interface.
# ufw allow in on tun0 # ufw allow out on tun0
OpenVPN obviously needs to be able to connect on the regular interface, and with the default PIA configuration files, this means allowing
- DNS resolution on port 53 to find e.g.
- The actual VPN port specified by the configuration files, i.e. 1198
# ufw allow out on eth0 from any to any port 53 # ufw allow out on wlan0 from any to any port 53 # ufw allow out on eth0 from any to any port 1198 # ufw allow out on wlan0 from any to any port 1198
You might also want to have the machine reachable from the local network to SSH into it and such, so allow everything to those IPs. My local network has IPs in the 192.168.0.* range so I’ll be using that.
# ufw allow in on eth0 from 192.168.0.0/24 to any # ufw allow in on wlan0 from 192.168.0.0/24 to any # ufw allow out on eth0 from any to 192.168.0.0/24 # ufw allow out on wlan0 from any to 192.168.0.0/24
Finally, block everything else and enable the firewall.
# ufw deny in on eth0 # ufw deny in on wlan0 # ufw deny out on eth0 # ufw deny out on wlan0 # ufw enable
Now test this by stopping OpenVPN and sending a request:
# systemctl stop openvpn # curl --connect-timeout 5 ipinfo.io
Because DNS is allowed outside the VPN, ipinfo.io is resolved, but the request times out after 5 seconds because port 80 is blocked. Turning OpenVPN on again restores connectivity.
And that’s basically it. Easy, right? Not really, but it works fairly well.