StrongSwan 5.9.1(Debian Bullseye):来自 IPsec 隧道的流量通过以太网转发,但不通过网桥转发

StrongSwan 5.9.1(Debian Bullseye):来自 IPsec 隧道的流量通过以太网转发,但不通过网桥转发

出现以下问题:

我有一台服务器,除了执行与当前问题无关的其他任务外,还应充当 IPsec 网关。当我使用指向“网络”的以太网接口来处理 IPsec 流量并发送和接收任何转发的流量时,一切都运行正常:流量通过 IPsec 隧道发送,到达服务器,然后解密并发送到其预期目的地,并且所有回复都通过 IPsec 隧道接收、加密和传递。

但是,一旦我设置了以太网桥,将上述以太网卡连接到它(我在桥上复制了它的 MAC 地址,否则所有出站流量将被服务器连接到的路由器丢弃)并重新映射所有内容(/etc/网络/接口和相关iptables语句),然后尝试使用服务器作为网关,没有转发任何内容。通过进行一些检查,我发现网关本身可以通过 IPsec 访问,其他主机可以从网关访问。在前一种情况下,客户端和网关之间没有列出其他站点,因此在执行 时它们肯定正在通过隧道(否则中间会有更多的跳数)traceroute,而在后一种情况下,执行 时traceroute显示数据包照常离开盒子。

当在两者上放置 TRACE 时伊彭卡普icmp双方-t raw 预路由-t 原始输出,当 IPsec 隧道启动时,只有出站数据包(发往客户端虚拟 IP 和接收方公共 IP 地址的数据包,后者是防火墙)会显示在 中xtables-monitor;入站数据包甚至不会被注意到。此外,网关上相关防火墙规则的计数器不断增加在整个规则集中但是,似乎没有发送任何内容(我甚至在ebtables过滤器中的 OUTPUT 链上记录了内容以验证 SNAT 正在做什么,并且一切看起来都很好:源 IP 地址已映射到网桥的 IP 地址,目标 IP 地址是它应该是的,并且源 MAC 地址设置为我在网桥上重复的地址。就转发的流量而言,从未收到任何响应,并且traceroute从我的 IPsec 客户端(我现在正在使用的那个)执行仅会引发网关的响应。除此之外的所有其他站都无法响应,这反过来告诉我服务器连接到的路由器正在丢弃转发的数据包。

关于如何修复此问题有什么想法吗?

相关的 netfilter 规则 (iptables) - 类似于 IPv6:

# Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP

# Additional chains
/usr/sbin/iptables -N block_ext_in    # Blocks unsolicited inbound traffic (Bogons, Spamhaus's DROP/EDROP)
/usr/sbin/iptables -N block_ext_out   # Blocks undesired outbound traffic (Bogons, Spamhaus's DROP/EDROP)
/usr/sbin/iptables -N zone_ext_in     # Accepts certain inbound traffic
/usr/sbin/iptables -N zone_ext_out    # Permits certain outbound traffic (currently empty)
/usr/sbin/iptables -N ipsec_in        # Handles inbound IPsec traffic
/usr/sbin/iptables -N drop_icmp       # Kills deprecated ICMP messages

iptables -t raw -A PREROUTING -p ipencap -j TRACE
iptables -t raw -A PREROUTING -p icmp -j TRACE
iptables -t raw -A OUTPUT -p ipencap -j TRACE
iptables -t raw -A OUTPUT -p icmp -j TRACE
iptables -t mangle -A PREROUTING -p tcp -s <virtual-ip-range>/24 -m policy --pol ipsec --dir in -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
iptables -t mangle -A POSTROUTING -p tcp -d <virtual-ip-range>/24 -m policy --pol ipsec --dir out -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j tCPMSS --set-mss 1360

iptables -A INPUT -i br0 -m policy --pol ipsec --dir in -j ipsec_in
iptables -A INPUT -i br0 -s <virtual-ip-range>/24 -m policy --pol ipsec --dir in -j ACCEPT
iptables -A INPUT -p tcp -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -p tcp -m tcp ! --tcp-flags SYN,ACK,FIN,RST SYN -m conntrack --ctstate NEW -j DROP
iptables -A INPUT -p tcp -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m tcpmss ! --mss 536:65535 -j DROP
iptables -A INPUT -i br0 -p icmp -j drop_icmp
iptables -A INPUT -i br0 -s <server-vip> -j ACCEPT
iptables -A INPUT -i eth0 -m policy --pol none --dir in -j block_ext_in
iptables -A INPUT -i br0 -p udp --dport isakmp -j ACCEPT
iptables -A INPUT -i br0 -p udp --dport 4500 -j ACCEPT
iptables -A INPUT -i br0 -p esp -j ACCEPT
iptables -A INPUT -p icmp -m conntrack --ctdir REPLY -j ACCEPT
iptables -A INPUT -i br0 -p icmp --icmp-type 8 -j ICMP_ECHO
iptables -A INPUT -i br0 -p icmp --icmp-type 3/4 -j ACCEPT
iptables -A INPUT -i br0 -j zone_ext_in
iptables -A INPUT -p icmp -j DROP
iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable

iptables -A OUTPUT -d <virtual-ip-range>/24 -m policy --pol ipsec --dir out -j ACCEPT
iptables -A OUTPUT -o br0 ! -p icmp -j block_ext_out
# Safeguards against transmitting unencrypted traffic!
iptables -A OUTPUT -o br0 -d 10.0.0.0/8 -m policy --pol none --dir out -j REJECT --reject-with icmp-net-unreachable
iptables -A OUTPUT -o br0 -d 172.16.0.0/12 -m policy --pol none --dir out -j REJECT --reject-with icmp-net-unreachable
iptables -A OUTPUT -o br0 -d 192.168.0.0/16 -m policy --pol none --dir out -j REJECT --reject-with icmp-net-unreachable
iptables -A OUTPUT -o eth0 ! -p icmp -j zone_ext_out

iptables -A FORWARD -o br0 -s <virtual-ip-range>/24 -m policy --pol ipsec --dir in -j ACCEPT
iptables -A FORWARD -p tcp -m conntrack --ctstate INVALID -j DROP
iptables -A FORWARD -m physdev --physdev-is-bridged -j ACCEPT

iptables -A FORWARD -i br0 -p tcp -m physdev --physdev-in eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i br0 -p icmp -m physdev --physdev-in eth0 -m conntrack --ctdir REPLY -j ACCEPT
iptables -A FORWARD -i br0 -p udp -m physdev --physdev-in eth0 -m conntrack --ctdir REPLY -j ACCEPT
# Safeguards against forwarding unencrypted traffic!
iptables -A FORWARD -o br0 -d 10.0.0.0/8 -m policy --pol none --dir out -j REJECT --reject-with icmp-net-unreachable
iptables -A FORWARD -o br0 -d 172.16.0.0/12 -m policy --pol none --dir out -j REJECT --reject-with icmp-net-unreachable
iptables -A FORWARD -o br0 -d 192.168.0.0/16 -m policy --pol none --dir out -j REJECT --reject-with icmp-net-unreachable
iptables -A FORWARD -j REJECT --reject-with icmp-net-unreachable

iptables -t nat -A POSTROUTING -s <server-ip> -j ACCEPT
iptables -t nat -A POSTROUTING -m policy --pol ipsec --dir out -j ACCEPT
iptables -t nat -A POSTROUTING -o br0 -j SNAT --to-source <server-ip> --persistent

/etc/网络/接口

allow-hotplug eth0
iface eth0 inet manual

auto br0
iface br0 inet dhcp
      hwaddress <mac-cloned-from-eth0>
      bridge_ports eth0
      bridge_stp off
      bridge_fd 0
      bridge_maxwait 0

iface br0 inet static
      address <server-virtual-ipv4>/24

iface br0 inet6 static
      address <public-ipv6>/128
      gateway fe80::1

iface br0 inet6 static
      address <server-virtual-ipv6>/64

StrongSwan 添加的规则(首先显示在各自的链中):

Chain FORWARD (policy DROP)
target     prot opt source               destination         
ACCEPT     all  --  <client-virtual-ipv4>         anywhere             policy match dir in pol ipsec reqid 3 proto esp
ACCEPT     all  --  anywhere             <client-virtual-ipv4>         policy match dir out pol ipsec reqid 3 proto esp

Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     ipencap--  <recipient-ip>           <public-ipv4>            policy match dir in pol ipsec reqid 3 proto esp

更新

由于这种行为在 Debian Bookworm 中并没有消失,我做了更多的挖掘,在设置以下日志规则后,我发现了以下情况(新服务器,因此请注意桥接端口的标识符已从 更改为eth0enp2s0

ebtables -t nat -A PREROUTING -p ip --ip-proto icmp -i enp2s0 --log-level warning --log-prefix "VPN-inbound: " -j CONTINUE
ebtables -t nat -A POSTROUTING -p ip --ip-proto icmp -o enp2s0 --log-level warning --log-prefix "VPN-outbound: " -j CONTINUE
ebtables -A INPUT -p ip --ip-proto icmp -i enp2s0 --log-level warning --log-prefix "VPN-reply: " -j CONTINUE
ebtables -A OUTPUT -p ip --ip-proto icmp -o enp2s0 --log-level warning --log-prefix "VPN-fwd: " -j CONTINUE
ebtables -A FORWARD -p ip --ip-proto icmp -i enp2s0 --log-level warning --log-prefix "VPN-bridged: " -j CONTINUE
iptables -I INPUT 1 -i br0 -p icmp -d 172.16.1.0/24 -j LOG --log-level warning --log-prefix "VPN-recv: "

然后,为了测试目的,我启动了 VPN 并尝试 ping www.google.de(一些外部服务器,以便强制输出数据包),并且像以前一样,我没有得到回复。

感谢我设置的日志规则,我发现了以下内容(示例来自/var/log/kern.log):

2023-12-23T13:17:31.783622+01:00 h3013014 kernel: [48204.632320] VPN-fwd:  IN= OUT=enp2s0 MAC source = xx:xx:xx:xx:xx:xx MAC dest = yy:yy:yy:yy:yy:yy proto = 0x
2023-12-23T13:17:31.783713+01:00 h3013014 kernel: [48204.642733] VPN-outbound:  IN= OUT=enp2s0 MAC source = xx:xx:xx:xx:xx:xx MAC dest = yy:yy:yy:yy:yy:yy proto
2023-12-23T13:17:31.808457+01:00 h3013014 kernel: [48204.663290] VPN-inbound:  IN=enp2s0 OUT= MAC source = zz:zz:zz:zz:zz:zz MAC dest = xx:xx:xx:xx:xx:xx proto

按照ebtable这个输出路由后规则因留下数据包而受到打击(正如预期的那样)预路由规则(因此实际上收到了回复),但随后事情开始变得混乱:ebtables的 INPUT 和 FORWARD 规则均未命中(前者是预期的,但后者是用于检查误入歧途的数据包),iptables的 INPUT 规则也未命中。这意味着在做出桥接决策之前的所有事情都已考虑在内,但除此之外,事情开始变得混乱。

由于我有桥接代码,因此也可以调用ip(6)tables(我已经设置了两者/proc/sys/net/bridge/bridge-nf-call-iptables/proc/sys/net/bridge/bridge-nf-call-ip6tables启用)我决定禁用它们。

echo 0 > /proc/sys/net/bridge/bridge-nf-call-iptables
echo 0 > /proc/sys/net/bridge/bridge-nf-call-ip6tables

随后对外部服务器进行 ping 操作最终显示了以下情况:

thorin@zanarkand:~$ ping www.google.de
PING www.google.de (142.250.185.227) 56(84) bytes of data.
64 bytes from fra16s53-in-f3.1e100.net (142.250.185.227): icmp_seq=1 ttl=57 time=65.3 ms
64 bytes from fra16s53-in-f3.1e100.net (142.250.185.227): icmp_seq=2 ttl=57 time=65.1 ms
64 bytes from fra16s53-in-f3.1e100.net (142.250.185.227): icmp_seq=3 ttl=57 time=89.8 ms
^C
--- www.google.de ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 65.107/73.397/89.763/11.572 ms

这反过来告诉我,当对需要桥接数据包的 IPsec 流量做出桥接决定时,出现了严重错误ip(6)tables。设置以下规则iptables也会命中并生成日志条目:

iptables -t nat -A PREROUTING -i br0 -p icmp -d 172.16.1.0/24 -m physdev --physdev-in enp2s0 -j LOG --log-level warning --log-prefix "VPN-inbound2: "

然而,由于这条规则被击中,ebtables输入规则不是,并且根据这张图片是在维基百科上找到的唯一iptables预路由链和ebtables输入chain 是桥接决策。由于两条链都是空的,因此它们不会以任何方式篡改任何传入数据包。我能想到的唯一问题是,出站 IPsec 流量在离开盒子之前会进行 NAT,并且在处理任何回复时可能会出错。
有趣的是,只有 IPsec 流量受到不利影响,并且只有当ip(6)tables被调用时才会受到不利影响。任何其他路由的出站流量即使经过 NAT 也会正常处理。

相关内容