4

Summary

I use a VPN to route all of my traffic through. Until recently, I used OpenVPN, but then switched to Wireguard. Unfortunately, this broke my redsocks setup, and I can't figure out why.

I'm using redsocks because I'm using a command-line program that doesn't support using a SOCKS proxy - but I need that program to go through a SOCKS SSH tunnel.

Setup

Overview:

no-socks-program -> iptables nat -> redsocks -> ssh -> destination
  1. The non-socks-capable program is launched with group 1001
  2. Iptables matches on the group and REDIRECTs to port 1533 where redsocks is listening
  3. Redsocks socksifies and redirects to 1533 where SSH is listening
  4. SSH uses DynamicForward to forward to target server

Details:

  1. Match group 1001 and internal destinations (I launch the program I want to socksify with this group, and the destinations are in 10/8):
    iptables -t nat -A OUTPUT -d 10.0.0.0/8 -m owner --gid-owner 1001 -j REDSOCKS
  1. Then in the redsocks chain, filter out the usual stuff and 10.2/16 because that's where the wireguard gateway is, then redirect to port 59375, where redsocks is listening:
    iptables -t nat -A REDSOCKS -d 0.0.0.0/8 -j RETURN
    iptables -t nat -A REDSOCKS -d 100.64.0.0/10 -j RETURN
    iptables -t nat -A REDSOCKS -d 127.0.0.0/8 -j RETURN
    iptables -t nat -A REDSOCKS -d 169.254.0.0/16 -j RETURN
    iptables -t nat -A REDSOCKS -d 172.16.0.0/12 -j RETURN
    iptables -t nat -A REDSOCKS -d 192.168.0.0/16 -j RETURN
    iptables -t nat -A REDSOCKS -d 198.18.0.0/15 -j RETURN
    iptables -t nat -A REDSOCKS -d 224.0.0.0/4 -j RETURN
    iptables -t nat -A REDSOCKS -d 240.0.0.0/4 -j RETURN
    iptables -t nat -A REDSOCKS -d 10.2.0.0/16 -j RETURN
    iptables -t nat -A REDSOCKS -p tcp -j REDIRECT --to-ports 59375
  1. Redsocks config, listens on 59375, redirects to 1533, where SSH listens:
    redsocks {
        local_ip = 127.0.0.1;
        local_port = 59375;
        ip = 127.0.0.1;
        port = 1533;
        type = socks5;
    }
  1. SSH config for listening on 1533:
    Host socksify
        User me
        Hostname intermediate
        DynamicForward 1533

And this works when using OpenVPN, but it breaks when using Wireguard, and I'm too stupid to figure out why.

The only difference I can make out between the two is the routing and the kernel parameter net.ipv4.conf.all.src_valid_mark which is set to 1 by wg-quick but not by OpenVPN.

Routing for Wireguard (redsocks not working):

> sudo ip rule
0:      from all lookup local
32764:  from all lookup main suppress_prefixlength 0
32765:  not from all fwmark 0xca6c lookup 51820
32766:  from all lookup main
32767:  from all lookup default

> sudo ip route show table all
default dev wg0 table 51820 scope link
default via 192.168.1.1 dev wlp3s0 proto dhcp metric 600
192.168.1.0/24 dev wlp3s0 proto kernel scope link src 192.168.1.205 metric 600
local 10.2.0.2 dev wg0 table local proto kernel scope host src 10.2.0.2
local 127.0.0.0/8 dev lo table local proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo table local proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo table local proto kernel scope link src 127.0.0.1
local 192.168.1.205 dev wlp3s0 table local proto kernel scope host src 192.168.1.205
broadcast 192.168.1.255 dev wlp3s0 table local proto kernel scope link src 192.168.1.205

> ip route get 10.89.2.21
10.89.2.21 dev wg0 table 51820 src 10.2.0.2 uid 1000
    cache

Routing for OpenVPN (redsocks is working):

> sudo ip rule
0:      from all lookup local
32766:  from all lookup main
32767:  from all lookup default

> sudo ip route show table all
0.0.0.0/1 via 10.30.0.1 dev tun0
default via 192.168.1.1 dev wlp3s0 proto dhcp metric 600
10.30.0.0/16 dev tun0 proto kernel scope link src 10.30.0.14
128.0.0.0/1 via 10.30.0.1 dev tun0
192.168.1.0/24 dev wlp3s0 proto kernel scope link src 192.168.1.205 metric 600
local 10.30.0.14 dev tun0 table local proto kernel scope host src 10.30.0.14
broadcast 10.30.255.255 dev tun0 table local proto kernel scope link src 10.30.0.14
local 127.0.0.0/8 dev lo table local proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo table local proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo table local proto kernel scope link src 127.0.0.1
local 192.168.1.205 dev wlp3s0 table local proto kernel scope host src 192.168.1.205
broadcast 192.168.1.255 dev wlp3s0 table local proto kernel scope link src 192.168.1.205

> ip route get 10.89.2.21
10.89.2.21 via 10.39.0.1 dev tun0 src 10.39.0.13 uid 1000
    cache

Troubleshooting steps taken

  1. I tried replacing redsocks with tun2socks from badvpn, but it's the exact same problem. So it isn't redsocks who is broken.
  2. I turned on iptables tracing like this:

sudo iptables -t raw -A OUTPUT -d 10.89.0.0/16 -p tcp -m tcp --dport 443 -j TRACE

and I see SYN, SYN-ACK, ACK when using OpenVPN, but only SYN when using Wireguard. No ACK.

  1. I used Wireshark for OpenVPN and for Wireguard and compared them. In the OpenVPN traffic, I see the same thing as in the iptables trace, a normal SYN, SYN-ACK, ACK. But, and that's the interesting thing, when using Wireguard, I see SYN, RST-ACK:

OpenVPN normal TCP:

OpenVPN

Wireguard SYN then RST:

Wireguard

(10.2.0.2 is my wg0 IP, and 10.89.2.21 is my tun0 IP.)

Question

Why is redsocks working with OpenVPN and not with Wireguard, and what do I need to do to make it work?

I am happy to provide additional information.

Update

I found another difference between OpenVPN and Wireguard. I am using wg-quick to set up the Wireguard connection, and that utility uses nftables to set some rules. Here they are:

table ip wg-quick-wg0 {
        chain preraw {
                type filter hook prerouting priority raw; policy accept;
                iifname != "wg0" ip daddr 10.2.0.2 fib saddr type != local drop
        }

        chain premangle {
                type filter hook prerouting priority mangle; policy accept;
                meta l4proto udp meta mark set ct mark
        }

        chain postmangle {
                type filter hook postrouting priority mangle; policy accept;
                meta l4proto udp meta mark 0x0000ca6c ct mark set meta mark
        }
}

I have zero knowledge of nftables and I don't know what they mean, but this could very well be the problem.

Setting 0/1 route

Setting 0.0.0.0/1, 128.0.0.0/1 as AllowedIPs in Wireguard, to match the route with OpenVPN, resulted in no internet access at all. Here is the routing table with that setting:

> sudo ip route show table all
0.0.0.0/1 dev wg0 scope link
default via 192.168.1.1 dev wlp3s0 proto dhcp metric 600
128.0.0.0/1 dev wg0 scope link
192.168.1.0/24 dev wlp3s0 proto kernel scope link src 192.168.1.205 metric 600
local 10.2.0.2 dev wg0 table local proto kernel scope host src 10.2.0.2
local 127.0.0.0/8 dev lo table local proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo table local proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo table local proto kernel scope link src 127.0.0.1
local 192.168.1.205 dev wlp3s0 table local proto kernel scope host src 192.168.1.205
broadcast 192.168.1.255 dev wlp3s0 table local proto kernel scope link src 192.168.1.205

> ip route get 10.89.2.21
10.89.2.21 dev wg0 src 10.2.0.2 uid 1000
    cache
4
  • 1
    Try AllowedIPs = 0.0.0.0/1, 128.0.0.0/1 instead of AllowedIPs = 0.0.0.0/0 in your WireGuard config, to match the routing from your OpenVPN config. Commented Aug 25, 2022 at 2:52
  • @JustinLudwig Thank you for the idea, unfortunately it hasn't worked, please see the updated question
    – Patrick
    Commented Aug 25, 2022 at 7:55
  • 1
    If the remote WireGuard endpoint is not on your local network, add a route to it through your default gateway before you set AllowedIPs = 0.0.0.0/1, 128.0.0.0/1 -- eg ip route add 198.51.100.123 via 192.168.1.1 dev wlp3s0 if the endpoint's IP is 198.51.100.123. Commented Aug 25, 2022 at 22:17
  • This worked! Could you please post it as an answer so I can give you the bounty? I would very much appreciate an expanation why this solved it (other than we matched the config with OpenVPN), so I can learn for the future, if you don't mind.
    – Patrick
    Commented Aug 26, 2022 at 8:09

1 Answer 1

3
+100

Using AllowedIPs = 0.0.0.0/0 with wg-quick on Linux triggers a special case that adds special policy routing and firewall rules. These rules are designed to force all non-LAN† traffic through the WireGuard interface, while preventing routing loops. But this conflicts with your redsocks setup (which can look like the start of a loop, as it sends outbound traffic back to a local socket).

Using AllowedIPs = 0.0.0.0/1, 128.0.0.0/1 is an alternative that does not trigger this special case. It also sends all non-LAN† traffic through the WireGuard interface, but relies only on your existing routing rules to prevent WireGuard traffic from looping back through the WireGuard interface.

So, you can fix your redsocks conflict with WireGuard by replacing AllowedIPs = 0.0.0.0/0 with AllowedIPs = 0.0.0.0/1, 128.0.0.0/1 in your WireGuard config.

But if you don't have any existing routing rules that cover your remote WireGuard endpoint, you will need to add a route for it separately. If the endpoint's IP address is 198.51.100.123, and your LAN's default gateway is 192.168.1.1, connected through your wlp3s0 interface, you'd need to add a rule like this:

ip route add 198.51.100.123 via 192.168.1.1 dev wlp3s0

† Not strictly non-LAN traffic -- actually anything not specifically listed in the host's own routing tables (which usually only includes routes for LAN traffic).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .