This guide explains how to use a VPN service to selectively route torrent traffic only, leaving all other Internet access on the computer unaffected.

This guide assumes a Linux workstation. Sorry Windows users, but I have no idea if something like this is possible on your OS.

Key Concepts

This solution makes use of 2 key concepts:

  1. The Linux kernel supports multiple routing tables (http://linux-ip.net/html/routing-tables.html).
  2. IP packets can be easily marked with a unique marker, and routed to a specific routing table based on that marker.

Implementation

This implementation is based on creating a dedicated user that will be used for running your torrent software. Then we use iptables to mark all packets originating from that user with our special marker that will route the traffic to our special routing table that will send all traffic through the VPN. That way all other users remain unaffected.

In my case I decided to use NordVPN (https://nordvpn.com). Any other VPN provider will work, as long as it is possible for you to connect using the amazing OpenVPN software (https://openvpn.net/).

So the first step is to make sure that you can connect to your VPN with OpenVPN. The instructions for NordVPN are found here: https://nordvpn.com/tutorials/linux/openvpn/

After you have that working, proceed to the next step.

Preparation

First, we need to comment out any authentication options specified in the OpenVPN config file you downloaded from your VPN provider. In my case, I had to comment out the ‘auth-user-pass‘ directive, because I didn’t want OpenVPN to ask me for my credentials every time. I want the connection to authenticate automatically, so I opened the config file for my selected server and commented out the line:

/etc/openvpn/ovpn_tcp/ca306.nordvpn.com.tcp.ovpn:

#auth-user-pass

We’ll specify a different way to authenticate later.

Next, we need to change a couple of kernel parameters so that we can route the packets the way we want. We’ll need to edit /etc/sysctl.conf and add the following lines:

net.ipv4.ip_forward = 1             # Enable IP Forwarding
net.ipv4.conf.default.rp_filter = 0 # Disable Source Route Path Filtering
net.ipv4.conf.all.rp_filter = 0     # Disable Source Route Path Filtering on All interfaces

and then run:

sudo sysctl -p /etc/sysctl.conf

to activate your new settings.

Next, create a user just for your torrent program. You’ll also want to make sure that your torrent program is always started by this user. The reason for this is that iptables has an “owner” packet matching plugin which matches all outgoing packets belonging to a specific UID. You can use this to put a mark on all packets belonging to that user which can be used by the kernel to route those packets through a specific interface.

In this guide we’ll assume that the user is called ‘torrents.

Startup Scripts

You are now ready to copy-and-paste some startup scripts that will set everything up for you when your computer boots.

/etc/openvpn/nord_vpn_start.sh

openvpn \
    --log-append  /var/log/openvpn/nordvpn-client.log \
    --route-noexec \
    --script-security 2 \
    --up-delay \
    --up /etc/openvpn/nord_vpn_callback_up.sh \
    --auth-user-pass /etc/openvpn/nord_vpn_auth.txt \
    --config /etc/openvpn/ovpn_tcp/ca306.nordvpn.com.tcp.ovpn

Note: The last line is where you specify the OpenVPN config file for the specific VPN server you wish to use.

/etc/openvpn/nord_vpn_common_setup.sh

#!/bin/sh

# User defined config
#
export USER_NAME='torrents'        # VPN will be enabled only for this user
export PACKET_MARKER=3             # Arbitrary packet marker<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
export ROUTING_TABLE_NUMBER=200    # Arbitrary routing table number

# These enviroment variables are set by OpenVPN.
# See manpage 'Environmental Variables' section.
#
export TUN_DEV=$dev
export VPN_LOCAL_IP=$ifconfig_local
export VPN_GATEWAY_IP=$route_vpn_gateway

/etc/openvpn/nord_vpn_callback_up.sh

#!/bin/sh

. /etc/openvpn/nord_vpn_common_setup.sh

echo "================================================"
echo "       --- Hooking up to Nord VPN ---"
echo "================================================"
echo "VPN Interface: $TUN_DEV"
echo "VPN Local IP: $VPN_LOCAL_IP"
echo "VPN Gateway IP: $VPN_GATEWAY_IP"
echo

if [ -z "$VPN_GATEWAY_IP" ] ; then
    echo "ERROR: Not all expected parameters were present!\n"
    exit 1
fi

# Just in case we didn't clean up before.
echo "\nCleaning up from previous run..."
ip rule delete fwmark $PACKET_MARKER
ip route flush table $ROUTING_TABLE_NUMBER

# Attach a marker to all packets coming from processes owned by the user
echo "\nInserting iptables rules..."
iptables -t mangle -A OUTPUT -m owner --uid-owner $USER_NAME -j MARK --set-mark $PACKET_MARKER

# Everything that leaves over the VPN's TUN device should have the source address set currectly.
# Apparently some torrent clients mistakenly grab the address from eth0 instead, which makes
# the VPN drop those packets. This corrects any such packets with bad source address.
iptables -t nat -A POSTROUTING -o $TUN_DEV -j SNAT --to-source $VPN_LOCAL_IP

echo "\nMANGLE OUTPUT:"
iptables -t mangle -L OUTPUT

echo "\nNAT POSTROUTING:"
iptables -t nat -L POSTROUTING

# Everything that is marked with $PACKET_MARKER should be routed to our custom routing table
echo "\nForwading all packets marked with $PACKET_MARKER to the new routing table $ROUTING_TABLE_NUMBER...."
ip rule add fwmark $PACKET_MARKER lookup $ROUTING_TABLE_NUMBER
ip rule

# All traffic destined for the LAN should go over eth0, not the VPN
echo "\nRouting LAN packets to eth0..."
ip route add 192.168.0.0/24 dev eth0 table $ROUTING_TABLE_NUMBER

# Everything else should be routed via the VPN device
echo "\nRouting all external traffic to the VPN Gateway $VPN_GATEWAY_IP..."
ip route add default via $VPN_GATEWAY_IP dev $TUN_DEV table $ROUTING_TABLE_NUMBER 

if [ $? -ne 0 ]; then
    echo "ERROR: Could not add default route $VPN_GATEWAY_IP!\n"
    exit 2
fi

# Show the new routing table
ip route list table $ROUTING_TABLE_NUMBER

echo
echo "\nVPN is connected!\n"
ifconfig $TUN_DEV
echo

/etc/openvpn/nord_vpn_auth.txt

Your Username
Your Password

/etc/openvpn/nord_vpn_down.sh

#!/bin/sh

. /etc/openvpn/nord_vpn_common_setup.sh

echo "================================================"
echo "       --- Cleaning up after Nord VPN ---"
echo "================================================"
echo

echo "Removing iptables rules..."
iptables -t mangle -D OUTPUT -m owner --uid-owner $USER_NAME -j MARK --set-mark $PACKET_MARKER
iptables -t nat -D POSTROUTING -o $TUN_DEV -j SNAT --to-source $VPN_LOCAL_IP

echo "\nMANGLE OUTPUT:"
iptables -t mangle -L OUTPUT

echo "\nNAT POSTROUTING:"
iptables -t nat -L POSTROUTING

echo "\nRemoving custom routing table $ROUTING_TABLE_NUMBER...\n"
ip rule delete fwmark $PACKET_MARKER
ip route flush table $ROUTING_TABLE_NUMBER

ip route
echo
ip rule
echo

NOTE: This script is provided for your convenience when you wish to shut down the VPN manually for some reason. We are purposefully not attaching it to OpenVPN’s down-hook. This provides us with our own “kill switch” functionality. If the VPN goes down for whatever reason, your torrents will stop working, rather than smoothly transitioning to non-encrypted downloads without telling you.

/etc/rc.local

echo "Starting NordVPN..."
/etc/openvpn/nord_vpn_start.sh &

exit 0

The above is one of many ways to start your VPN at boot.

And that’s it. Hope it helps.

Sources

Everything in this guide is based on the following Reddit post. All I did here is provide the actual scripts to make the setup easier.