Page 1 of 1

[TUTORIAL] Split VPN in Linux

Posted: Thu Aug 27, 2020 7:06 am
by Airton
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:
  • 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.
This solution was assisted by conversations with mhertz, and specifically references to this guide, which no longer works but had some useful information.

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

      Code: Select all

      EnvironmentFile=/etc/deluged_env
      
      and modify the following

      Code: Select all

      ExecStart=/usr/bin/deluged -d -i $vpn_ip
      
  • Start/enable OpenVPN, e.g. with sudo systemctl enable --now openvpn-server@<config>.service
That's it! OpenVPN will start, the vpn-up.sh script will create the tunnel, save the IP, and restart deluged, then the deluged service will launch with the IP address.

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.

Re: [TUTORIAL] Split VPN in Linux

Posted: Thu Aug 27, 2020 9:56 am
by shamael
thx for sharing it! ;)

Re: [TUTORIAL] Split VPN in Linux

Posted: Thu Aug 27, 2020 10:04 am
by Airton
shamael wrote:thx for sharing it! ;)
You are welcome! It took tens of hours to work out all the bits, so hopefully it's helpful to someone else!