Setup routing and iptables for new VPN connection to redirect **only** ports 80 and 443

Solution 1:

So, most of this is above, but the whole solution was as follows:

Edit /etc/iproute2/rt_tables and add 2 lines at the bottom:

101 internet
102 vpn

You can give these tables other names that make more sense, just be consistent.

Then you need to create a script (I called it rt_setup) in /etc/init.d

#!/bin/sh

DEV1=eth0
IP1=`ifconfig|perl -nE'/dr:(\S+)/&&say$1'|grep 192.`
GW1=192.168.0.1
TABLE1=internet
TABLE2=vpn
DEV2=tun0
IP2=`ifconfig|perl -nE'/dr:(\S+)/&&say$1'|grep 10.`
GW2=`route -n | grep 'UG[ \t]' | awk '{print $2}'`

ip route flush table $TABLE1
ip route flush table $TABLE2
ip route show table main | grep -Ev ^default | while read ROUTE ; do
    ip route add table $TABLE1 $ROUTE
    ip route add table $TABLE2 $ROUTE
done
ip route add table $TABLE1 $GW1 dev $DEV1 src $IP1
ip route add table $TABLE2 $GW2 dev $DEV2 src $IP2
ip route add table $TABLE1 default via $GW1
ip route add table $TABLE2 default via $GW2

echo "1" > /proc/sys/net/ipv4/ip_forward
echo "1" > /proc/sys/net/ipv4/ip_dynaddr

ip rule add from $IP1 lookup $TABLE1
ip rule add from $IP2 lookup $TABLE2
ip rule add fwmark 1 lookup $TABLE1
ip rule add fwmark 2 lookup $TABLE2

iptables -t nat -A POSTROUTING -o $DEV1 -j SNAT --to-source $IP1
iptables -t nat -A POSTROUTING -o $DEV2 -j SNAT --to-source $IP2

iptables -t nat -A PREROUTING           -m state --state ESTABLISHED,RELATED          -j CONNMARK --restore-mark
iptables        -A OUTPUT               -m state --state ESTABLISHED,RELATED          -j CONNMARK --restore-mark
iptables -t nat -A PREROUTING -i $DEV1  -m state --state NEW                          -j CONNMARK --set-mark 1
iptables -t nat -A PREROUTING -i $DEV2  -m state --state NEW                          -j CONNMARK --set-mark 2
iptables -t nat -A PREROUTING           -m connmark --mark 1                          -j MARK --set-mark 1
iptables -t nat -A PREROUTING           -m connmark --mark 2                          -j MARK --set-mark 2
iptables -t nat -A PREROUTING           -m state --state NEW -m connmark ! --mark 0   -j CONNMARK --save-mark

iptables -t mangle -A PREROUTING -i $DEV2 -m state --state NEW -p tcp --dport  80 -j CONNMARK --set-mark 2
iptables -t mangle -A PREROUTING -i $DEV2 -m state --state NEW -p tcp --dport 443 -j CONNMARK --set-mark 2

route del default
route add default gw 192.168.0.1 eth0

Then, obviously, link it from /etc/rc2.d (I use ubuntu, runlevel may differ for you). Make sure you give it an S number higher than the openvpn link!

The script does a number of things. The top portion sets up the variables, with some perl and awk statements used to pick up the dynamic IPs and gateway addresses.The second section cleans up the tables that you setup in ipruote2, and copies the current routing table to them. It then creates two new routes, and two default gateways for them, with the VPN one going over the VPN, and the internet one going over my local network.

I'm not convinced the next 2 lines are necessary, but they enable ip forwarding for use in iptables.

Next the script creates some rules on where to look for traffic originating from the relevant IP address, and where to look if the traffic is specifically marked.

The POSTROUTING and PREROUTING ensures that traffic originating from an address gets the reply!

The final iptables PREROUTING is the part the tags the traffic, and ensures that anything going to ports 80 or 443 is marked to use Table 2 (VPN)

The final two lines remove the VPN gateway from the default routing table, and add back my local network gateway.

As it stands, the process works brilliantly. The VPN is started as the machine comes up, and this script is run a few seconds later (I may add a sleep statement just to ensure the VPN is fully initialised before running this script). My remote access connection (ssh etc.) work great. My connections out that don't go to ports 80 or 443 are using my local connection, but all web traffic is going over the VPN, and bypassing the controls put in place by my ISP!

As I said in my comment under my question, I wouldn't have even started looking at this route without the suggestion of @anttir. Off the back of that suggestion, the sites http://blog.khax.net/2009/11/28/multi-gateway-routing-with-iptables-and-iproute2/ and http://linux-ip.net/html/adv-multi-internet.html have been very useful (even if the code is not 100% complete!)

Solution 2:

routing per protocol is a tad complicated. Usually routing table is used to check the gateway according to destination IP and use either the openvpn or the 192.168.0.1 default gateway.

It would be easier to set up e.g. Squid http proxy on the other end of the VPN and set browser to use the proxy.

You wouldn't use the iptables as it would change the destination IP of the HTTP connection and it would not work.

You could create a new routing table (/etc/iproute2/rt_tables) with default route set to the VPN endpoint, use iptables fwmark ( -j MARK ) to mark all the HTTP packets and then use ip rule to create a custom rule for the marked packages to use the new routing table.