通过 VPN 本身将网络流量传递到 OpenVPN 信封以外的 VPN 端点

通过 VPN 本身将网络流量传递到 OpenVPN 信封以外的 VPN 端点

我有一台 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是处理安全防御框架协议回复流量。有以下几种情况:

  • rp_filter=0到处

    包括net.ipv4.conf.default.rp_filternet.ipv4.conf.all.rp_filter。无 RPF 检查:无事可做。否nftables需要。

  • rp_filter=1

    现在信封回复流量可能会使 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 没有显示任何与请求相关的数据包,甚至没有显示错误的地址或没有答复。

有什么可以阻止的线索吗?!如果我误解了任何一点,请原谅,坦率地说,您提出的设置对我来说很有意义。谢谢!

相关内容