Policy routing for OpenVPN server & client on the same router

iproute2iptablesopenvpnroutingvpn

PROBLEM

An OpenVPN server instance (tun, udp, port 1194) is set up on a Linux-based router that also runs an OpenVPN client instance (tun, udp, port 1197) connecting it to a VPN provider. Both the client and the server instance work fine individually. However, clients cannot connect to the VPN server when the VPN client instance is enabled.

I'm sure this happens because the VPN client instance modifies the main routing table so that all traffic from (OUTPUT) and through (FORWARD) the router is routed to the VPN provider. This is the desired default behaviour, but not for connections that are being initiated from the Internet.

Using iptables and/or ip, how can a routing policy be set up so that all (or at least VPN) connections initiated from the Internet are routed via the standard default gateway (192.168.1.1)?

SETUP

LINUX ROUTER
-----------------------------------------
| LAN if:        br-lan, 192.168.2.1/24 |
| VPN client if: tun0,   10.63.10.6/32  |
| VPN server if: tun1,   10.255.0.1/24  |
| DMZ if:        eth0,   192.168.1.2/24 |
-----------------------------------------
               |
GATEWAY ROUTER |
--------------------------
| DMZ if: 192.168.1.1/24 |
| WAN if: x.x.x.x/x      |
--------------------------
               |
INTERNET       |     VPN CLIENT OF LINUX ROUTER (public IP: y.y.y.y/y)
-----------------    --------------------------------------
|               |----| VPN client if: tun1, 10.255.0.6/24 |
-----------------    --------------------------------------
               |
               |
VPN PROVIDER OF LINUX ROUTER
--------------------------------    
| VPN server if: 10.63.10.1/32 |
--------------------------------

As you will notice we're in a double NAT situation here. As uncomfortable as it may be this is not the problem, since clients can connect when the VPN client instance is disabled.

With the VPN client and server instances enabled, this is the main routing table, as given by ip route list table main:

0.0.0.0/1 via 10.63.10.5 dev tun0   #added by VPN client instance
default via 192.168.1.1 dev eth0  proto static 
10.63.10.1 via 10.63.10.5 dev tun0   #added by VPN client instance
10.63.10.5 dev tun0  proto kernel  scope link  src 10.63.10.6   #added by VPN client instance
10.255.0.0/24 via 10.255.0.2 dev tun1 
10.255.0.2 dev tun1  proto kernel  scope link  src 10.255.0.1 
128.0.0.0/1 via 10.63.10.5 dev tun0   #added by VPN client instance
178.162.199.211 via 192.168.1.1 dev eth0   #added by VPN client instance
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.2 
192.168.2.0/24 dev br-lan  proto kernel  scope link  src 192.168.2.1 

and these are the ip rules, as given by ip rule list:

0:  from all lookup 128   #a non-existent table
1:  from all lookup local 
32766:  from all lookup main 
32767:  from all lookup default   #empty table

ATTEMPTS

First of all I've built a no_vpn_provider routing table (echo "2 no_vpn_provider" >> /etc/iproute2/rt_tables), an exact copy of the main table without the VPN client instance modifications. ip route list table no_vpn_provider shows

default via 192.168.1.1 dev eth0  proto static 
10.255.0.0/24 via 10.255.0.2 dev tun1 
10.255.0.2 dev tun1  proto kernel  scope link  src 10.255.0.1 
192.168.1.0/24 dev eth0  proto kernel  scope link  src 192.168.1.2 
192.168.2.0/24 dev br-lan  proto kernel  scope link  src 192.168.2.1

1) I tried these simple ip rules

ip rule add from 192.168.1.2 table no_vpn_provider priority 2
ip rule add from 10.255.0.1 table no_vpn_provider priority 3

where 192.168.1.2 and 10.255.0.1 are the IP addresses of the external interface eth0 and of the VPN server interface tun1 respectively.

I also tried from all iif eth0 and from all iif tun1 instead of from 192.168.1.2 and from 10.255.0.1.

2) I tried marking with 0x1 new connections (the conntrack module is installed) to interfaces eth0 and tun1

iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -i eth0 -j CONNMARK --set-mark 0x1
iptables -t mangle -A PREROUTING -m conntrack --ctstate NEW -i tun1 -j CONNMARK --set-mark 0x1

and adding this rule to make marked connections use the new routing table

ip rule add fwmark 0x1 table no_vpn_provider priority 2

3) I tried marking outgoing packets from the VPN server port 1194

iptables -t mangle -A OUTPUT -p udp --sport 1194 -j MARK --set-mark 0x1
iptables -t mangle -A OUTPUT -p tcp --sport 1194 -j MARK --set-mark 0x1   # just in case

and using the same rule

ip rule add fwmark 0x1 table no_vpn_provider priority 2

I've also tried 2) and 3) with different marks and with inserting (-I) instead of appending (-A). Suspecting my firewall wasn't marking at all I tested

iptables -t mangle -A FORWARD -s 192.168.2.0/24 -j MARK --set-mark 0x1
ip rule add fwmark 0x1 table no_vpn_provider priority 2

but, as expected, packets from my LAN didn't get forwarded to the VPN provider.

4) The only thing that works so far is this ugly rule

ip rule add from all to y.y.y.y/y table no_vpn_provider priority 2

where y.y.y.y/y is the public IP address of the client trying to connect: this is of course a terrible solution, because the clients are not going to connect always from the same network.

Best Answer

To make all packets sent by an interface with IP address 192.168.1.2 use a custom routing table no_vpn_provider, you can simply use

ip rule add from 192.168.1.2 table no_vpn_provider priority 2

as I did in attempt 1). The problem is that by default an OpenVPN server doesn't bind to any specific IP address, so the above rule won't have any effect (https://serverfault.com/a/228258).

To bind your OpenVPN server to the IP address 192.168.1.2, just add this line to its configuration file and you're good to go

local 192.168.1.2

NOTE If you create the rule and the custom routing table with ip, they will be erased every time you reboot the router. Depending on your openvpn configuration, the custom routing table may also get modified every time you restart openvpn. You can write scripts that re-create the rule and the custom routing table whenever it's needed.

Related Question