3

I would like to connect to a 3rd party VPN server in Linux (e.g. Debian Jessie) but by default still use my eth0 lan interface as the default route, and am curious how to achieve this. I will be using policy routing or network namespaces or rulesets to select when I want to use the 3rd party VPN.

But it's not clear to me what openvpn is doing behind the scenes that establishes it to direct all traffic through it. To overcome this, is it as simple as overriding "redirect-gateway" when connecting from my client?

2 Answers 2

6

Here's a complete solution using control groups (cgroups), which allows per-process resource control. A network control group allows isolating the VPN route, easily allows any process and its children to be selectively run within it, allows non-root users to be granted access to running processes within the cgroup, and using a second instance of dnsmasq can isolate DNS queries as well. This assumes you have openvpn, dnsmasq, cgroup, and version 1.6+ of iptables with cgroup support installed. This was all done on Debian Jessie

The first step is to create the cgroup and setup the iptables accordingly. This should be done on every reboot, so I place the following in /etc/rc.local

# enable ip forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# create cgroup for 3rd party VPN (can change 'vpn' to your name of choice)
mkdir -p /sys/fs/cgroup/net_cls/vpn

# give it an arbitrary id 
echo 11 > /sys/fs/cgroup/net_cls/vpn/net_cls.classid

# grant a non-root user access (change user:group accordingly)
cgcreate -t user:group -a user:group -g net_cls:vpn

# mangle packets in cgroup with a mark
iptables -t mangle -A OUTPUT -m cgroup --cgroup 11 -j MARK --set-mark 11

# NAT packets in cgroup through VPN tun interface
iptables -t nat -A POSTROUTING -m cgroup --cgroup 11 -o tun0 -j MASQUERADE

# redirect DNS queries to port of second instance, more on this later
iptables -t nat -A OUTPUT -m cgroup --cgroup 11 -p tcp --dport 53 -j REDIRECT --to-ports 5354
iptables -t nat -A OUTPUT -m cgroup --cgroup 11 -p udp --dport 53 -j REDIRECT --to-ports 5354

# create separate routing table
ip rule add fwmark 11 table vpn

# add fallback route that blocks traffic, should the VPN go down
ip route add blackhole default metric 2 table vpn

# disable reverse path filtering for all interfaces
for i in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > $i; done

The next step is to edit your 3rd party VPN's client configuration file, e.g. /etc/openvpn/client.conf. Leave the rest of your config unchanged.

# redirect-gateway def1  <--- comment or remove the redirect-gateway line if it exists

# disable automatically configuring routes and run our own routeup.sh script instead
route-noexec
route-up /etc/openvpn/routeup.sh

# run our own update-dnsmasq-conf script on interface up/down; comment out existing up/down lines
up /etc/openvpn/update-dnsmasq-conf
down /etc/openvpn/update-dnsmasq-conf

We now need to create the /etc/openvpn/routeup.sh script

#!/bin/bash
# add default route through vpn gateway to our separate routing table
/sbin/ip route add default via $route_vpn_gateway dev $dev metric 1 table vpn
exit 0

And we now need to create a modified version of update-resolv-conf, which usually comes installed in /etc/openvpn, to create the second instance of dnsmasq. I call it /etc/openvpn/update-dnsmasq-conf

#!/bin/bash

[ "$script_type" ] || exit 0

split_into_parts()
{
    part1="$1"
    part2="$2"
    part3="$3"
}

case "$script_type" in
  up)
    NMSRVRS=""
    for optionvarname in ${!foreign_option_*} ; do
        option="${!optionvarname}"
        split_into_parts $option
        if [ "$part1" = "dhcp-option" ] ; then
            if [ "$part2" = "DNS" ] ; then
                NMSRVRS="${NMSRVRS:+$NMSRVRS }--server $part3"
            fi
        fi
    done
    dnsmasq $NMSRVRS --no-hosts --no-resolv --listen-address=127.0.0.1 \
        --port=5354 --bind-interfaces --no-dhcp-interface=* \
        --pid-file=/var/run/dnsmasq/dnsmasq2.pid
    ;;
  down)
    kill -9 $(cat /var/run/dnsmasq/dnsmasq2.pid)
    ;;
esac

And that should be it. Now you can start your vpn connection and selectively run processes through that interface (the --sticky option ensures that child processes are run in the same cgroup).

cgexec -g net_cls:vpn --sticky chromium &

NOTE: For dnsmasq, ensure that /etc/resolv.conf points to the localhost (nameserver 127.0.0.1). Your main dnsmasq instance will process queries on your normal non-VPN route and use (/var/run/dnsmasq/resolv.conf) which usually consists of your default gateway or some public DNS (e.g. Google's 8.8.8.8). The second instance is only used by the isolated cgroup

0

I would suggest using the route tables. You could specify the default route for all traffic, then add the routes via the VPN as needed.

Our friends over at nx.xc have a good write up on doing this.

2
  • Thanks. I mentioned I planned to add the routes to VPN as needed, but I imagine when I establish a connection to the 3rd party VPN server that openvpn does some 'magic' that makes all traffic go through that VPN by default. That's the concern and am curious how to overcome this. I'm thinking that overriding 'redirect-gateway' may be the solution, as that option is what what specifies that all traffic be routed through the VPN interface. Mar 25, 2016 at 23:33
  • so yeah, the first step was to override the VPN server's redirect-gateway option, by editing the client openvpn conf file, as described here link. Mar 27, 2016 at 0:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.