Making all network traffic for a Linux user use a specific network interface
I’ve recently been testing out a VPN service, and normally while running the VPN, all internet traffic goes over the VPN interface. This isn’t really ideal, as I only want traffic from specific applications to use the VPN. IPTables doesn’t seem to have the option to filter specific processes, but it can filter based on a specific user account.
IPTables itself doesn’t really deal with routing packets to interfaces, so we can’t use it to directly route packets. We can however mark packets from the user so they can be routed by the ip routing table. I’ve created a script to flush and apply firewall rules, which does what we need ( obviously set the variables at the beginning of the script to match your details ):
#! /bin/bash export INTERFACE="tun0" export VPNUSER="vpnuser" export LANIP="192.168.1.0/24" export NETIF="br0" iptables -F -t nat iptables -F -t mangle iptables -F -t filter # mark packets from $VPNUSER iptables -t mangle -A OUTPUT ! --dest $LANIP -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 iptables -t mangle -A OUTPUT --dest $LANIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 iptables -t mangle -A OUTPUT --dest $LANIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j MARK --set-mark 0x1 iptables -t mangle -A OUTPUT ! --src $LANIP -j MARK --set-mark 0x1 # allow responses iptables -A INPUT -i $INTERFACE -m conntrack --ctstate ESTABLISHED -j ACCEPT # allow bittorrent iptables -A INPUT -i $INTERFACE -p tcp --dport 59560 -j ACCEPT iptables -A INPUT -i $INTERFACE -p tcp --dport 6443 -j ACCEPT iptables -A INPUT -i $INTERFACE -p udp --dport 8881 -j ACCEPT iptables -A INPUT -i $INTERFACE -p udp --dport 7881 -j ACCEPT # block everything incoming on $INTERFACE iptables -A INPUT -i $INTERFACE -j REJECT # send DNS to google for $VPNUSER iptables -t nat -A OUTPUT --dest $LANIP -p udp --dport 53 -m owner --uid-owner $VPNUSER -j DNAT --to-destination 8.8.4.4 iptables -t nat -A OUTPUT --dest $LANIP -p tcp --dport 53 -m owner --uid-owner $VPNUSER -j DNAT --to-destination 8.8.8.8 # let $VPNUSER access lo and $INTERFACE iptables -A OUTPUT -o lo -m owner --uid-owner $VPNUSER -j ACCEPT iptables -A OUTPUT -o $INTERFACE -m owner --uid-owner $VPNUSER -j ACCEPT # all packets on $INTERFACE needs to be masqueraded iptables -t nat -A POSTROUTING -o $INTERFACE -j MASQUERADE # reject connections from predator ip going over $NETIF iptables -A OUTPUT ! --src $LANIP -o $NETIF -j REJECT
Now all packets from the user will be marked for the VPN. We also need to add a routing table, by adding the table name to the rt_tables file. In Gentoo this file is in /etc/iproute2/rt_tables , other distros will have it in different places, on my machine the file looks like this, the last line being the one added:
# # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep 200 vpnuser
Next we need a script to configure the routing rules for the marked packets:
#! /bin/bash VPNIF="tun0" VPNUSER="vpnuser" GATEWAYIP=`ifconfig $VPNIF | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}' | egrep -v '255|(127\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})' | tail -n1` if [[ `ip rule list | grep -c 0x1` == 0 ]]; then ip rule add from all fwmark 0x1 lookup $VPNUSER fi ip route replace default via $GATEWAYIP table $VPNUSER ip route append default via 127.0.0.1 dev lo table $VPNUSER ip route flush cache
If you are using OpenVPN, you will need to ensure this line is in your config file, to prevent all traffic from sending over the VPN by default:
route-nopull
You may also need to add these lines into /etc/sysctl.d/9999-vpn.conf to ensure the kernel lets the traffic get routed correctly ( this disables reverse path filtering ):
net.ipv4.conf.all.rp_filter = 0 net.ipv4.conf.default.rp_filter = 0 net.ipv4.conf.br0.rp_filter = 0
Then run:
sysctl -p
To apply the new sysctl rules. You may also need to restart your VPN if you are already connected.
Now run the two scripts ( the second script needs to run when the network interface starts – this is in /etc/conf.d/net on Gentoo, or the ‘up’ command in OpenVPN’s config file ) , and the specific user should only be able to access traffic on the VPN, and other users on the system should access the network as normal.