出现以下问题:
我有一台服务器,除了执行与当前问题无关的其他任务外,还应充当 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 中并没有消失,我做了更多的挖掘,在设置以下日志规则后,我发现了以下情况(新服务器,因此请注意桥接端口的标识符已从 更改为eth0
)enp2s0
:
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 也会正常处理。