iptables 已添加 DROP 规则,为何端口依然可以连接?
引言
在日常的 Linux 服务器运维中,很多管理员都会遇到这样一个令人困惑的问题:明明已经通过 iptables 添加了 DROP 规则来封锁某个端口,但使用 telnet、nc 或 curl 等工具测试时,该端口依然可以正常连接。这个问题看似简单,实则涉及 iptables 的多条规则链、规则匹配顺序、Docker 网络机制等多个层面。本文将深入分析常见原因,并提供系统化的排查思路和解决方案。
一、常见原因分析
1. 规则顺序问题:ACCEPT 规则排在 DROP 之前
iptables 按照规则在链中的先后顺序依次匹配,一旦匹配成功就不再继续往下走。如果你的 DROP 规则被添加在 ACCEPT 规则之后,那么数据包会在到达 DROP 之前就已被放行。
典型错误示例:
# 错误:先放行了 8080 端口,再添加 DROP
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT
iptables -A INPUT -p tcp --dport 8080 -j DROP
上面的配置中,DROP 规则永远不会生效,因为数据包在第一条规则就已经被 ACCEPT 了。
正确做法: 使用 -I(Insert)将规则插入到链的最前面:
# 将 DROP 规则插入到 INPUT 链的第一条
iptables -I INPUT 1 -p tcp --dport 8080 -j DROP
2. 状态跟踪模块(conntrack)放行了已建立连接
iptables 的 state 或 conntrack 模块会跟踪连接状态。如果你的防火墙规则中包含类似以下的规则:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
那么,在 DROP 规则添加之前已经建立的连接,以及相关联的连接,都会被这条规则放行。这意味着:
- 已建立的 TCP 连接不会被中断
- 相关的 ICMP 等关联流量也会被放行
排查方法: 查看当前活跃连接:
# 查看已建立的连接
ss -tnp | grep 8080
# 或者使用 conntrack 工具
conntrack -L -p tcp --dport 8080
解决方案: 如果需要立即阻断已建立的连接:
# 使用 conntrack 删除特定端口的连接跟踪条目
conntrack -D -p tcp --dport 8080
# 或者更激进的方式:重启相关服务
systemctl restart your-service
3. Docker 容器端口映射绕过 INPUT 链
这是最常见的”陷阱”之一。Docker 在启动容器并映射端口时,会直接操作 iptables 的 nat 表和 filter 表,在 DOCKER 链中添加规则。Docker 的端口映射走的是 PREROUTING → DOCKER 链 → FORWARD 链的路径,完全不经过 INPUT 链。
因此,在 INPUT 链中添加 DROP 规则对 Docker 映射的端口完全无效。
验证方法:
# 查看 Docker 相关的 iptables 规则
iptables -nL DOCKER
iptables -t nat -nL DOCKER
iptables -nL FORWARD | grep DOCKER
你会看到类似如下的规则:
Chain DOCKER (2 references)
target prot opt source destination
ACCEPT tcp -- 0.0.0.0/0 172.17.0.2 tcp dpt:8080
解决方案:
方案 A:在 DOCKER-USER 链中添加规则(推荐)
Docker 预留了 DOCKER-USER 链,专门用于用户自定义过滤规则:
# 在 DOCKER-USER 链中添加 DROP 规则
iptables -I DOCKER-USER -p tcp --dport 8080 -j DROP
# 如果只想禁止特定来源 IP
iptables -I DOCKER-USER -s 192.168.1.0/24 -p tcp --dport 8080 -j DROP
方案 B:在 FORWARD 链中添加规则
# 在 FORWARD 链中封锁目标容器 IP 和端口
iptables -I FORWARD -d 172.17.0.2 -p tcp --dport 8080 -j DROP
方案 C:修改 Docker 配置,不自动操作 iptables
编辑 /etc/docker/daemon.json:
{
"iptables": false
}
⚠️ 注意:此方法会导致 Docker 容器无法访问外部网络,除非你手动配置 NAT 规则。仅建议在明确了解后果的情况下使用。
4. nat 表中的 PREROUTING 转发
如果系统中配置了 nat 表的 PREROUTING 规则(端口转发),数据包会在进入 filter 表之前就被重定向。即使你在 filter 表的 INPUT 链中封锁了原始端口,重定向后的流量可能走的是完全不同的路径。
排查方法:
# 查看 nat 表的所有规则
iptables -t nat -nL --line-numbers
# 特别关注 PREROUTING 和 OUTPUT 链
iptables -t nat -nL PREROUTING
iptables -t nat -nL OUTPUT
典型场景:
# 有人配置了端口转发,将 80 端口的流量转发到 8080
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
此时即使你 DROP 了 80 端口,经过 nat 表重定向后的流量实际上访问的是 8080 端口,需要在对应端口也添加封锁规则。
5. 多表多链的默认策略问题
iptables 有多个表(filter、nat、mangle、raw)和多个链(PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING)。数据包的处理流程是:
PREROUTING (raw) → PREROUTING (mangle) → PREROUTING (nat)
↓ (路由决策)
INPUT (mangle) → INPUT (filter) → 本地进程
或
FORWARD (mangle) → FORWARD (filter) → POSTROUTING
如果你的 DROP 规则只在 filter 表的 INPUT 链中,但流量是通过 FORWARD 链转发的,规则就不会生效。
6. IPv6 流量未被封锁
很多人只配置了 iptables(IPv4),却忽略了 ip6tables(IPv6)。如果服务同时监听 IPv4 和 IPv6,攻击者可以通过 IPv6 地址绕过防火墙。
# 检查 IPv6 规则
ip6tables -nL
# 同样需要添加 IPv6 的 DROP 规则
ip6tables -I INPUT -p tcp --dport 8080 -j DROP
二、系统化排查步骤
当遇到”DROP 规则不生效”的问题时,按以下步骤逐一排查:
步骤 1:确认规则确实存在
# 查看 filter 表 INPUT 链的所有规则(带行号)
iptables -nL INPUT --line-numbers
# 查看所有表的所有规则
iptables-save | grep 8080
步骤 2:检查规则顺序
# 确认 DROP 规则在 ACCEPT 规则之前
iptables -nL INPUT --line-numbers | grep -E "8080|ACCEPT|DROP"
步骤 3:使用 iptables 日志追踪数据包
# 在 DROP 规则之前插入一条日志规则
iptables -I INPUT 1 -p tcp --dport 8080 -j LOG --log-prefix "IPTABLES-8080: "
# 查看内核日志
dmesg | grep "IPTABLES-8080"
# 或
journalctl -k | grep "IPTABLES-8080"
步骤 4:检查是否为 Docker 端口映射
# 查看端口是否由 Docker 占用
docker ps | grep 8080
netstat -tlnp | grep 8080
# 查看 Docker 的 iptables 规则
iptables -nL DOCKER-USER
iptables -nL DOCKER
步骤 5:检查 nat 表和端口转发
iptables -t nat -nL --line-numbers
步骤 6:检查 IPv6 规则
ip6tables -nL INPUT --line-numbers
步骤 7:使用 tcpdump 抓包确认流量路径
# 抓取目标端口的流量
tcpdump -i any port 8080 -nn
三、完整解决方案速查表
| 场景 | 解决方案 |
|---|---|
| 规则顺序错误 | 使用 iptables -I INPUT 1 ... 将 DROP 规则插入最前面 |
| 已建立连接未中断 | 使用 conntrack -D 删除连接跟踪 |
| Docker 端口映射 | 在 DOCKER-USER 链中添加规则 |
| nat 表端口转发 | 同时在 nat 表和 filter 表中配置 |
| FORWARD 链转发 | 在 FORWARD 链中添加 DROP 规则 |
| IPv6 未封锁 | 使用 ip6tables 添加对应规则 |
四、最佳实践建议
-
使用
iptables-save和iptables-restore管理规则:避免逐条添加导致的顺序混乱。 -
优先使用
-I而非-A:对于安全相关的 DROP 规则,始终使用插入方式确保优先级。 -
定期审查规则:使用
iptables -nL --line-numbers -v查看规则的命中计数(packets/bytes),判断规则是否真正生效。
# 查看带统计信息的规则
iptables -nL INPUT --line-numbers -v
-
Docker 环境下使用 DOCKER-USER 链:这是 Docker 官方推荐的方式,重启 Docker 后规则不会丢失(但需配合持久化方案)。
-
持久化规则:
# Debian/Ubuntu
apt install iptables-persistent
netfilter-persistent save
# CentOS/RHEL
yum install iptables-services
service iptables save
总结
iptables 的 DROP 规则”不生效”通常不是 iptables 本身的 bug,而是由规则顺序、连接跟踪、Docker 网络架构、nat 表转发、IPv6 等多种因素导致的。理解 Linux 网络数据包在内核中的完整处理流程(Netfilter 框架),是排查此类问题的关键。
核心要记住:数据包从网卡到应用程序,要经过多条链和多个表,任何一条链上的 ACCEPT 都可能让你的 DROP 规则失效。 系统化的排查思路加上对 Netfilter 架构的深入理解,才能真正做到”封得住、管得牢”。
如果本文对你有帮助,欢迎收藏和分享。如有疑问,欢迎在评论区讨论。















暂无评论内容