3

With a wireguard configuration provided from a VPN company:

How do you modify iptables and wg0.conf to route only traffic from the user vpn trough wireguard's interface wg0, leaving all other traffic untouched?

reactions to comments and answers

When running suggested commands (by Hauke Laging) as PostUp script in wg0.conf results in the user sending traffic trough the wg0 interface but still unable to reach internet, why?

#!/bin/sh
# up.sh

iptables -t mangle -nvL OUTPUT | grep -q 0x2a ||
    iptables -t mangle -A OUTPUT -m owner --uid-owner test -j MARK --set-mark 42

grep -q '^42  vpn$' /etc/iproute2/rt_tables ||
    echo '42  vpn' >>/etc/iproute2/rt_tables

ip route show table vpn | grep -q default ||
    ip route add default via 10.66.95.98 dev wg0 table vpn

ip rule | grep -q 0x2a ||
    ip rule add fwmark 42 lookup vpn prio 42

The configuration wg0.conf currently looks the following:

[Interface]
PrivateKey = <Hidden>
Address = 10.66.95.98/32,fc00:bbbb:bbbb:bb01::3:5f61/128
DNS = <DNS>
Table = off
PostUp = up.sh

#Following 2 lines added in attempt to allow local traffic 
PreUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
PreDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE

[Peer]
PublicKey = <Hidden>
AllowedIPs = 0.0.0.0/0,::0/0
Endpoint = 185.65.135.224:51820

ip route returns following output:

default via 192.168.1.1 dev enp2s0 proto dhcp metric 100 
169.254.0.0/16 dev enp2s0 scope link metric 1000 
192.168.1.0/24 dev enp2s0 proto kernel scope link src 192.168.1.2 metric 100 

ip route show table vpn returns output

default via 10.66.95.98 dev wg0 

ip rule returns

0:  from all lookup local
42: from all fwmark 0x2a lookup vpn
32766:  from all lookup main
32767:  from all lookup default

iptables -t nvL returns

Chain PREROUTING (policy ACCEPT 5465 packets, 1114K bytes)
 pkts bytes target     prot opt in     out     source               destination         
 2829  671K CONNMARK   udp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* wg-quick(8) rule for wg0 */ CONNMARK restore

Chain INPUT (policy ACCEPT 5450 packets, 1113K bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 5786 packets, 1203K bytes)
 pkts bytes target     prot opt in     out     source               destination         
  961 1123K RETURN     all  --  *      *       0.0.0.0/0            192.168.1.0/24       owner UID match 1002
 261M  414G MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1002 MARK set 0x2a
  156 56019 RETURN     all  --  *      *       0.0.0.0/0            192.168.1.0/24       owner UID match 1002
 261M  414G MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1002 MARK set 0x2a
   77 48572 MARK       all  --  *      *       0.0.0.0/0            0.0.0.0/0            owner UID match 1002 MARK set 0x2a

Chain POSTROUTING (policy ACCEPT 6507 packets, 1310K bytes)
 pkts bytes target     prot opt in     out     source               destination         
 1281  209K CONNMARK   udp  --  *      *       0.0.0.0/0            0.0.0.0/0            mark match 0xca6c /* wg-quick(8) rule for wg0 */ CONNMARK save

When running tcpdump -i wg0 -n & ping google.se following returns:

17:48:43.496475 IP 192.168.1.2.33044 > 185.65.135.224.51820: UDP, length 1184

indicating that packets indeed reach the wg0 interface. However, the ping doesn't yield any results.

110 packets transmitted, 0 received, 100% packet loss, time 111603ms
2
  • You might be able to find a solution by looking into policy based routing.
    – Pedro
    May 28, 2020 at 16:50
  • A simpler way would be to create a network namespace, put the wireguard network interface into that namespace, and then put all processes of user vpn also into that namespace (for example, if he logs on). That has the advantage that this also works for multiple users, on the fly, or even for one user wanting to choose between those per application.
    – dirkt
    Jun 2, 2020 at 17:14

1 Answer 1

3

You can do that by

  1. using iptable's module owner to detect those packets
  2. using iptable's target MARK to set the packet mark to any not yet used value
  3. create an additional routing table which sends all traffic through the VPN interface
  4. using ip rule policy routing to select the special routing table for the marked packets
iptables -t mangle -nvL OUTPUT | grep -q 0x2a ||
    iptables -t mangle -A OUTPUT -m owner --uid-owner test -j MARK --set-mark 42

grep -q '^42  vpn$' /etc/iproute2/rt_tables ||
    echo '42  vpn' >>/etc/iproute2/rt_tables

ip route show table vpn | grep -q default ||
    ip route add default via 10.66.95.98 dev wg0 table vpn

ip rule | grep -q 0x2a ||
    ip rule add fwmark 42 lookup vpn prio 42
8
  • When using the suggested as a upscript in wg0.conf with "PostUp" results in all users going over vpn except the user "vpn" which dosent have internet connection at all. #!/bin/sh iptables -t mangle -A OUTPUT -m owner --uid-owner vpn -j MARK --set-mark 42 echo '42 vpn' >>/etc/iproute2/rt_tables ip route add default via $(ifconfig -a wg0 | grep "inet " | grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" | head -n 1) dev wg0 table vpn ip rule add fwmark 42 lookup vpn prio 42
    – Maximus
    May 31, 2020 at 14:27
  • 1
    @Maximus That is unreadable. Put so much code not into a comment, add it to your question instead (like I have done now). The echo '42 test' must be executed once only as it changes a file. Doesn't make sense to put it in a script. though. Please add the output of ip route, ip route show table vpn, ip rule, and iptables -t mangle -nvL to your question. May 31, 2020 at 22:47
  • Hello again. I've yet to understand the flow of the platform. Thanks for your patience & tips. The requested command output have now been added to the question.
    – Maximus
    Jun 1, 2020 at 14:12
  • I belive this does not work since wireguard will automatically start sending all the computers traffic trough wg0. Somehow all other traffic must be stopped and only make sure to only let marked traffic trough, which does work with the suggested code.
    – Maximus
    Jun 1, 2020 at 18:20
  • 1
    @Maximus I must admit I do not see the problem. Except for connections to 192.168.1.0/24 because those packets do not get the necessary mark due to the first rule in OUTPUT. I assume the UID of user vpn is 1002. You can try to understand the problem better by checking with tcpdump -i wg0 -n whether the packets of user vpn reach the VPN interface. And you can check whether the packets of a different user get marked: iptables -t mangle -A OUTPUT -m owner --uid-owner otheruser -m mark --mark 0 (must be at the end of the chain; doesn't do anything besides counting packets). Jun 2, 2020 at 0:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.