我偶然发现了一个奇怪的问题:
我在家庭网络上的 Ubuntu 16.04 上设置了一个 OpenVPN 服务器,以使我的 Android 手机和基于 Debian 的笔记本电脑能够通过我的家庭网络发送所有互联网流量。为此,我配置了一个 iptables MASQUERADE 规则来欺骗我的 OpenVPN 客户端 tun0 隧道接口的源 IP 地址。从最终用户的角度来看,这一切都运行良好。但是,我的路由器/防火墙有时会抱怨“火星源”数据包,其源 IP 地址是我客户端的 tun0 接口(在本例中是 Android 手机)。我对这些信息感到困惑,想知道它们是否会造成安全风险——不太可能,因为火星数据包通常会被丢弃在路由器中。但好奇心占了上风,我进一步调查了它。长话短说,这是我发现的:
当您关闭手机(仅关闭屏幕)时,OpenVPN 客户端会软退出,从而中断通过 tun0 隧道接口到 OpenVPN 服务器的一些 TCP 连接。
当您再次打开手机时,OpenVPN 连接会自动重新启动。当发生这种情况时,Ubuntu 防火墙通常会检测到从手机到互联网 IP 地址(例如 google.com)的大量无效数据包。这些消息是 RST/ACK、FIN/ACK 和 RST 类型的 TCP 数据包,其中大多数在 iptables 防火墙连接跟踪的意义上是无效的。
现在到了令人费解的部分:iptables 防火墙很乐意将这些无效数据包转发到路由器的 Ubuntu 服务器传出以太网接口,在那里它们会导致 martian 源数据包警告,但它们的 IP 源地址是原始 android 手机 tun0 接口 IP 地址,而不是 Ubuntu 服务器的传出 IP 地址。所有非无效数据包都会得到正确的 MAAQUERADE 源 NAT 处理。
以上结论是基于使用 wireshark 捕获流量,并使用以下规则的 iptables 统计来捕获前向链中的无效数据包得出的。
我已设法使用以下规则丢弃这些数据包:
-A FORWARD -s xxx.xxx.169.0/24 -o p2p1 -p tcp -m state --state INVALID -j DROP
引入此规则后,路由器中不再有任何火星人,并且 wireshark 不再在 Ubuntu 传出以太网接口上看到来自手机的源地址的数据包。
我应该提到,在测试期间,手机没有通过 WiFi 连接到家庭局域网,所以所有流量都是 OpenVPN UDP 协议,它通过我的家庭路由器/防火墙到达 Ubuntu 服务器上的 OpenVPN 服务器。
我希望有人知道所描述的行为是否符合 Linux netfilter/iptables 的设计,或者它是一个错误?我怀疑问题与 iptables 连接跟踪有关,其中断开的 TCP 连接在 iptables 中丢失了它们的连接跟踪条目,这反过来又以某种方式阻止了 MASQUERAD 规则的源 NAT 逻辑发挥作用。
如果有人能对此提供更深入的见解,甚至还能对任何安全方面发表评论,我会很高兴——它可以以某种方式被利用吗?
答案1
你的分析很透彻,很全面。非常好。8年前我也做过同样的调查。
尽管有人不断报告说这是一个 bug,但它并不是一个 bug。根据我当时的笔记:
NAT 引擎根本不知道如何处理无效数据包,因此它无需地址转换即可通过。因此需要识别并丢弃此类数据包。这一要求在大多数文档中都不太明显。
我不知道这会带来什么特别的安全问题。我知道有些 ISP 可能会对发送不可路由的数据包感到不满。在我的 iptables 规则集中,除了 INVALID 检查外,我还设置了许多规则,以确保数据包永远不会离开我的 LAN,并且源 IP 或目标 IP 不可路由(即 192.168.XX、10.XXX、172.16.XX,...)
规则实施 2.5 年后,您已:
-A FORWARD -s xxx.xxx.169.0/24 -o p2p1 -p tcp -m state --state INVALID -j DROP
曾经有一个非 NAT 数据包从我的 LAN 中逃逸的情况。原因是来自智能手机的 ICMP 数据包格式不正确(这种情况不应该发生,但确实发生了。这是一个伪造的报头长度)。因此协议规范被删除:
-A FORWARD -s xxx.xxx.169.0/24 -o p2p1 -m state --state INVALID -j DROP