[TUTORIAL] Split VPN in Linux
Posted: Thu Aug 27, 2020 7:06 am
Background
After spending several weeks on this, I've finally worked out how to create a split VPN in Linux. That is, all traffic on my system goes through the normal network, except for deluged's torrents, which go through the VPN. Deluge's UI also goes through the normal network, allowing you to connect to it locally.
This guide was written for Arch Linux on a Raspberry Pi, using Mullvad for the VPN, but it will probably work with most up-to-date Linux distros and VPN providers. I've based it around systemd services, but you can modify those parts and use whatever you prefer.
I couldn't find much information on this on the internet, but there are a few other options:
Guide
Additional information
AFAIK there is no way that this can cause an IP leak. If OpenVPN isn't running, /etc/deluged_env won't exist and the service fails to start. If OpenVPN goes down, the "old" IP address just doesn't work any more. Deluged will stop torrenting.
Unfortunately I had to employ a few workarounds. I couldn't restart deluged with try-restart, because it might have previously failed to start as above. Instead, I checked to see if deluged was enabled, and if so, then started the service.
I also couldn't find a way to make the deluged service require the OpenVPN service instance, so when rebooting a system, it will fail to start several times before the VPN is up. It should work fine once the VPN is up though.
After spending several weeks on this, I've finally worked out how to create a split VPN in Linux. That is, all traffic on my system goes through the normal network, except for deluged's torrents, which go through the VPN. Deluge's UI also goes through the normal network, allowing you to connect to it locally.
This guide was written for Arch Linux on a Raspberry Pi, using Mullvad for the VPN, but it will probably work with most up-to-date Linux distros and VPN providers. I've based it around systemd services, but you can modify those parts and use whatever you prefer.
I couldn't find much information on this on the internet, but there are a few other options:
- There are some scripts here that might be useful, but according to the issues it appears untested and only partially working as is.
- There is a docker image, but it only works for 64-bit systems. I'm running a 32-bit system. Also, the solution proposed below is transparent, so you know exactly what you are doing to your system.
Guide
- Download and copy the OpenVPN configuration files to /etc/openvpn/server. (I changed permissions to root:network, but this is probably not important.)
- Create the following script at /etc/openvpn/server/vpn-up.sh . Make sure you have ipcalc installed.
Code: Select all
#!/bin/sh # Configure routing tables to implement split routing based on source IP. # This script should be called by the `--up` option. # abort on fail set -e rt_table="mullvadtunnel" # populate variables manually, because the parameters fed to this script now fail table_ip=$(ip route show table main | grep $ifconfig_local | cut -d' ' -f1 ) HostMax=$(ipcalc $table_ip | grep HostMax | awk '{print $2}') # save IP address for deluged echo "vpn_ip=${ifconfig_local}" > /etc/deluged_env # make routing tables ip rule add from "$ifconfig_local" table "$rt_table" ip route add table "$rt_table" default via "$HostMax" ip route add table "$rt_table" "$HostMax" via "$ifconfig_local" dev "$dev" # restart deluged if [ -e /etc/systemd/system/multi-user.target.wants/deluged.service ]; then systemctl restart deluged.service; fi
- Create the following script at /etc/openvpn/server/vpn-down.sh
Code: Select all
#!/usr/bin/env sh # Tear down rules which implement split routing based on source IP. This # script should be called by the `--down` option. rt_table="mullvadtunnel" rm /etc/deluged_env ip rule delete from "$ifconfig_local" table "$rt_table" ip route flush table "$rt_table" systemctl stop deluged.service
- Make both these files executable, and modify the main OpenVPN configuration file at /etc/openvpn/server/<config>.conf: add route-nopull, and modify up and down as follows:
Code: Select all
up /etc/openvpn/server/vpn-up.sh down /etc/openvpn/server/vpn-down.sh
- Create a reference to the tunnel with
Code: Select all
$ sudo bash -c 'echo 200 mullvadtunnel >> /etc/iproute2/rt_tables'
- Deluge's GUI allows us to specify the VPN IP address in "Incoming Address", but this changes each time I connect to Mullvad. The script above writes the IP address to a file, and we can automatically read this when we start deluged. Hence, modify /usr/lib/systemd/system/deluged.service.
- In the [Service] section, add
and modify the following
Code: Select all
EnvironmentFile=/etc/deluged_env
Code: Select all
ExecStart=/usr/bin/deluged -d -i $vpn_ip
- In the [Service] section, add
- Start/enable OpenVPN, e.g. with sudo systemctl enable --now openvpn-server@<config>.service
Additional information
AFAIK there is no way that this can cause an IP leak. If OpenVPN isn't running, /etc/deluged_env won't exist and the service fails to start. If OpenVPN goes down, the "old" IP address just doesn't work any more. Deluged will stop torrenting.
Unfortunately I had to employ a few workarounds. I couldn't restart deluged with try-restart, because it might have previously failed to start as above. Instead, I checked to see if deluged was enabled, and if so, then started the service.
I also couldn't find a way to make the deluged service require the OpenVPN service instance, so when rebooting a system, it will fail to start several times before the VPN is up. It should work fine once the VPN is up though.