🔒 Routing specific traffic through VPN

26 Feb 2018

Virtual Private Networks are crucial things in privacy or security and most of them are configured ready to use. Although by default those are routing everything through the VPN. That is not ideal in every case, sometimes it is enough to route only a part of the traffic and leave the rest on the physical interface. Taking examples of different cases this tutorial describes the steps needed to route specific traffic only over the VPN.

OpenVPN

This must be properly installed and configured to act as the VPN client. No providers application can be used here.

  1. Install OpenVPN and its dependencies:
    sudo apt-get install openvpn
  2. Download *.ovpn config files from the provider's site. These are usually available to download as a compressed file.

  3. Extract files in a subfolder of /etc/openvpn/. It is important to put those files in a subfolder, otherwise they gets interpreted as config files when starting the systemd service.
    sudo unzip ovpn_files.zip -d /etc/openvpn/servers
  4. Now all these files needs to be updated with the route-nopull option. This is to tell OpenVPN not to route anything, just create the connection and the interface.
    for file in /etc/openvpn/servers/* ; do echo -e "\nroute-nopull" >> $file; done
  5. Starting the VPN automatically on boot requires also to have the username and password preconfigured, so it doesn't ask for it during the initialization of the connection. A single file is needed with username and password in 2 lines.
    sudo nano /etc/openvpn/.secret
  6. The file content should look something like this:
    username
    password
    When done editing, don't forget to change permissions properly:
    sudo chmod 600 /etc/openvpn/.secret
  7. Softlink one of the config files from the subfolder to the openvpn folder, but with a *.conf extension. This connection will be initialized when the systemd service has been started.
    sudo ln -s /etc/openvpn/servers/nl123.vpn-provider.tcp.ovpn /etc/openvpn/nl123.vpn-provider.tcp.conf
  8. The OpenVPN service can be started now. It is going to create a (probably) tun0 adapter, but not doing any routing so still unusable.
    sudo service openvpn start
    Check if it is really up and running:
    ps aux | grep openvpn
    ifconfig tun0
  9. Create a new routing table for the purpose by adding an extra line with the tables name in the file rt_tables.
    echo -e '\n200\tmy_table' | sudo tee --append /etc/iproute2/rt_tables > /dev/null
  10. </li>
  11. Define a new default route and gateway to the new connection. The gateway 1.2.3.4 have to be replaced with the tun0's gateway. This is usually the ip address of the interface, but the last group of numbers is 1. Like 1.2.3.1
    sudo ip route add default via 1.2.3.4 dev tun0 table my_table
  12. Add a new rule to the ip table for redirecting marked packages on the above defined route:
    sudo ip rule add fwmark 0x1 table my_table
  13. Disable the reverse path filtering on all interface to let the routing work with this config:
    for f in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $f; done
    echo 1 > /proc/sys/net/ipv4/route/flush

The VPN is already usable for now, but only by applications which supports a bind option. Since not all of them have one here comes two options for redirecting traffic on the VPN.

Redirect based on port

  1. Mark the packets on specific port to be redirected, for example the http port 80:
    sudo iptables -A OUTPUT -t mangle -o eth0 -p tcp --dport 80 -j MARK --set-mark 1
  2. Redirect every packet with a mark and rewrite the source ip address. Don't forget to replace the ip 1.2.3.4!
    sudo iptables -A POSTROUTING -t nat -o tun0 -p tcp --dport 80 -j SNAT --to 1.2.3.4

Redirect based on multiple ports range

  1. Mark the packets on specific port-range to be redirected, for example the ports between 6884-6889:
    sudo iptables -A OUTPUT -t mangle -o eth0 -p tcp --match multiport --dports 6884:6889 -j MARK --set-mark 1
  2. Redirect every packet with a mark and rewrite the source ip address. Don't forget to replace the ip 1.2.3.4!
    sudo iptables -A POSTROUTING -t nat -o tun0 -p tcp --match multiport --dports 6884:6889 -j SNAT --to 1.2.3.4

Redirect based on user

  1. Mark the packets from a specific user to be redirected. Don't forget to change the user-id 1002:
    sudo iptables -A OUTPUT -t mangle -o eth0 -p all -m owner --uid-owner 1002 -j MARK --set-mark 1
  2. Redirect every packet with a mark and rewrite the source ip address. Don't forget to replace the ip 1.2.3.4!
    sudo iptables -A POSTROUTING -t nat -o tun0 -p all -j SNAT --to 1.2.3.4

Ready to test

After it is all set-up, possible to check if it properly working by issuing the following:

tcpdump -i tun0 -n

Or by simply checking the current ip:

curl icanhazip.com