manual-connections icon indicating copy to clipboard operation
manual-connections copied to clipboard

[enhancement] Add support for Split Tunnel

Open fcastilloec opened this issue 3 years ago • 13 comments

The scripts here have been immensely helpful for my system. Now, I've reached a point where I would like to use the Split Tunnel feature of the desktop client with these scripts. Would it be possible to add this feature? I'm using Linux (and Wireguard, but should work with OpenVPN as well), I'm guessing that this will either work using containers (i.e. Docker) or namespaces. I would prefer the use of namespaces because it doesn't require to install Docker on the system. Wireguard will run under a namespace and any app that requires to use of the VPN will also run under the same namespace.

It wouldn't work as the desktop app, which automatically detects the app and routes the traffic through the VPN, instead, the user will use a script/command to start the app under the VPN/namespace; for example, piaVPN firefox will start firefox under the VPN. This approach will also allow having multiple instances of an application running, one inside, and one outside the VPN.

I only have very basic knowledge of namespaces, so I don't think I could provide a good PR for this. Hopefully, somebody can.

fcastilloec avatar Dec 02 '20 21:12 fcastilloec

I am glad you are enjoying the repo.

This repo is trying to offer scripts that are simple and accessible. Adding split-tunneling means adding a lot of complexity, that will make the scripts a lot harder to understand for most of the people that are reading through them. At this moment, the native Desktop application is offering split tunneling, and these scripts do not aim to replace the app. We are just trying to give everybody a simple way of configuring manual connections on their favorite device. Sadly we can not offer both the simplicity and the complexity at the same time.

Split tunneling would be cool, but I think it is better fitted for a fork than for this project. If somebody makes a fork or a project that will allow manual connections with split tunneling, I will add it to the 3rd Party Section in the README. By choosing this path, we get the best of both worlds. I think the issue can stay opened till that point.

If you want to tackle this yourself, you don't have to worry about making it "good". Just make it work and share it so that other people can have a look. :wink:

g00nix avatar Dec 03 '20 00:12 g00nix

I don't think that the scripts here need to change in any way. It would be more of having one additional script that creates the namespace, and then just simply run these scripts in the namespace. Perhaps I can submit a PR here, so you can take a look at what I have in mind, and other people more familiar with namespaces can fix any errors or improve it.

fcastilloec avatar Dec 03 '20 00:12 fcastilloec

@fcastilloec I've seen mentions that namespace isolation also could serve as a "kill switch". I.e. when the VPN connection breaks for some reason, then the application won't be able to communicate anymore. If that is so, then it's very useful. Do namespaces interfere with forwarded ports? Would you be so kind to add a bit of documentation to your cloned repo? (Change looks small: added two scripts. More - merrier. It's targeted at advanced/power users anyway.)

Kervius avatar Dec 10 '20 22:12 Kervius

@Kervius indeed namespaces provide isolation and could serve as a "kill switch" but the way these scripts currently work, they won't provide that isolation. If the VPN stops the apps will still have access to the internet, this was done by design so the PIA scripts can be run without any modifications. It's not hard to provide the actual isolation but it requires to use wg rather than wg-quick, which requires modification of the PIA scripts (not a lot of modification).

Regarding forwarded ports, if you're talking about port forwarding from PIA, then everything will work as it always has. On the other hand, if your app exposes a web interface, it will require extra configuration (i.e. socat). Otherwise that interface (or port) is only accessible inside the namespace.

For documentation, I think my scripts have a lot of comments to help people out but if you have any recommendations or changes, feel free to do a PR. I'm hopping PIA just decided to have the scripts in their own repo, either inside a folder or another branch, so there won't be a reason for me to have extra documentation (besides the comments)

fcastilloec avatar Dec 10 '20 23:12 fcastilloec

I'm hopping PIA just decided to have the scripts in their own repo, either inside a folder or another branch, so there won't be a reason for me to have extra documentation (besides the comments)

Yea, I am still thinking on how to include this without over-complicating everything. You still have firefox hardcoded in your PR, btw. If we do find a simple way to include this, the code still has to be clean.

g00nix avatar Dec 10 '20 23:12 g00nix

You still have firefox hardcoded in your PR, btw. If we do find a simple way to include this, the code still has to be clean.

firefox is only mentioned as an example, and only shown inside an echo command. If you think I shouldn't provide an example, or if you prefer I mention a different app, or have a different approach, do let me know and I'll change/remove that example.

fcastilloec avatar Dec 10 '20 23:12 fcastilloec

firefox is only mentioned as an example, and only shown inside an echo command.

Oh, sorry! I didn't see that! I take my previous statement back :smile:

g00nix avatar Dec 11 '20 00:12 g00nix

I found anther way to split tunnel traffic. It may be cleaner as it mostly involves modifying the wire guard configuration file. The settings can be reversed when the wire guard connection is taken offline. They should work on virtually any Linux system.

Step 1: Create a routing table in /etc/iproute2/rt_tables. For example echo "2 pia" >> /etc/iproute2/rt_tables

Step 2: Enable critical parameters to allow for IP filtering and forwarding.

net.ipv4.conf.all.rp_filter = 2
net.ipv4.ip_forward = 1

Step 3: Retrieve the gateway and interface currently being used by the default route.

export sys_gateway=$(/sbin/ip route | awk '/default/ { print $3 }')
export sys_interface=$(/sbin/ip route | awk '/default/ { print $5 }')

Step 4: Add a new default route and IPv4 filtering rule after wireguard is up.

PostUp = ip -4 route add default via "$sys_gateway" dev "$sys_interface" proto static onlink table pia
PostUp = ip -4 rule add fwmark 0x2 table pia

Step 5: Mark traffic through specified protocols and ports (using iptables) to be split tunneled. For example tcp port 22 for SSH.

PostUp = /sbin/iptables -A OUTPUT -t mangle -o pia -p tcp --sport 22 -j MARK --set-mark 2
PreDown = /sbin/iptables -D OUTPUT -t mangle -o pia -p tcp --sport 22 -j MARK --set-mark 2

Step 6: Remove the default route and IPv4 filtering rule before wireguard goes down.

PreDown = ip -4 rule del fwmark 0x2 table pia
PreDown = ip -4 route del default via "$sys_gateway" dev "$sys_interface" proto static onlink table pia

stickz avatar Dec 19 '20 03:12 stickz

I found anther way to split tunnel traffic. It may be cleaner as it mostly involves modifying the wire guard configuration file. The settings can be reversed when the wire guard connection is taken offline. They should work on virtually any Linux system.

I initially considered this approach but it mainly deals with iptables which doesn't provide enough support for application-based routing. You can route a specific port, which might be enough for some people, but not application traffic. How would you set a route for firefox traffic through PIA but letting Chromium traffic not use PIA? Having namespaces allows you to even run the same program twice, one with VPN and one without.

fcastilloec avatar Dec 22 '20 01:12 fcastilloec

As v2.0.0 got release, we can now start to look at extra features like this. We will have another look at this as soon as we have time.

g00nix avatar Jan 21 '21 23:01 g00nix

@g00nix That's great news. The only advice/request the less we mess with iptables, the better it'll be. #64 is a crude generalized way that works for both WireGuard and OpenVPN but, in the case of WireGuard only, there are easier, cleaner ways to do it.

fcastilloec avatar Jan 22 '21 01:01 fcastilloec

Your PR is more about application isolation and split tunneling than for split tunnel. You can split tunnel just by specifying the subnets in the VPN protocol, but you also want to isolate the apps. Namespaces are a good choice for this, indeed.

g00nix avatar Jan 22 '21 10:01 g00nix

In case anyone that is following this thread wants to get this working today, with openvpn, I currently use this script to create a namespace that has an isolated tun0 interface which connects to PIA. The only other interface on that namespace is loopback, so this approach functions effectively as a kill switch as well.

sbappudi avatar Jul 11 '21 20:07 sbappudi