防火墙日志里有一行你没预料到:一个 ACK 包,conntrack 状态为 NEW,源地址不是你的,目的端口处于开放状态。很容易把它读成一条无人授权的新入站连接。这行实际说的要窄得多:当这个包到达这个 hook 时,追踪器没有匹配的状态,而这个包带的是 ACK。这是关于两件不同事情的两个事实,两者都不是一次握手的完整记录。下面是我下结论之前会走的顺序。

从包开始,不要从结论开始

在解释之前先把观察分开。TCP 标志描述的是这个包,追踪器的状态描述的是它在该 hook 处知道什么,而你的抓包只描述你录下的那段窗口。

把这三者混为一谈,就是一个 ACK 被读成“服务商把某人放进来了”的过程。

NEW 在 hook 处到底意味着什么

iptables 的 conntrack 匹配 把 NEW 记录为发起连接的包,或者与一个尚未在两个方向上都见到流量的连接相关联的包。ESTABLISHED 意味着追踪器已经在两个方向上都看到包。两个定义都没有说这个包是 SYN,也没有说它是在没有历史的情况下到达的。

从中间开始观察一条流的追踪器,会按自己已知的信息去分类下一个包,而这个信息可能什么都没有。这是关于追踪器记忆的陈述,不是关于远端主机意图的陈述。

宽松追踪解释了大多数 ACK 带来的意外

内核记录了 nf_conntrack_tcp_loose,它允许追踪器接管它没有看到开始的 TCP 连接。在该选项取默认值时,来自流中段的 ACK 可能被当作已建立的流接收。把它关掉,同一个包更可能被分类为 INVALID 而不是 NEW。两种结果描述的都是追踪器的规则,而不是应用的经历。

所以在把标签当作理论依据之前,先检查这个设置。

按这个顺序排除无趣的原因

我跟过的 NEW-on-ACK 事件大多落在这几条里,而且每一条检查起来都很便宜:

  • conntrack 表被清空,或者追踪器重启过。
  • 主机重启过,或者这条流迁移到了别的 namespace 或设备。
  • 非对称路由把 SYN 送上了你没有抓的路径。
  • NAT 改写了 tuple,所以这条表项不再对应你读到的内容。
  • 抓包开始得太晚,从来没有看到开头。

每一条都会把更早的包从追踪器的视野里移走,同时让更晚的包保持完整。重启或清表最容易用 uptime 和日志确认。非对称路由表现为单向抓包。NAT 是最阴的一条,因为追踪器存的 tuple 并不是日志里打印的那个 tuple。

在继续之前,写下你排除了哪几条,以及怎么排除的。

读 tuple、接口和 hook

conntrack 表项以地址、端口和协议为键,所以任何转换都会改变哪一条表项匹配。也要记下接口和 hook,因为出现在错误接口上、或者经过别的链之后的包,说明路径和图里画的不一样。当事件涉及 VPN 时, 判断你看的是内层包还是外层承载。然后和网关针对同一个五元组记录的内容对比。

对同一条流的两种互相矛盾的视角,比其中任何一种单独看都更有信息量。

什么时候值得升级处理

当你能拿出一个本不该可达却可达的服务,并附带这条流、放行它的策略以及两端的证据时,才升级处理。不要只凭一个状态标签或一次不完整的抓包就报告服务商缺陷,因为那份报告会被你刚读到的解释直接关掉。如果服务确实暴露了,那条边界的负责人就是该修它的人,而委托出去的端口映射或意外的监听者,比一个 bug 更可能是原因。

然后再补上让一个包看起来像整段会话的可见性缺口。

相关文章