M. Rincón Guided by the asterik

Making ProtonVPN Usable on Linux


ProtonVPN provides a GUI and a console application for Linux, but on 2023, it is nearly unusable. It consumes up to 2GB of memory idled, drops connections and fails to reconnect unless the kill switch is deleted using nmcli. And the command line application has a bizarre dependency on nm-applet, which of course means it cannot be used on a headless machine. Brilliant. And it appears that ProtonVPN is not going to fix this. The solution is to use ProtonVPN with Wireguard —or better yet, find a provider that cares about Linux.

First, installed Wireguard. Then, I logged into ProtonVPN, and in the download section, made and downloaded a few Wireguard configuration files. Once downloaded, I added the connections using NetworkManager.

nmcli con import type wireguard file <filename.ovpn>

By default, all wireguard connections will auto connect on start up. Since I only want one default connection, I changed that. In addition, I disabled IPv6 on all the connection.

# The first value will be set as default + autoconnect
mapfile -t WG < <(nmcli con show | grep wireguard | cut -d' ' -f1 | tr '\n' ' ' )

# No ipv6, auto connect to first only
for CONN in "${WG[@]}"; do
    nmcli con modify "${CONN}" ipv6.method "disabled"
    nmcli con mod "${CONN}" connection.autoconnect "${AUTOCONNECT}"

echo "Autoconnect Settings:"
echo ""
nmcli -f name,autoconnect connection

I also wanted a killswitch, so I used ufw to configure one. First I disabled connections outside my LAN.

# ignore the range, that's the loop back
ip address | grep inet

ufw reset
ufw default deny outgoing
ufw default deny incoming
# whitelist the range found with ip address
ufw allow in to
ufw allow out to

Because I wanted to be able to reconnect without turning off the firewall, I also made exceptions for the VPN connections.

mapfile -t WG < <(nmcli con show | grep wireguard | cut -d' ' -f1 | tr '\n' ' ' )

for CONN in "${WG[@]}"; do
   mapfile -t EP < <(grep endpoint <
         /etc/NetworkManager/system-connections/"${CONN}".nmconnection |
         cut -d= -f2 |
         tr : ' ')
   if [[ -v EP[0] ]] && [[ -v EP[1] ]]; then
       ufw allow out to "${EP[0]}" port "${EP[1]}" proto udp
       # send everything on VPN tunnel (equivalent to tun0 for openVPN)
       ufw allow out on "${CONN}" from any to any
       # Enable for bit torrent
       # ufw allow in on "${CONN}" from any to any
       echo "Unable to determine endpoint for ${CONN}" >&2
       echo "Skipped" >&2

# Enable ufw if needed
{ ufw status | grep inactive; } && ufw enable
echo "Rules:"
ufw status verbose

At this point there shouldn't be any connection outside the LAN unless I have a VPN connections.

# ping google should fail
# but work after vpn is activate
nmcli --ask con up <vpn name>
ping google
# we can also check for leaks
curl https://ipleak.net/json/ | bat -l json
# and in the DNS
session=$(echo mrfox | sha1sum)
for i in $(seq 5000 5003);
   curl "https://${session:0:40}-${i}.ipleak.net/dnsdetection/"
   sleep 1

And that's that. If I need to disable the VPN I can easily do that, and things should work as long as I also disable the firewall.