snail007 / goproxy

🔥 Proxy is a high performance HTTP(S) proxies, SOCKS5 proxies,WEBSOCKET, TCP, UDP proxy server implemented by golang. Now, it supports chain-style proxies,nat forwarding in different lan,TCP/UDP port forwarding, SSH forwarding.Proxy是golang实现的高性能http,https,websocket,tcp,socks5代理服务器,支持内网穿透,链式代理,通讯加密,智能HTTP,SOCKS5代理,黑白名单,限速,限流量,限连接数,跨平台,KCP支持,认证API。
https://snail007.host900.com/goproxy/manual/zh/
GNU General Public License v3.0
15.84k stars 3.01k forks source link

Goproxy on Linux does not pick up application-based split tunneling from VPN #507

Open JubilantJerry opened 2 years ago

JubilantJerry commented 2 years ago

When I use Astrill VPN and make it only tunnel traffic from the proxy binary using the "Tunnel only these apps" feature, Goproxy still does not use the VPN tunnel.

It doesn't look like a problem with Astrill, since other SOCKS5 server applications I've tested (such as dante-server and https://github.com/ssrlive/s5proxy) all correctly start using the VPN tunnel when Astrill is set up like this. Browsers also start using the VPN if they are in the "Tunnel only these apps" list. The only application I've discovered to date that does not pick up the appropriate network settings is Goproxy. I'm pretty sure other VPN applications with application-based split tunneling will reproduce the same problem, but I don't have other VPN services to test with.

Expected Behavior

When "Tunnel only these apps" for a VPN client includes the proxy process, Goproxy should start using the VPN tunnel for all network traffic, even if the VPN is started after the proxy process.

Current Behavior

proxy directly accesses the network interface (Wi-Fi in my case) without using the tunnel. Clients connected to the Goproxy server behave as if none of their traffic is tunneled.

Possible Reason

I am guessing that application-based split tunneling works by changing the routing tables to make the highest priority default gateway use the tunnel interface (usually tun0). Then the tunnel re-routes the packets to one of two paths depending on which application made the request. This could only mean that Goproxy is accessing the network stack in an uncommon way that somehow causes it to bypass the VPN tunnel gateway.

This is what I saw with checking my routing tables:

Before VPN:
$ ip rule list
0:  from all lookup local
32766:  from all lookup main
32767:  from all lookup default

After VPN:
$ ip rule list
0:  from all lookup local
32765:  from 198.18.68.78 lookup 111
32766:  from all lookup main
32767:  from all lookup default

$ ip route show table 111
default via 198.18.120.1 dev tun0

$ ip route show table all
default via 198.18.120.1 dev tun0 table 111 
default via 192.168.3.1 dev wlp60s0 proto dhcp metric 600
[...] 

If I run sudo find /proc/* -type f | grep dev_snmp6 | grep tun0 I find all sorts of application PID's, including ones that aren't supposed to use VPN, meaning that everything is supposed to be tunneled. But I don't see the Goproxy PID (on any network interface for that matter). I don't see any child processes spawned by Goproxy, so all I can say is that though I don't understand how Goproxy uses the network stack it looks like some non-standard practice is used.

Steps to Reproduce

Pretty sure the same applies for any VPN provider, here's how it happens for me:

  1. Start Astrill VPN
  2. Select StealthVPN on the top right (same problem also happens with WireGuard)
  3. Select Application Filter on the top left
  4. Change the dropdown to "Tunnel only these apps"
  5. Press the plus sign, and select the proxy binary from Goproxy
  6. Connect to Astrill VPN
  7. Start proxy
  8. Make a client connect to the SOCKS5 server, and use it to connect to the network to check external IP / connectivity

Context (Environment)

  1. proxy version is : v12.2
  2. full command is : ./proxy @config.txt config.txt contains:
    socks 
    -p :9701
    --forever  
  3. system is : Ubuntu 20.04 LTS, Linux kernel 5.4.0-131-generic, Dell Inspiron-7577
  4. relevant logs (connection to Google when blocked, behaving as if no VPN is used):
    Oct 26 13:51:51 jubilantjerry-Inspiron-7577 proxy[15726]: 2022/10/26 13:51:51.366692 WARN get out conn fail,dial tcp 74.86.12.172:443: i/o timeout
    Oct 26 13:51:57 jubilantjerry-Inspiron-7577 proxy[15726]: 2022/10/26 13:51:57.167818 WARN get out conn fail,dial tcp 142.250.217.196:443: i/o timeout
    Oct 26 13:52:02 jubilantjerry-Inspiron-7577 proxy[15726]: 2022/10/26 13:52:02.376417 WARN get out conn fail,dial tcp 142.250.217.196:443: i/o timeout
    Oct 26 13:52:07 jubilantjerry-Inspiron-7577 proxy[15726]: 2022/10/26 13:52:07.596367 WARN get out conn fail,dial tcp 142.250.217.196:443: i/o timeout
JubilantJerry commented 2 years ago

Interestingly, the same problem happens for Gost, so it's not just Goproxy: https://github.com/ginuerzh/gost/issues/897 https://github.com/go-gost/gost/issues/121

Both use Go, so perhaps it is a problem related to a Go library?