基于WireGuard(wg-quick)的特定用户VPN分流/绕过方案咨询
你好,针对你遇到的特定用户绕过WireGuard VPN的需求,我来帮你梳理下现有尝试的问题,并给出几个可行的解决方案:
一、先说说你之前尝试的问题所在
1. nftables标记时机错误
你之前用route hook output priority mangle设置标记,这个hook的触发时机是系统完成路由决策之后,此时数据包已经被分配到wg0接口了,再设置mark自然无法改变路由方向。要让标记生效,必须把动作放在路由决策之前。
2. 路由规则缺失回程处理
你添加的ip rule add uidrange 1003-1003 lookup main确实能让该用户的出站流量走eth0,但问题是:响应包从外网回来时,系统不知道该把它转发给uid1003的进程——因为回程包没有携带用户标识,导致流量无法正确回到发起进程,所以你看不到响应。
3. 网络命名空间的DNS问题
macvlan是二层隔离的,确实无法直接访问主机命名空间的服务(比如你的本地DNS)。要解决这个问题,换用veth pair来连接命名空间和主机就行,它支持跨命名空间的网络互通。
二、推荐的可行方案
方案1:调整nftables标记时机+完善路由规则(最直接)
既然你的WireGuard用fwmark来控制路由(wg-quick默认会添加ip rule add fwmark <wg-mark> lookup <wg-table>的规则),我们只需要让uid1003的流量避开这个标记,或者直接让它们走main路由表。
具体步骤:
先确认WireGuard的fwmark和路由表:
执行ip rule show,你会看到类似这样的规则:32764: from all fwmark 0x76BA lookup 51820这里
0x76BA是WireGuard用的mark,51820是它的专用路由表。修改nftables规则,在路由决策前给uid1003的流量设置标记:
创建/修改nftables的mangle表规则:table ip mangle { chain outbound_mark { type route hook output priority -300; policy accept; # 给uid1003的流量设置一个自定义mark(比如0x100),优先级设为-300确保比WireGuard的处理更早 meta skuid 1003 meta mark set 0x100; } }加载规则:
nft -f <你的规则文件>添加路由规则,让带这个mark的流量走main表:
ip rule add fwmark 0x100 lookup main priority 100优先级设为100,确保比WireGuard的规则(通常是32764)更早生效。
处理回程流量:
为了让响应包能正确回到uid1003的进程,需要给关联的回程包打上同样的mark,在nftables里添加:table ip mangle { chain prerouting_mark { type filter hook prerouting priority -300; policy accept; # 给已建立/关联的回程包打同样的mark,确保走main路由表 ct state related,established meta mark set 0x100; } }这样一来,去程和回程流量都会走eth0,就能正常收发数据了。
方案2:改进网络命名空间方案(解决DNS问题)
如果你想要完全隔离的环境,把用户进程放到命名空间里,换用veth pair代替macvlan就能解决DNS访问问题。
具体步骤:
创建命名空间:
ip netns add no_vpn_ns创建veth pair(一对虚拟网卡,分别连接主机和命名空间):
ip link add veth_host type veth peer name veth_ns将veth_ns放到命名空间:
ip link set veth_ns netns no_vpn_ns配置主机侧veth的IP并启用:
ip addr add 192.168.100.1/24 dev veth_host ip link set veth_host up配置命名空间内的网络:
# 设置命名空间内veth的IP ip netns exec no_vpn_ns ip addr add 192.168.100.2/24 dev veth_ns # 启用网卡 ip netns exec no_vpn_ns ip link set veth_ns up # 设置默认网关为主机侧veth的IP ip netns exec no_vpn_ns ip route add default via 192.168.100.1 dev veth_ns配置主机的NAT转发,让命名空间的流量能通过eth0访问外网:
table ip nat { chain postrouting { type nat hook postrouting priority srcnat; policy accept; # 对命名空间的流量做masquerade oifname "eth0" ip saddr 192.168.100.0/24 masquerade; } }配置命名空间的DNS:
# 指向主机的IP,这样就能访问主机上的DNS服务了 ip netns exec no_vpn_ns echo "nameserver 192.168.100.1" > /etc/resolv.conf让用户no_vpn默认进入命名空间:
可以写个简单的脚本,比如:#!/bin/bash ip netns exec no_vpn_ns su - no_vpn以后运行这个脚本就能直接进入命名空间的用户环境,所有流量都会绕过VPN。
方案3:修改WireGuard配置(适合深度自定义)
如果你想完全掌控路由规则,可以在/etc/wireguard/wg0.conf里添加Table = off,这样wg-quick就不会自动添加默认路由,你可以手动添加路由规则,只让特定流量走VPN,其他流量走默认的eth0。不过这个方法需要重新规划路由,适合有一定网络基础的用户。
总结
如果只是简单实现特定用户分流,方案1最直接,改动小见效快;如果需要完全隔离的环境,方案2更可靠,解决DNS问题后体验很好。
备注:内容来源于stack exchange,提问作者uhsl_m

