Blog | Tristan Kernan

“That some of us should venture to embark on a synthesis of facts and theories, albeit with second-hand and incomplete knowledge of some of them – and at the risk of making fools of ourselves” (Erwin Schrödinger)

Split Tunneling with Network Interfaces

In setting up my new homelab server, I ran across a networking issue: my cloudflare tunnels were regularly failing when connected through my vpn. The failing network architecture:

architecture-beta group homelab(server)[Homelab] service qbt(server)[Qbittorrent] in homelab service cft(server)[Cloudflare Tunnel] in homelab service app1(server)[Application 1] in homelab service app2(server)[Application 2] in homelab service vpn(internet)[VPN] service pub(internet)[Public internet] junction jleft in homelab junction jcenter in homelab junction jright in homelab junction cfj in homelab qbt:T -- B:jleft jleft:R -- L:jcenter jcenter:T -- B:vpn cft:T -- B:jright jright:L -- R:jcenter vpn:T -- B:pub cfj:T -- B:cft app1:R -- L:cfj app2:L -- R:cfj

I experimented with different vpn endpoints, but each new endpoint would reliably fail after a short time. Clearly cloudflare is blocking connections through the vpn. Disabling the vpn was out of the question, so I researched different approaches. Split tunneling is afforded by vpns in wireguard via AllowedIPs, but that only works if the destination IPs are known and stable - which in p2p they are not. I needed to split tunnel based on the application itself, as in the following network diagram:

architecture-beta group homelab(server)[Homelab] service qbt(server)[Qbittorrent] in homelab service cft(server)[Cloudflare Tunnel] in homelab service app1(server)[Application 1] in homelab service app2(server)[Application 2] in homelab service vpn(internet)[VPN] service pub(internet)[Public internet] junction cfj in homelab qbt:T -- B:vpn cft:T -- B:pub cfj:T -- B:cft app1:R -- L:cfj app2:L -- R:cfj

Claude suggested a fairly complicated solution, involving isolated networks and bridges. I demurred and searched for something sleek and simple, and found it in the qbittorrent settings: binding the application to a specific network interface, in this case the vpn network interface. I had actually enabled this before, in order to cut off network access when the vpn is down. The missing piece of the puzzle was in the wireguard configuration: setting Table = off disables wireguard cli from configuring automatic network route binding, in other words from automatically routing all traffic through the vpn interface. Applications may still route traffic through the vpn network interface by explicitly specifying it via the SO_BINDTODEVICE socket option.

Not all applications support binding to a particular network interface, so this won't always work; when it does, as in this case with qbittorrent, it makes for trivial split tunneling.