我有一台 Linux 服务器,它是 OpenVPN 端点,但也托管着一个 Web 服务器。当我的客户端连接到 Web 服务器的服务器地址时,数据包会传输到 VPN 之外。这是理所当然的,因为 OpenVPN 设置的到服务器的路由比进入 VPN 的默认路由更具体。然而,我认为这是“泄漏”。
因此我尝试设置与 Wireguard 类似的设置(Wireguard 很棒,但我需要 OpenVPN 因为它需要是 TCP)。
我的设置基于 Wireguard 页面以及其他问题: 使用 Wireguard 中的 FwMark 防止路由循环 (向在那里举办的讲座致敬!) 使用 nftables mark 将 fwmark 路由到 VPN 网关
尽管进行了设置,Wireshark 仍显示 http/https 请求仍通过物理接口而不是通过 vpn tun0 接口。当我使用 nft 监控跟踪查看数据包标记时,似乎元标记已正确设置,并且仅显示适当的数据包(往返于端口 1194)。
所以我怀疑这是:
- pbr 规则未按预期工作。
- 数据包标记发生得不够早。
我尝试改变链条,将传出的数据包标记为:
- 类型路由钩子输出
- 类型过滤器钩子输出
- --> 不再有运气
这些命令返回以下内容:
- ip rule:
0: from all lookup local
32764: from all lookup main suppress_prefixlength 0
32765: not from all fwmark 0x4 lookup vpn
32766: from all lookup main
32767: from all lookup default
- ip route show table vpn:
default dev tun0 scope link
- ip route:
default via 10.8.0.1 dev tun0 proto static metric 50
default via 192.168.1.1 dev wlp4s0 proto dhcp src 192.168.1.10 metric 600
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.2 metric 50
END.POINT.IP.ADDRESS via 192.168.1.1 dev wlp4s0 proto static metric 50
192.168.1.0/24 dev wlp4s0 proto kernel scope link src 192.168.1.10 metric 600
-nft list ruleset:
table inet vpn {
chain premangle {
type filter hook prerouting priority mangle; policy accept;
ip saddr END.POINT.IP.ADDRESS tcp sport 1194 meta nftrace set 1
meta mark set ct mark
}
chain postmangle {
type filter hook postrouting priority mangle; policy accept;
ip daddr END.POINT.IP.ADDRESS tcp dport 1194 meta nftrace set 1
ip daddr END.POINT.IP.ADDRESS tcp dport 1194 meta mark set 0x00000004
meta mark 0x00000004 ct mark set meta mark
}
}
- traceroute -n --fwmark=0x4 END.POINT.IP.ADDRESS
shows it goes via the physical interface out of the vpn (as expected)
- traceroute -n END.POINT.IP.ADDRESS
shows it goes via the physical interface out of the vpn (UNWANTED)
提前谢谢您!
答案1
如果不使用严格反向路径转发(“SRPF”),则否nftables根本就不应该被使用。
尽管路由(转发)流量通常在处理标记时工作正常iptables或者nftables,本地发起改道由于标记(在type route hook output
链中)而导致的流量通常会出现问题:在链中发生的重新路由检查type route hook output
不会神奇地改变客户端套接字上已经选择的本地源 IP 地址。通常它是错误的 IP 地址。因此,它通常需要 NAT 创可贴(在type nat hook output
),并且可能会使 UDP 处理比在多宿主环境中更加困难。使用nftables因为应尽可能避免这种情况。
就像WireGuard一样,OpenVPN可以在其封装的传出流量上充分设置防火墙标记,并且这将在本地传出流量发生任何路由查找之前发生:
--mark value
标记加密数据包正在发送带有值的标记值。标记值可以在策略路由和数据包过滤规则中匹配。这选项仅支持 Linux在其他操作系统上不执行任何操作。
其工作原理与 WireGuard 相同:在实际接口上,传出的信封数据包会得到标记,可能是通过让客户端使用SO_MARK
在连接到服务器之前在其套接字上:
SO_MARK
(自 Linux 2.6.25 起)为通过此套接字发送的每个数据包设置标记(类似于 netfilter MARK 目标,但基于套接字)。更改标记可用于不使用 netfilter 的基于标记的路由或用于数据包过滤。
当然,如果没有重新路由,也没有直接使用策略路由,包括直接标记(使用SO_MARK
或等效方法),那么很可能根本不起作用。
所以删除所有nftables规则:
nft delete table inet vpn
并在客户端配置中添加:
mark 4
保留路由规则和表(它们可能应该集成在 VPN 挂钩中):
- ip rule: 0: from all lookup local 32764: from all lookup main suppress_prefixlength 0 32765: not from all fwmark 0x4 lookup vpn 32766: from all lookup main 32767: from all lookup default
- ip route show table vpn: default dev tun0 scope link
注意:本答案末尾的部分仅适用于 SRPF 情况,应在添加上述路由表条目之前添加,以避免暂时中断。
不要通过 VPN 添加默认路由,也不要添加到远程端点的显式路由。不要让服务器推送此配置。或者让客户忽略它和:
pull-filter ignore redirect-gateway
或者:
route-nopull
为了不出现这些路线:
default via 10.8.0.1 dev tun0 proto static metric 50
END.POINT.IP.ADDRESS via 192.168.1.1 dev wlp4s0 proto static metric 50
但仅添加这一个:
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.2 metric 50
vpn
相反,策略路由规则仅在适当时才通过选择路由表来处理默认路由。
正如解释的那样我的答案对于第一个链接问答,大部分nftablesWireGuard 的规则集Table = auto
+AllowedIPs = 0.0.0.0
是处理安全防御框架协议回复流量。有以下几种情况:
-
包括
net.ipv4.conf.default.rp_filter
和net.ipv4.conf.all.rp_filter
。无 RPF 检查:无事可做。否nftables需要。 -
现在信封回复流量可能会使 SRPF 失败
在主界面上选择Loose RPF:
sysctl -w net.ipv4.conf.wlp4s0.rp_filter=2
就此了结。不nftables需要,
或者实现所有逻辑来标记返回信封流量,就像在 WireGuard 中所做的那样
让 fwmark 也用于反向路径查找
通过启用
src_valid_mark
在主接口上(也可以改为all
),从而允许 SRPF 通过:sysctl -w net.ipv4.conf.wlp4s0.src_valid_mark=1
转置(此处仅限 IPv4)WireGuard 的设置
如图所示链接问答最后还考虑了其他极端情况,因此回复流量获得了 fwmark
table ip vpn { chain preraw { type filter hook prerouting priority raw; policy accept; iifname != "tun0" ip daddr 10.8.0.2 fib saddr type != local drop } chain premangle { type filter hook prerouting priority mangle; policy accept; ct mark 4 meta mark set ct mark } chain postmangle { type filter hook postrouting priority mangle; policy accept; meta mark 4 ct mark set meta mark } }
链
preraw
是可选的,如果需要可以删除。它可以防止远程(LAN)尝试访问内部 VPN 本地地址。OpenVPN 在传出的信封数据包上创建标记,在钩子后路由时复制到连接标记中,并在钩子预路由时重新注入回复信封数据包中。任何地方均未出现端点地址或端口。
没有进行重新路由(没有,
type route hook output
也不type nat hook output
存在)。
注意:sysctl 命令和nftables在路由表中添加默认路由之前,应该执行上述两个规则集
vpn
,否则将发生暂时失去连接的情况,直到 VPN TCP 套接字恢复(仍然,只有在添加两者之后)。
客户端系统现在可以从隧道内部访问服务器。
连接测试可以像这样进行:
socat -d -d TCP4:END.POINT.IP.ADDRESS:443 -
OPtcpdump
应该通过 VPN 单跳到达 END.POINT.IP.ADDRESS。
至少在 amd64 (x86-64) 架构上,可以使用以下方式绕过 VPN(以 root 身份):
socat -d -d TCP4:END.POINT.IP.ADDRESS:443,setsockopt-listen=1:36:L4 -
其中的setsockopt-listen
意思是:SO_MARK
在连接之前使用(而不是在本例中监听)。并且中的 4L4
与 OpenVPN 使用的标记值相同。
注意:客户端通过隧道查询服务器上具有服务器公共 IP 地址的 UDP 服务的特定情况可能会遇到一个常见问题,该问题实际上与 VPN 无关,而是与使用 UDP 和多宿主有关。这要求 UDP 服务具有多宿主感知能力:通常通过使用多个 UDP 套接字,为每个本地地址绑定一次(因此通常至少每个接口一次)或使用单个未绑定的 UDP 套接字IP_PKTINFO
带有附加的处理代码。
答案2
首先非常感谢您的及时回复。
哦,好的。我明白,由于请求过程的初始路由决策已经发生,如果没有 NAT,重新路由将发送具有错误源的数据包,因此会失败。
所以我做了以下事情:
关于 rp_filter,我的发行版默认值是 2,我不会再增加复杂性。因此,按照建议,将所有这些都排除在外。我暂时将所有这些都设置为 0。
我保留 nftables 只是为了跟踪数据包并确保 openvpn 确实按照下文所述标记数据包)。
- ip rule
0: from all lookup local
32764: from all lookup main suppress_prefixlength 0 **I tried with and without that one with no effect on reaching the webserver**
32765: not from all fwmark 0x4 lookup vpn
32766: from all lookup main
32767: from all lookup default
- ip route show table vpn:
default dev tun0 scope link
- ip route
default via 192.168.1.1 dev wlp4s0 proto dhcp src 192.168.1.10 metric 600
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.2
192.168.1.0/24 dev wlp4s0 proto kernel scope link src 192.168.1.10 metric 600
- nft list ruleset (kept just for seeing if openvpn did mark the packets, and it did)
table inet vpn {
chain premangle {
type filter hook prerouting priority mangle; policy accept;
meta mark 0x00000004 meta nftrace set 1
}
chain postmangle {
type route hook output priority mangle; policy accept;
meta mark 0x00000004 meta nftrace set 1
}
}
但是现在,我无法再访问网络服务器:
traceroute -n --fwmark=0x4 END.POINT.IP.ADDRESS
Shows it goes via the physical interface and my ISP ips outside of the vpn envelope (as expected). Wireshark shows all the Icmp packets.
traceroute -n END.POINT.IP.ADDRESS
**attempts the 30 hops without success and fails to find a route. Wireshark does not show any Icmp packet on any interface**
socat -d -d TCP4:END.POINT.IP.ADDRESS:443 -
socat[56833] N opening connection to AF= ** and stalls until Ctrl-C
我ip route flush cache
以防万一。没有成功,但一切似乎都井然有序:
ip route get END.POINT.IP.ADDRESS
END.POINT.IP.ADDRESS dev tun0 table vpn src 10.8.0.2 uid 0 cache
ip route get END.POINT.IP.ADDRESS mark 4
END.POINT.IP.ADDRESS via 192.168.1.1 dev wlp4s0 src 192.168.1.10 mark 4 uid 0 cache
Wirehshark 没有显示任何与请求相关的数据包,甚至没有显示错误的地址或没有答复。
有什么可以阻止的线索吗?!如果我误解了任何一点,请原谅,坦率地说,您提出的设置对我来说很有意义。谢谢!