How to use VPN for only one application on Linux
The default approach with routing all traffic over VPN can hit user experience sometimes. If user need to do some tasks with application which requires low traffic or speed - it can be done with cheap VPN, but _comfortable web browsing will be definitely broken_.
Why it's useful
The default approach with routing all traffic over VPN can hit user experience sometimes. If user need to do some tasks with application which requires low traffic or speed - it can be done with cheap VPN, but comfortable web browsing will be definitely broken.
Corporate network and web surfing
Remote job opened perfect opportunities for many people. In 99% cases, remote worker should be connected to corporate network over VPN and all traffic will be routed through company server and all browsing history will be collected. Do you want to buy a ticket or check a football match score? Surely it all can be done with personal mobile phone via 4G or 5G connection, but hey - you have already the working laptop or desktop around when doing your daily job.
Torrent client
Torrent sharing now heavily promoted by media as bad and illegal way to share files. You can't find a torrent app for iPhone, thanks Apple! Despite all, torrent protocol still popular in music, amateur movie and documentary video industries as fast way to share demo files. If you need to give your colleagues or friends 20 GB raw video with last school game - uploading it all to a file sharing will be slow in mostly cases, way easier is to create a torrent and share the magnet link.
To hide your torrent activity from automatic scanning, it's reasonable to use VPN for torrent client only and keep your web browsing experience good like before.
Namespaces on Linux
Namespaces is an environment for a process, user, block device, also for network. And this works very well for network isolation, so we can tune network stack and routing and create a full virtual network for a simple app.
- Let's create a namespace called
vpn
, allip
commands should be executed withsudo
:
ip netns add vpn
- Now we need to create virtual interfaces to access our namespace:
root namespace
<->
eth1v
<->
traffic <->
peer1
<->
namespace
- Create the virtual interface:
ip link add eth1v type veth peer name peer1
- Now add the
peer1
to ourvpn
namespace:
ip link set peer1 netns vpn
- Set IP to the virtual interface:
ip addr add 10.0.1.1/24 dev eth1v
- Now activate this interface:
ip link set eth1v up
- Now add IP
10.0.1.2
and/24
subnet to thevpn
namespace:
ip netns exec vpn ip addr add 10.0.1.2/24 dev peer1
- Activate the interface:
ip -n vpn link set peer1 up
- Add local interface in our
vpn
namespace:
ip -n vpn link set lo up
- Now route traffic from
vpn
namespace to root namespace throughethv
interface:
ip -n vpn route add default via 10.0.1.1
Here we go! Our namespace called vpn
is up and running. One task left - traffic routing through virtual interfaces.
- Enable forwarding via
sysctl
command:
echo 1 > /proc/sys/net/ipv4/ip_forward
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
- Flush all forward and NAT rules, set policy DROP by default:
iptables -P FORWARD DROP
iptables -F FORWARD
iptables -t nat -F
- Enable masquerading:
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o eth0 -j MASQUERADE
- Allow forwarding between physical
eth0
and virtualeth1v
interfaces:
iptables -A FORWARD -i eth0 -o eth1v -j ACCEPT
iptables -A FORWARD -o eth0 -i eth1v -j ACCEPT
- Allow all output traffic:
iptables -P OUTPUT ACCEPT
DNS Configuration
DNS configuration in vpn
namespace can be different from default. For example, let's use Quad9 DNS servers 9.9.9.9
.
mkdir -p /etc/netns/vpn
echo "nameserver 9.9.9.9" > /etc/netns/vpn/resolv.conf
echo "nameserver 149.112.112.112" >> /etc/netns/vpn/resolv.conf
Run and hide
Let's run an application inside our namespace. We need to activate Wireguarg connection inside vpn
namespace:
ip netns exec vpn wg-quick up wg-us-florida
And finally, now it's possible to run our application inside namespace the same way as we did before. From security point of view, let's avoid using the superuser this time:
sudo ip netns exec vpn runuser $USER -c transmission
Let's summarize it all in one Bash script called vpn.sh
:
#!/bin/bash
ip netns add vpn
ip link add eth1v type veth peer name peer1
ip link set peer1 netns vpn
ip addr add 10.0.1.1/24 dev eth1v
ip link set eth1v up
ip netns exec vpn ip addr add 10.0.1.2/24 dev peer1
ip -n vpn link set peer1 up
ip -n vpn link set lo up
ip -n vpn route add default via 10.0.1.1
echo 1 > /proc/sys/net/ipv4/ip_forward
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
iptables -P FORWARD DROP
iptables -F FORWARD
iptables -t nat -F
iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth0 -o eth1v -j ACCEPT
iptables -A FORWARD -o eth0 -i eth1v -j ACCEPT
iptables -P OUTPUT ACCEPT
mkdir -p /etc/netns/vpn
echo "nameserver 9.9.9.9" > /etc/netns/vpn/resolv.conf
echo "nameserver 149.112.112.112" >> /etc/netns/vpn/resolv.conf
ip netns exec vpn wg-quick up wg-us-florida
sudo ip netns exec vpn runuser $USER -c "$1"
Now we can use it for many applications too:
sudo vpn.sh firefox
sudo vpn.sh chrome
Another script called reset.sh
for removing the namespace and undo all changes:
#!/bin/bash
ip netns exec vpn wg-quick down wg-us-florida
rm -rf /etc/netns
ip netns delete vpn
ip addr del 10.0.1.1/24 dev eth1v
ip link delete eth1v
ip link delete peer1
echo 0 > /proc/sys/net/ipv4/ip_forward
echo "net.ipv4.ip_forward = 0" >> /etc/sysctl.conf
iptables -F
Usage is super simple: sudo reset.sh
.
Congrats! Stay secure.