iptables 规则写完不生效,3 个常见排查点
写 iptables 规则最让人头疼的场景莫过于:命令执行成功、规则也能列出来,但流量就是不按预期走 —— 该拦的拦不住,该放的放不通。
排查这类问题不用瞎猜,90% 的情况都逃不开下面这 3 个经典坑。按顺序过一遍,基本都能定位。
排查点一:规则顺序错了,被前面的规则 “截胡”
问题现象
明明加了一条 DROP 规则封禁某个 IP,对方却还能正常访问;或者写了 ACCEPT 放行端口,端口依然不通。用iptables -L看规则确实存在,但计数器(pkts/bytes)一直是 0。
根本原因
iptables 遵循首次匹配原则:数据包从上到下依次匹配链中的规则,一旦某条规则命中并执行了 ACCEPT / DROP / REJECT 等终止性动作,后续规则就不再检查。
绝大多数人习惯用-A(append)追加规则,新规则会被加到链的末尾。如果前面已经有一条范围更大的规则先匹配了流量,你后面加的规则就永远不会触发。
典型反面教材
# 先放行所有入站流量iptables-AINPUT-jACCEPT# 再封禁某个 IP —— 永远不会生效!iptables-AINPUT-s192.168.1.100-jDROP上面第二条规则写了等于白写,因为所有包在第一条就被 ACCEPT 了。
正确做法
精细规则放前面,宽泛规则放后面
插入规则用
-I插到链首,或用-I INPUT 数字插到指定位置封禁类规则优先放在放行规则之前
# 正确顺序:先封禁,再放行其他iptables-IINPUT-s192.168.1.100-jDROP iptables-AINPUT-jACCEPT快速验证
用带计数器的命令查看命中情况:
iptables-LINPUT-n-v--line-numbers如果某条规则 pkts 一直是 0,大概率是被前面的规则截胡了。
排查点二:链或表选错了,流量根本不走这条链
问题现象
规则写了、顺序也对,但就是零命中。常见于端口映射、容器暴露端口、转发流量等场景。
根本原因
iptables 有 “四表五链”,不同流向的数据包经过的链完全不同。规则放错了链或表,流量自然碰不到它。
最容易踩的几个坑:
1. INPUT 和 FORWARD 搞混
INPUT 链:目标地址是本机的流量(别人访问你服务器上的服务)
FORWARD 链:经过本机转发的流量(路由器 / 网关场景、容器流量转发)
如果你在做端口映射或者用 Docker 暴露端口,流量经过的是 FORWARD 链而非 INPUT,往 INPUT 里加规则当然无效。
2. filter 表和 nat 表搞混
filter 表(默认):用来过滤放行 / 拦截
nat 表:用来做地址转换(DNAT/SNAT)
DNAT 端口映射必须写在nat表的PREROUTING链,写在 filter 表里不会生效。
3. Docker 环境的特殊坑
Docker 启动后会自己管理 iptables 规则,容器暴露的端口流量走DOCKER链和DOCKER-USER链。你在宿主机 INPUT 链里加的封禁规则,对容器端口基本无效。
正确做法是把规则加到DOCKER-USER链:
# 封禁某个 IP 访问容器暴露的端口iptables-IDOCKER-USER-s192.168.1.100-jDROP快速自查清单
流量是访问本机还是经过转发?→ 选 INPUT / FORWARD
是过滤还是地址转换?→ 选 filter /nat 表
有没有 Docker / Kubernetes 等容器环境?→ 检查 DOCKER-USER 链
服务监听在 [127.0.0.1](127.0.0.1) 还是 [0.0.0.0](0.0.0.0)?→ 回环地址不走物理网卡规则
排查点三:连接跟踪机制导致 “看起来没生效”
问题现象
新加了一条封禁规则,但已建立的连接还在正常通信,过了好几分钟甚至更久才断开。尤其常见于 SSH、MySQL、长连接 HTTP 等场景。
根本原因
这不是 bug,是 iptables 的连接跟踪(conntrack)机制在起作用。
Linux 内核会跟踪每一条 TCP/UDP 连接的状态(NEW / ESTABLISHED / RELATED 等)。对于已经处于ESTABLISHED状态的连接,后续数据包会直接复用连接跟踪表的记录,不会逐条重新匹配规则。
所以你新增的 DROP 规则,只能影响之后新建的连接,不会主动切断已经建立好的连接。看起来就像 “规则没生效”。
TCP 长连接的默认超时时间可以长达 5 天(nf_conntrack_tcp_timeout_established默认值 432000 秒),如果不主动断开,能一直挂着。
解决方法
如果需要立即生效,需要手动清理已有的连接跟踪记录:
# 安装 conntrack 工具yuminstallconntrack-tools# CentOS/RHELaptinstallconntrack# Debian/Ubuntu# 清除指定 IP 的连接记录conntrack-D-s192.168.1.100# 清除指定端口的连接记录conntrack-D-ptcp--dport3306延伸:写规则时的好习惯
建议在规则开头放行已建立连接的回包,这是标准写法:
# 放在 INPUT 链最前面iptables-IINPUT-mconntrack--ctstateESTABLISHED,RELATED-jACCEPT这样本机主动发起请求的返回流量能正常通过,不用为每个回包方向单独写规则。
最后:一分钟排查流程
遇到 iptables 规则不生效,按这个顺序走:
看顺序:
iptables -L -n -v --line-numbers,检查目标规则前面有没有更大范围的规则截胡看链 / 表:确认流量流向和规则所在的链、表是否匹配,注意 Docker 等特殊环境
看连接:
conntrack -L检查是不是已有长连接在复用,需要手动清理
掌握这三点,绝大多数 iptables “玄学不生效” 问题都能快速定位。如果还不行,可以加 LOG 目标打印匹配日志,进一步精准分析。
提示:操作 iptables 前建议先备份规则,远程操作时尤其注意不要把自己关在外面。生产环境修改规则建议先在测试机验证,或配合定时任务做自动回滚。