Simple VPN Split Tunnel on Linux

The IP-based split-tunneling that I explore here allows control over which IP addresses go through a default VPN tunnel and which instead go through your regular network interface (or another VPN).

Simple VPN Split Tunnel on Linux
Photo by Jordan Harrison / Unsplash

I recently switched over to ProtonVPN and found that the Linux Desktop App does not have split-tunnel options like it does in the Android App.

Luckily it seems pretty easy to configure with the ip tool and it is compatible with both Wireguard and OpenVPN.

Eventually I plan to use this with Tailscale. ProtonVPN will protect all of my internet traffic, excluding what is sent through my Tailscale Mesh Network. Tailscale handles routing to services that are otherwise not exposed to the public internet, such as in my HomeLab.

The command looks like this:

sudo ip route add $EXCLUDED_ROUTE via $GATEWAY dev $INTERFACE

sudo ip route add 100.64.0.0/10 via 192.168.0.1 dev wlp2s0

Where EXCLUDED_ROUTE is the CIDR range you want to re-route from the current default (VPN tun0) to somewhere else. The example above uses Tailscales IPv4 range as listed here.

INTERFACE and GATEWAY are the new target network. In my case I use my devices WiFi interface wlp2s0 that routes through my networks 192.168.0.1 gateway router.

I verified the above command by opening several 'What's my IP' webpages and found their IP addresses in my Browsers Network tab. By using the route add command with those Webpage IPs and refreshing the page they would now show my unmasked IP address. Refreshing other pages whose IP addresses I have not exclude still correctly show the VPN Source IP.

These route changes do not persist between reboots. If you want them to be run automatically then add them to a startup script or wrap them in a service.

⚠️
There are obviously security implications of bypassing your VPN in this way - proceed with caution.

Finally, this script makes it easier to find your gateway and interface.

#!/bin/bash

# Usage: ./add-route-exclusion.sh 100.64.0.0/100

EXCLUDED_ROUTE=$1
# Validate CIDR notation address for user input
if [[ ! "$EXCLUDED_ROUTE" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ ]]; then
    echo "Error: Not a valid CIDR notation address."
    echo "Usage: $0 <CIDR_notation_address>"
    exit 1
fi

# Get our default route (excluding the VPN tunnel tun0)
ROUTE=$(ip route | grep default | grep -v tun0 | head -n1)
GATEWAY=$($ROUTE | awk '{print $3}')
INTERFACE=$($ROUTE | awk '{print $5}')

# e.g. ip route add 100.64.0.0/10 via 192.168.0.1 dev wlp2s0
ip route add $EXCLUDED_ROUTE via $GATEWAY dev $INTERFACE

# Remove the route with this
#ip route del $EXCLUDED_ROUTE via $GATEWAY dev $INTERFACE

GitHub - jroddev/wireguard-example: Example config for testing wireguard
Example config for testing wireguard. Contribute to jroddev/wireguard-example development by creating an account on GitHub.
Can I use Tailscale alongside other VPNs?
Learn about Tailscale compatibility or incompatibility with other VPNs.
What is VPN split tunneling, and how does it work?
VPN split tunneling lets you tailor your VPN protection to your needs. Secure your traffic while letting some apps bypass encryption. Learn more.