NetworkManager
#
Split VPN with a dispatcher scriptPretty much all internal services that I need access at work are gated by a VPN. It works very well most of the time, but routing video calls or other heavy traffic through it is abysmal. So, I need to split my traffic: all internal services should be routed through the VPN, and all the rest should go outside of it.
This can be solved with a script that collects all the domain names that I want to route through the VPN, resolving them to their IP:s with dig
, and adding them as routes with route replace
.
Combined with ticking the "Use this connection only for resources on its network" box in NetworkManager's GUI, we have a successful split tunnel. This can be confirmed with route
which lists all the added IP:s together with the network interface they are routed through. Hurray!
This is the configuration equivalent to ticking the box, from /etc/Networkmanager/system-connection/vpn-name.nmconnection
:
[ipv4]never-default=true
[ipv6]never-default=true
There is a problem with this approach however. If the IP:s for a route change, we need to re-execute the script in order to reach the VPN-gated service. But we don't know when or how often that might happen. We could run the script manually every time we notice that a service is unreachable, but we can do better than that.
Dispatcher scripts to the rescue!
By installing the script to /etc/NetworkManager/dispatcher.d/pre-up.d/split-tunnel
, NetworkManager will call the script once it connects to a VPN. The first argument will be the network interface name and the second argument the network event type.
From the docs:
vpn-pre-up
The VPN is connected to the network but is not yet fully activated. Scripts acting on this event must be placed or symlinked into the /etc/NetworkManager/dispatcher.d/pre-up.d directory, and NetworkManager will wait for script execution to complete before indicating to applications that the VPN is fully activated.
To really make sure that we only update our routes when necessary, we can add this snippet in the beginning of the script:
if [ "$2" != "vpn-pre-up" ]; then exit 0fi
And if we have multiple VPN:s to manage and only want this to execute for one of them, we can add the following snippet to the beginning of the script:
VPN_NAME="The name of the VPN"VPN_ACTIVE=$(nmcli -t -f name c show --active | grep -xP "$VPN_NAME")if [ -z "$VPN_ACTIVE" ]; then exit 0fi