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 tableno_vpn_provider
, you can simply useas 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 goNOTE If you create the rule and the custom routing table with
ip
, they will be erased every time you reboot the router. Depending on youropenvpn
configuration, the custom routing table may also get modified every time you restartopenvpn
. You can write scripts that re-create the rule and the custom routing table whenever it's needed.