My home server has two main interfaces, eth1
(a standard internet connection) and tun0
(an OpenVPN tunnel). I'd like to use iptables
to force all packets generated by a local process owned by UID 1002 to exit through tun0
, and all other packets to exit through eth1
.
I can easily mark matched packets:
iptables -A OUTPUT -m owner --uid-owner 1002 -j MARK --set-mark 11
Now, I'd like to put some rule in the POSTROUTING chain (probably of the mangle table) to match packets marked with 11 and send them to tun0
, followed by a rule that matches all packets and send them to eth1
.
I found the ROUTE target, but that seems to only re-write the source interface (unless I'm reading it incorrectly).
Is iptables capable of this? Do I have to mess around with the routing table (via ip route
or just the legacy route
commands) instead?
Edit: I thought that maybe I should provide more information. I have no other iptables rules at present (although I may create some rules to carry out unrelated tasks in the future). Also, the output of ip route
is:
default via 192.168.1.254 dev eth1 metric 203
10.32.0.49 dev tun0 proto kernel scope link src 10.32.0.50
85.17.27.71 via 192.168.1.254 dev eth1
192.168.1.0/24 dev eth1 proto kernel scope link src 192.168.1.73 metric 203
I haven't touched the routing table - this is just how it is at present (although it looks fairly dirty). I'm sorry, but I don't have the legacy route
command installed on this machine.
And the output of ip addr
(of course, eth0 and eth2 can be ignored - they're NICs that aren't being used at present):
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 1c:6f:65:2a:73:3f brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:1b:21:9d:4e:bb brd ff:ff:ff:ff:ff:ff
inet 192.168.1.73/24 brd 192.168.1.255 scope global eth1
inet6 fe80::21b:21ff:fe9d:4ebb/64 scope link
valid_lft forever preferred_lft forever
4: eth2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN qlen 1000
link/ether 00:1b:21:6a:c0:4b brd ff:ff:ff:ff:ff:ff
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100
link/none
inet 10.32.0.50 peer 10.32.0.49/32 scope global tun0
Edit: I've gotten something sorta working, but it's not forwarding marked packets to tun0. Basically, I added a table (11), and used:
ip route add table 11 via 10.32.0.49 dev tun0
ip rule add priority 10000 fwmark 11 table 11
When I just sudo -u user1000 wget -qO- whatismyip.org
, I get my home's external IP address, but if I do sudo -u user1002 wget -qO- whatismyip.org
, I also get my home's IP address (but I should be getting the IP at the other end of the OpenVPN tunnel).
Running iptables -vL
confirms that the packets are getting matched by the marking rule, but they don't appear to be following the routing rule.
EDIT: I've spent a long time on this, and although it still doesn't work, I think I'm a bit closer.
The iptables rule has to be in the mangle
table's OUTPUT chain. I think I also need a MASQUERADE rule in the nat
table's POSTROUTING chain, to set the source address. However, the re-routing that occurs after OUTPUT's mangle is not working correctly.
I've spent 5 hours on this now, so I'm taking a break and will probably return to it later tonight or sometime tomorrow.