返回

Kernel 3.10 TPROXY Iptables失效:问题与解决方案

Linux

Kernel 3.10 下 TPROXY iptables 规则失效问题分析

在使用 TPROXY 模式进行流量劫持时,经常会遇到一些与内核版本相关的兼容性问题。尤其是从较新的内核迁移到旧版本(比如 Kernel 3.10)时,一些 iptables 规则可能会无法正常工作。 问题的关键在于内核模块实现以及相关钩子函数的行为可能有所差异。 这个问题涉及使用 iptablesTPROXY 来劫持进入 pod 的流量,当客户端在外部发起连接的时候,客户端收到了RST导致连接中断。本文分析可能的原因和相应的解决方案。

问题现象

在内核 5.x 版本正常运行的 iptables 规则,迁移至内核 3.10 版本后,会出现客户端发送 SYN 包,服务端回复 SYN,ACK 包,客户端再回一个 ACK 包后,连接被 RST 掉。这种情况下,客户端的连接会被立即终止, 服务不可用。 具体现象如抓包所示:连接被RST。这表明流量劫持链路中,可能存在问题导致连接状态错误。

问题分析

上述问题很可能和 TPROXY 的早期版本的一些缺陷或者行为有关。TPROXY 的工作方式是在 IP 层劫持数据包,并在用户空间处理后将其重新注入协议栈。在早期内核版本中, TPROXY 处理 SYNSYN,ACKACK 包的方式, 可能和当前最新内核的处理方式不一致。这种不一致可能导致连接状态出现混淆,从而出现 RST 。具体而言, tproxy 在 early linux kernel 3.x 版本, 会把经过的tcp连接状态从TCP_SYN_SENT状态置为TCP_CLOSE_WAIT,这使得链接无法继续正常完成握手, 所以在 client ack 到达服务端之后, 被 RST

解决方案

以下列举几种可能的解决方案,每种方案都提供了相关的 iptables 命令示例。

1. 更精细地过滤非 established 连接

原方案的过滤策略不够精确,会导致所有新建的tcp链接都被 tproxy 劫持,即包括建立链接所需的SYN 包。为了避免 SYN 包被不必要的处理,一种方法是更加精准的过滤出 非 established 的链接。例如我们可以利用 iptables 提供的连接追踪功能(conntrack) 来排除一些情况。调整 iptables 规则只针对 非 established,以及非SYN包的包才执行TPROXY跳转。

iptables -t mangle -N TSTF_PREROUTING
iptables -t mangle -A PREROUTING -j TSTF_PREROUTING
iptables -t mangle -A TSTF_PREROUTING -p tcp --tcp-flags SYN,ACK SYN,ACK -j RETURN
iptables -t mangle -A TSTF_PREROUTING  -m conntrack --ctstate NEW,RELATED,UNTRACKED  -j  RETURN
iptables -t mangle -A TSTF_PREROUTING  ! -i lo -p tcp -j TPROXY --on-port 20092 --on-ip 0.0.0.0 --tproxy-mark 0x1/0x1

ip rule add fwmark 0x1/0x1 pref 100 table 100
ip route add local default dev lo table 100

工作原理: 此方案在原方案的基础上,增加了过滤,跳过了 SYN 包,确保只有后续的数据包进入TPROXY流程。 其中 -m conntrack --ctstate NEW,RELATED,UNTRACKED 可以跳过初次 SYN 连接。

2. 修改 TPROXY mark 位

原规则中--tproxy-mark 0x1/0x1设置的mark可能在一些内核版本中有问题, 修改mark值有可能解决问题。比如修改为--tproxy-mark 0x100/0x100

iptables -t mangle -N TSTF_PREROUTING
iptables -t mangle -A PREROUTING -j TSTF_PREROUTING
iptables -t mangle -A TSTF_PREROUTING -p tcp --tcp-flags SYN,ACK SYN,ACK -j RETURN
iptables -t mangle -A TSTF_PREROUTING ! -i lo -p tcp -m conntrack ! --ctstate ESTABLISHED -j TPROXY --on-port 20092 --on-ip 0.0.0.0 --tproxy-mark 0x100/0x100

ip rule add fwmark 0x100/0x100 pref 100 table 100
ip route add local default dev lo table 100

工作原理: 重新设置 TPROXY mark 值, 一些旧版本的 linux kernel mark值会被覆盖,或者不能准确读取。 改变此mark 可以帮助避开 mark 值潜在问题。

3. 使用不同的端口监听

如果上述方案无法解决,可能和某些内核对特定端口的行为有冲突。 可以考虑更改 tproxy 的监听端口来避免这种冲突。

iptables -t mangle -N TSTF_PREROUTING
iptables -t mangle -A PREROUTING -j TSTF_PREROUTING
iptables -t mangle -A TSTF_PREROUTING -p tcp --tcp-flags SYN,ACK SYN,ACK -j RETURN
iptables -t mangle -A TSTF_PREROUTING ! -i lo -p tcp -m conntrack ! --ctstate ESTABLISHED -j TPROXY --on-port 21000 --on-ip 0.0.0.0 --tproxy-mark 0x1/0x1

ip rule add fwmark 0x1/0x1 pref 100 table 100
ip route add local default dev lo table 100

工作原理: 更改 tproxy 的监听端口,避开可能被其他进程占用或者被内核特殊处理的端口。

额外安全建议

使用 TPROXY 时务必谨慎,因为它会对网络流量造成巨大影响。 部署时务必经过充分测试,避免潜在的安全风险。同时,为了增加安全性,建议将监听 IP 绑定到特定的 IP 而非0.0.0.0,避免不必要的网络风险。并且, 在必要时使用 network namespace 分割, 以减少攻击面。

上述方法针对内核 3.10 环境,通常可以有效地解决 TPROXY iptables规则不兼容的问题。 不同的解决方案适用不同的场景,需要依据具体情况调整。如果问题仍未解决, 可以尝试升级内核或重新考虑是否选择其他方式进行流量劫持。