我想通过 WireGuard VPN 路由来自特定程序的流量。我设置了 wireguard 接口、配置、路由等。为了仅为单个程序进行 VPN 路由,我使用不同的组 ( sudo -g GROUPNAME
) 运行该程序并使用
iptables -t mangle -A OUTPUT -o owner --gid-owner GROUPNAME -j MARK --set-mark MARK` to set
标记传出数据包。然后,我添加一条规则
ip add rule fwmark MARK table TABLE
这样,来自该组的数据包GROUPNAME
将使用另一个路由表进行路由TABLE
。
在TABLE
我添加的路线中:
ip add VPN_SUBNET/24 dev wg0 table TABLE
ip add VPN_SERVER_IP via INTERNET_GATEWAY table TABLE
ip add default via VPN_GATEWAY table TABLE
使用此配置,所有 ping 和请求都会失败。使用tcpdump -i wg0 -n
我发现数据包实际上是从错误的 IP 地址发送的,从我服务器的公共 IP 地址而不是我 VPN 网络中的内部 IP 地址发送的,正如我所料。为了验证这一点,我添加了iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
,以便通过 VPN 接口发送的所有错误源地址都转换为正确的 VPN 源地址。而且它有效!
为了找出为什么没有伪装的数据包会以错误的源地址发送,我尝试在默认路由表(没有 fwmark 过滤)中为试图发送请求的 IP 地址添加路由:
ip add SERVER_IP via VPN_GATEWAY
即使没有 POSTROUTING 技巧,它也能正常工作。但是当然,现在整个系统路由SERVER_IP
通过 VPN 发送的所有数据包,我不需要这样做。
所以现在我很好奇,为什么标记为使用另一个路由表的数据包似乎仍然首先使用默认路由表,设置默认源地址,然后才使用另一个路由表来获取数据包发送到目的地的网关?
有没有什么方法可以让我从该GROUPNAME
组发送的数据包立即使用正确的路由表进行路由,从而具有正确的源地址,而不需要看似不必要的 MASQUERADE 技巧?
答案1
首先解释一下您所遇到的情况以及为什么会出现这样的情况:
当应用程序生成数据包时,fwmark 尚未设置。由于应用程序未指定源 IP,内核会根据默认路由表为其分配一个,因为为其分配 fwmark 的 OUTPUT 规则尚未运行。这就是为什么它会将您的公共 IP 分配为源 IP。稍后,OUTPUT 规则只会影响 fwmark,当然不会影响源 IP。请参阅这了解内核在创建数据包时如何选择源 IP。请注意,如果您更改全局默认路由,内核将为新数据包提供另一个源 IP,正如您所体验到的,因为它所查看的是这个默认路由。
现在您可以做什么......
显然,您找到了一个解决方案,即在传出数据包上添加伪装,这是很正常的,而不是“技巧”。当然,由于您有一个源地址,因此您可以改用静态 SNAT,如下所示:
iptables -t nat -A POSTROUTING -s <YOUR_PUBILC_IP> -j SNAT --to <YOUR_VPN_IP>
如果您的“公共 IP”并非真正公开,但您无论如何都位于 NAT 后面(在您的默认表中),那么您可以保持源 IP 不变,而是在 VPN 的另一端为您的“公共 IP”(即数据包的来源)添加路由,以便知道如何再次通过隧道路由到该 IP。当然,既然您称其为公共 IP,那么在您的情况下,我假设它确实是公共的,并且不是您的选择,但为了完整性,在这里添加它,因为这是一种常见情况。
请注意这两种解决方案是如何“对称”的,在一种情况下,您在公共接口上有一个 NAT,在另一种情况下,您需要通过隧道进行 NAT。
现在,如果出于某种原因,您确实需要源 IP 从一开始就正确(在这种情况下,请编辑您的问题以提供更多详细信息,说明您认为 NAT/MASQUERADING 存在问题的原因),您必须在实际运行的程序中执行此操作。许多程序都有本机选项(例如 `nc -s <source_ip> ...>),或者如果您有权访问代码,您可以修改它以添加硬编码的源 IP,或者最好添加命令行参数。如果您无权访问代码,我现在想到的其他一些事情比简单地添加 NAT 更具“诡计”。