防火墙如何在没有 RST 或 FIN 的情况下终止 TCP 连接?

网络工程 通讯协议 防火墙
2021-07-03 19:57:32

情况是这样的:

http client ----> corporate firewall ----> http server

由于保持活动,服务器和客户端将保持 TCP 连接打开,客户端将使用连接池进行 HTTP 请求。

防火墙有一个规则,可以在 1 小时后“杀死”长期存在的 TCP 连接。问题是我们的 HTTP 客户端不会检测到 TCP 连接被破坏,它试图重用基本上死的连接,在我们这边看起来像客户端在一段时间后“挂起”。一个请求会挂起,然后下一个会工作,大概是因为建立了新的连接。

这里的问题是防火墙以我们的 HTTP 客户端无法检测到的方式杀死 TCP 连接的机制什么我尝试通过以下几种方式在本地重现此行为:

  1. 杀死我们 vyos 路由器上的 TCP 连接,客户端的 Wireshark 捕获 TCP FIN-ACK。
  2. 在 Windows 上的 TCPView 中杀死 TCP 连接客户端,Wireshark 在客户端检测到 TCP RST。
  3. 与客户端防火墙建立连接后阻塞端口,导致套接字重置异常。

我在服务器端有一个 Wireshark 转储,我试图找出防火墙是否发送了 FIN 或 RST,ip.dst==serverip && (tcp.flags.reset==1 || tcp.flags.fin==1)但没有显示任何内容。

此外,客户端的 Wireshark 捕获将问题显示为 HTTP 请求发出,然后是十多次 TCP 重传,最终无处可去。

HTTP 客户端是 Java 原生和/或 Jetty HTTP 客户端(都尝试过),两者都未能检测到死 TCP 连接。我想在本地重现该行为,但我无法弄清楚防火墙以何种狡猾的方式杀死连接,因此正在寻找可能的答案。

4个回答

你没有提到防火墙的种类,但我怀疑大多数只是丢弃数据包。

我在服务器端有一个 Wireshark 转储,我试图找出防火墙是否使用 ip.dst==serverip && (tcp.flags.reset==1 || tcp.flags.fin==1) 发送 FIN 或 RST 但什么也没有出现了。

这往往会证实这一点。

很可能防火墙只是丢弃了数据包而不发送 RST 数据包,可能是在达到某种会话超时值之后。这通常是可配置的行为。

我个人更喜欢准确发送 RST 数据包,因为它可以帮助客户正常运行,但我听说过这样的争论,即不应在面向外部的防火墙上这样做,以避免向潜在攻击者提供任何类型的反馈。

我已经看到这会导致很多问题,因为客户通常不会非常优雅地处理这种情况。本质上,它们通过原始 TCP 会话(现在已失效)不断重试,而从不尝试重新建立新的会话。最终会触发客户端超时,并且用户会收到令人讨厌的错误消息。为应用程序适当地设置 HTTP keepalive 可以帮助解决这个问题。

@Ron Trunk 是完全正确的,几乎可以肯定,打开的连接正在被主动删除(插入拒绝规则)或被动删除(从已知连接中删除,并且不允许在没有同步的情况下重新创建)。其中一条评论建议您自己尝试一下。这是使用 linux 网络命名空间这样做的秘诀。它假设在您的主机内核中启用了 ip 转发,您是 root 用户,并且可能还有其他东西。

# Create network namespacs
ip netns add one; ip netns add two; ip netns add three
# Create interfaces between namespaces
ip link add dev i12 type veth peer name i21
ip link add dev i32 type veth peer name i23
# Bring interfaces up and assign them to respective namespaces
ip link set dev i12 netns one up
ip link set dev i21 netns two up
ip link set dev i32 netns three up
ip link set dev i23 netns two up
# Assign IP addresses
ip netns exec one ip addr add 1.1.1.1/24 dev i12
ip netns exec two ip addr add 1.1.1.2/24 dev i21
ip netns exec three ip addr add 3.3.3.3/24 dev i32
ip netns exec two ip addr add 3.3.3.2/24 dev i23
# Add routes when necessary
ip netns exec one ip route add default via 1.1.1.2
ip netns exec three ip route add default via 3.3.3.2

然后您需要三个窗口/外壳/屏幕/终端。在不同的终端中运行以下每个命令:

  • 开始监听服务器: ip netns exec three socat TCP-LISTEN:5001 STDIO
  • 在客户端开始传输: ip netns exec one socat STDIO TCP:3.3.3.3:5001

请注意,运行这些命令后,您在一个窗口中键入的所有内容都将反映在另一个窗口中,反之亦然(在按回车键后)。如果不是这样,您可能需要启用 ip 转发。

  • 实例化拒绝规则: ip netns exec two iptables -I FORWARD -j DROP

那么你输入的任何东西都不会被允许通过。

您可以使用(未​​经测试的)转发规则模拟不太活跃的丢弃方法,例如:

ip netns exec two iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
ip netns exec two iptables -A FORWARD -m tcp -p tcp -dport 5001 --tcp-flags ALL SYN -j ACCEPT
ip netns exec two iptables -A FORWARD -j DROP

请参阅https://unix.stackexchange.com/questions/127081/conntrack-tcp-timeout-for-state-stablished-not-workinghttps://www.kernel.org/doc/Documentation/networking/nf_conntrack-sysctl .txt以获取有关如何调整超时的信息——尽管我不清楚 iptables 本身是否支持最长连接寿命;我相信所有超时都是空闲超时。

清理与 ip netns del one; ip netns del two; ip netns del three

防火墙可以发送一个 ICMP 数据包,表明目标不可达。对于 TCP 以外的任何内容,这是唯一可能的错误指示,例如,将数据包发送到关闭的 UDP 端口将生成“目标无法访问”消息,原因代码设置为“端口无法访问”。

也可以发送“端口不可达”消息作为对 TCP 数据包的响应,这也会终止连接,但任何分析数据包转储的人都会注意到这是不寻常的,因为 TCP 约定是用 RST 指示关闭的端口。

发送方应将收到的任何 ICMP 错误数据包映射回原始连接并适当处理它们,因此防火墙生成的错误数据包也可用于终止 TCP 连接。ICMP 数据包包含违规数据包标头的副本以允许此映射。