1 - PIA
PIA is a decent VPN service with reasonable community engagement1. They support wireguard (the best protocol) and have some serviceable command line tools, useful for headless servers.
The latest release (v2.0) is somewhat dated, so you may want to check before proceeding. There are some third-party tools, but they lack port-forwarding.
Install
# Install without recommends if you're in a lxc container
sudo apt install --no-install-recommends ca-certificates curl iptables jq wget wireguard-tools
# Download, extract and move. The scripts expect a home of "/opt/piavpn-manual"
wget -O - https://github.com/pia-foss/manual-connections/archive/refs/tags/v2.0.0.tar.gz | tar xzf -
sudo mv manual-connections-2.0.0 /opt/piavpn-manual
Test
The README details a lot of what you need to know. After reading it, you’ll gleen that you just need to run the run_setup.sh script.
# The scripts expect this working directory.
cd /opt/piavpn-manual
# Run a test with port-forwarding
sudo VPN_PROTOCOL=wireguard DISABLE_IPV6=yes AUTOCONNECT=true PIA_PF=true PIA_DNS=true PIA_USER=pXXXXXXX PIA_PASS=XXXXXXXXXX /opt/piavpn-manual/run_setup.sh
You should see messages like these in the text.
...
The WireGuard interface got created.
...
Trying to bind the port... OK!
...
If so, success!. Stop the process and take down the interface
# Hit Ctrl-C to stop the port forwarding script, then down the interface with:
sudo wg-quick down pia
Create Service
If the test went well it’s time to create a service unit. This outputs any messages to the system journal and doesn’t request a port-forward.
sudo vi /etc/systemd/system/wg-pia.service
[Unit]
Description=PIA WireGuard Connection
After=network-online.target
Wants=network-online.target
[Service]
SyslogIdentifier=wg-pia
Type=oneshot
Restart=no
RemainAfterExit=true
WorkingDirectory=/opt/piavpn-manual
# Set up the ENVs. You could also use an EnvironmentFile if you prefer
Environment="VPN_PROTOCOL=wireguard"
Environment="DISABLE_IPV6=yes"
Environment="AUTOCONNECT=true"
Environment="PIA_PF=false"
Environment="PIA_DNS=true"
Environment="PIA_USER=XXXXXXX"
Environment="PIA_PASS=XXXXXXX"
ExecStart=/opt/piavpn-manual/run_setup.sh
ExecStopPost=/usr/bin/wg-quick down pia
# Need to check DNS is responding before we proceed
ExecStartPre=/bin/bash -c 'until ping -c 1 google.com &> /dev/null; do sleep 1;done'
ExecStart=/usr/bin/bash -c "VPN_PROTOCOL=wireguard DISABLE_IPV6=yes DIP_TOKEN=no AUTOCONNECT=true PIA_DNS=true PIA_PF=false PIA_USER=XXXXXXX PIA_PASS=XXXXXXX /opt/piavpn-manual/run_setup.sh"
ExecStop=/usr/bin/bash -c "wg-quick down pia"
User=root
[Install]
WantedBy=multi-user.target
If you want to forward a port just change these lines. The script will stay running to keep the forwarded port active.
Type=simple
Restart=always
Environment="PIA_PF=true"
Reload, enable and start, and then keep an eye on how it’s doing by following its journal entries.
sudo systemctl daemon-reload
sudo systemctl enable --now wg-pia.service
sudo journalctl -f -u wg-pia.service
Detecting the Forwarded Port
If you forwarded a port you probably need to know what it is. There’s nothing built-in for that, but you can hack PIA’s port_forwarding.sh script to write it out somewhere, or you can look at the log. That’s a bit less hacky.
Tac this onto the bottom of the [Service] section above. It will continue running to watch the log and update /run/pia-port as needed.
ExecStartPre=/bin/sh -c 'journalctl -u pia-wg -f --no-pager \
| grep --line-buffered -oP "Forwarded port\s+\K[0-9]+" > /run/pia-port &'
ExecStopPost=rm /run/pia-port
Notes
IPtables
PIA uses wg-quick in a way that assumes you have iptables installed. Many containers don’t and there’s no easy way to tell PIA that. Installing iptables it takes less than a Meg, so you can do that, or you can just create a dummy command.
echo -e '#!/bin/sh\nexit 0' | sudo tee /sbin/iptables-restore
sudo chmod +x /sbin/iptables-restore
Port Change Tracking
The port detection process leaves grep running, but it’s debatable if you actually need that. PIA says the port request has a two month lifespan. And we are just assuming the pia port forwarding script gets a new one when it expires. Also, anything using that port will need to know it changed. It was actually less code to leave it running, so I left it as such.