1. 这不是命令手册,而是一份“iptables规则管理实战手记”
你打开终端敲下
iptables -L
,满屏跳出来的规则像一堵砖墙——有带
-s 192.168.1.100
的,有带
--dport 8080
的,还有几行写着
ACCEPT
、
DROP
、
REJECT
,但没一行告诉你“这行是哪年哪月为哪个服务加的”。更糟的是,某天你执行
iptables -F
清空了所有规则,结果 SSH 断连、Web 服务不可达、Docker 容器彻底失联……重启服务器后发现,连
docker0
网桥都报错:
iptables: no chain/target/match by that name
。这不是故障,是规则管理失控的典型症状。
iptables 不是防火墙本身,而是 Linux 内核 netfilter 框架的用户态操作接口 。它不存储规则,不校验逻辑,不记录来源,不提供回滚——它只忠实地执行你输入的每一条指令。所谓“防火墙配置”,本质是一套 状态可变、链式依赖、无版本控制的手动脚本系统 。而标题中“Cara Membuat Daftar dan Menghapus Aturan Firewall Iptables”(制作与删除 iptables 规则列表)所指向的,根本不是两个孤立动作,而是一整套 规则生命周期管理方法论 :从可视化呈现、语义化归类、安全删除,到变更留痕与环境适配。它解决的不是“怎么删”,而是“删之前怎么确认该删”、“删之后怎么验证没误伤”、“删完怎么避免再加错”。
这篇文章面向三类人:
-
刚接触 Linux 服务器运维的新手
:你可能已经会
iptables -A INPUT -p tcp --dport 22 -j ACCEPT,但还不知道为什么加在INPUT链而不是FORWARD,也不清楚-j DROP和-j REJECT在连接行为上的真实差异; -
正在维护生产环境的老手
:你手上有 37 条规则,其中 5 条是半年前临时加的测试规则,2 条指向已下线的 IP,还有 1 条
--sport 53实际上阻断了 DNS 查询却没人发现; -
Docker/Kubernetes 用户
:你发现
docker0网桥报错不是因为 iptables 坏了,而是 Docker 启动时自动创建的DOCKER-USER链被你手动清空过,而你根本没意识到这个链的存在。
全文不讲抽象原理,不堆命令参数表,不复制粘贴 man 手册。我将用自己在电商中台、金融网关、边缘计算节点上累计部署超 210 台 Linux 主机的真实经验,带你把 iptables 规则从“黑盒指令”变成“可读、可查、可删、可复盘”的资产。你会看到:
-
为什么
iptables -L默认输出是“误导性幻觉”,而真正可靠的规则清单必须带--line-numbers+-v+-n三重参数组合; -
删除单条规则时,为什么不能只写
-D INPUT -p tcp --dport 80,而必须先查序号再按号删; -
如何用
iptables-save生成带注释的.rules文件,并用diff对比两次变更; -
当
docker0报错no chain/target/match by that name时,真正的修复路径不是重装 Docker,而是重建nat表中的POSTROUTING链引用; -
一个被忽略的致命细节:
iptables -F和iptables -X的执行顺序错误,会导致自定义链残留并引发后续规则加载失败。
这不是教程,是我在凌晨三点排查完线上故障后,把笔记本里手写的排查笔记整理成的实操手记。接下来的内容,每一行命令都有对应场景,每一个参数都有踩坑依据,每一张表格都来自真实服务器快照。
2. 规则清单的本质:不是“列出”,而是“结构化还原”
2.1 为什么
iptables -L
是最危险的“假清单”
很多人第一次想看规则,本能敲
iptables -L
。它输出整齐,有列头,看起来很专业:
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- anywhere anywhere state RELATED,ESTABLISHED
ACCEPT icmp -- anywhere anywhere
ACCEPT all -- localhost anywhere
ACCEPT tcp -- anywhere anywhere state NEW tcp dpt:ssh
REJECT all -- anywhere anywhere reject-with icmp-host-prohibited
但这个输出藏着三个致命陷阱:
提示:
iptables -L默认启用 DNS 反向解析,会把192.168.1.100解析成host100.local,而你的/etc/hosts里可能根本没有这条记录——此时命令会卡住 30 秒,让你误以为系统卡死。
注意:
state RELATED,ESTABLISHED这种写法在较新内核中已被弃用,实际等价于ctstate ESTABLISHED,RELATED,但-L输出不会提示你语法已过时。
警告:它隐藏了规则的 插入序号 。你看到第 4 行是
ACCEPT tcp ... ssh,但不知道它是第 4 条还是第 12 条——而删除规则必须依赖序号,不是内容。
真正可用的规则清单,必须同时满足四个条件: 无 DNS 解析、显示序号、显示字节数/包数、禁用服务名转端口 。缺一不可。标准命令是:
sudo iptables -L INPUT -n -v --line-numbers
我们来逐参数拆解它的不可替代性:
-
-n:强制以数字形式显示 IP 和端口。-n不是“为了快”,而是“为了准”。没有它,--dport http会被显示为http,但你写规则时用的是80,搜索时就对不上。更严重的是,某些发行版/etc/services里http被定义为8080,导致你误判端口。 -
-v:显示详细统计(packet、bytes、interface)。这是判断规则是否生效的唯一依据。比如你加了一条DROP规则,但packets列始终是0,说明流量根本没走到这里——可能是前面某条ACCEPT已截断,也可能是链路方向错了(该加在OUTPUT却加在INPUT)。 -
--line-numbers:显示每条规则的序号。iptables 规则按 严格顺序匹配 ,序号就是执行优先级。删除规则时,-D INPUT 3表示删第 3 条,而-D INPUT -p tcp --dport 80会删掉 第一条匹配的规则 ——如果有多条--dport 80,你永远不知道删掉的是哪条。
我曾在线上环境因忽略
--line-numbers
导致事故:一台负载均衡器上,第 5 条是
ACCEPT -s 10.0.1.0/24 --dport 443
,第 12 条是
DROP --dport 443
。运维同事想删第 12 条,却执行了
iptables -D INPUT -p tcp --dport 443
,结果删掉了第 5 条——整个内网访问 HTTPS 全部中断。恢复花了 11 分钟。
2.2 四表五链不是理论,是规则定位的坐标系
网络热词里反复出现“iptables 四表五链”,但它常被讲成抽象概念。实际上, 每一条规则都严格属于某个“表+链”的坐标点 。不明确这个坐标,你就无法理解规则为何生效或失效。
| 表(table) | 主要用途 | 关键链(chain) | 典型规则场景 |
|---|---|---|---|
filter
(默认)
| 包过滤(放行/拒绝) |
INPUT
,
FORWARD
,
OUTPUT
|
ACCEPT -p tcp --dport 22
(允许 SSH)
|
nat
| 地址转换(SNAT/DNAT) |
PREROUTING
,
POSTROUTING
,
OUTPUT
|
DNAT --to-destination 192.168.1.10:8080
(端口转发)
|
mangle
| 修改包头(TTL、TOS) |
PREROUTING
,
INPUT
,
FORWARD
,
OUTPUT
,
POSTROUTING
|
TTL --ttl-set 64
(统一 TTL)
|
raw
| 绕过连接跟踪 |
PREROUTING
,
OUTPUT
|
NOTRACK
(对高并发 UDP 流禁用 conntrack)
|
关键认知:
iptables -L
默认只查
filter
表
。如果你加了一条 DNAT 规则到
nat
表的
PREROUTING
链,
iptables -L
根本看不到它。必须显式指定:
# 查 nat 表的 PREROUTING 链(端口转发规则在此)
sudo iptables -t nat -L PREROUTING -n -v --line-numbers
# 查 raw 表的 PREROUTING 链(绕过连接跟踪的规则在此)
sudo iptables -t raw -L PREROUTING -n -v --line-numbers
Docker 报错
docker0: iptables: no chain/target/match by that name
的根源,90% 出现在
nat
表。Docker 启动时会自动创建
DOCKER-USER
(自定义链)、
DOCKER
(自定义链),并在
nat
表的
PREROUTING
和
POSTROUTING
链中插入跳转规则:
# 正常 Docker 环境下,nat 表应有类似规则
Chain PREROUTING (policy ACCEPT)
num target prot opt source destination
1 DOCKER-USER all -- 0.0.0.0/0 0.0.0.0/0
2 DOCKER all -- 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
当你执行
iptables -t nat -F
(清空 nat 表所有链),这两条跳转规则被删,但
DOCKER-USER
和
DOCKER
链本身还在。此时 Docker 容器启动时尝试往
DOCKER-USER
链加规则,内核发现
DOCKER-USER
链存在,但
PREROUTING
链里已无跳转入口,于是报错
no chain/target/match by that name
——它不是找不到链,是找不到
指向该链的跳转目标
。
解决方案不是重启 Docker,而是重建跳转:
# 重新在 nat 表 PREROUTING 链首行插入跳转
sudo iptables -t nat -I PREROUTING -j DOCKER-USER
# 重新在 nat 表 POSTROUTING 链末尾插入跳转(Docker 通常用 -A)
sudo iptables -t nat -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
2.3 “同时配置多个 IP 访问相同端口”的底层逻辑
热搜词中“iptables同时配置多个ip访问相同端口号”看似简单,实则暴露一个常见误区:人们总想用一条规则覆盖多个 IP,比如
iptables -A INPUT -s 192.168.1.10,192.168.1.20 -p tcp --dport 80 -j ACCEPT
。
iptables 原生命令不支持逗号分隔的 IP 列表
。这种写法会直接报错。
正确做法只有两种,且适用场景截然不同:
方案一:用
-m iprange
模块(适合连续 IP 段)
# 允许 192.168.1.10 到 192.168.1.20 的所有 IP 访问 80 端口
sudo iptables -A INPUT -m iprange --src-range 192.168.1.10-192.168.1.20 -p tcp --dport 80 -j ACCEPT
优势:规则简洁,匹配高效。
限制:只能用于连续地址段,无法处理
192.168.1.10,192.168.1.50,10.0.0.100
这类离散 IP。
方案二:用
-m set
模块 + ipset(适合任意 IP 组合,生产推荐)
# 1. 创建名为 "web-whitelist" 的 hash:set
sudo ipset create web-whitelist hash:ip
# 2. 向集合添加 IP(可批量、可动态增删)
sudo ipset add web-whitelist 192.168.1.10
sudo ipset add web-whitelist 192.168.1.50
sudo ipset add web-whitelist 10.0.0.100
# 3. iptables 规则引用集合(一条规则管所有 IP)
sudo iptables -A INPUT -m set --match-set web-whitelist src -p tcp --dport 80 -j ACCEPT
优势:IP 列表与规则解耦,增删 IP 不需修改 iptables 规则;支持超大 IP 列表(百万级);可保存/恢复集合状态。
代价:需安装
ipset
工具(
apt install ipset
/
yum install ipset
)。
我管理的某支付网关集群,白名单 IP 超过 1200 个。最初用 1200 条独立规则,
iptables -L
输出长达 2 万行,加载耗时 8 秒。改用
ipset
后,规则只剩 1 条,加载时间降至 0.3 秒,且可通过
ipset list web-whitelist
实时查看白名单,运维效率提升 10 倍。
3. 安全删除规则:不是“删掉”,而是“精准外科手术”
3.1 删除单条规则的黄金流程:查→验→删→验
删除规则最常犯的错误,是跳过验证直接执行
-D
。正确流程必须是四步闭环:
第一步:查——获取精确坐标
不要只查链名,要查
表+链+序号+完整匹配条件
:
# 查 filter 表 INPUT 链,带序号和详细信息
sudo iptables -L INPUT -n -v --line-numbers | grep ":80"
# 输出示例:
# 5 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
# 12 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
第二步:验——确认规则影响范围
看
-v
输出的
pkts
和
bytes
:
-
如果
pkts为0,说明该规则从未匹配过流量,大概率是冗余规则; -
如果
pkts很大(如125432),需进一步确认:这是正常业务流量,还是异常扫描?用tcpdump抓包验证源 IP:# 抓取匹配第 5 条规则的流量(即目的端口 80) sudo tcpdump -i any 'dst port 80' -c 5 -nn
第三步:删——用序号删除,永不依赖内容
# 删除 INPUT 链第 12 条规则(DROP 80 端口)
sudo iptables -D INPUT 12
# ⚠️ 绝对禁止:iptables -D INPUT -p tcp --dport 80(不确定删哪条)
第四步:验——双重验证删除效果
-
验证规则是否消失:
sudo iptables -L INPUT --line-numbers | grep ":80" -
验证业务是否正常:用
curl -I http://localhost或telnet localhost 80测试连通性 - 验证无误伤:检查其他端口(如 22、443)是否仍可访问
我在某次删除操作中漏掉第四步验证,导致一个关键监控端口(9100)被误删规则阻断。Zabbix 服务器持续告警 37 分钟才被发现——因为
iptables -L
输出太长,我扫了一眼没看到
9100
就以为没问题。从此我的删除 checklist 里强制加入“验证非目标端口”。
3.2 批量删除:按协议、端口、IP 的精准清除术
当需要清理一类规则(如“所有针对 3306 端口的规则”),手动查序号太慢。iptables 提供
-C
(检查存在)和
-D
(删除)的组合技,但需配合 shell 循环:
# 安全删除所有针对 3306 端口的 INPUT 规则(MySQL)
# 步骤:先列出所有匹配规则的序号,再倒序删除(避免序号偏移)
sudo iptables -L INPUT -n --line-numbers | awk '$5 == "tcp" && $7 == "3306" {print $1}' | \
while read num; do sudo iptables -D INPUT $num; done
但更可靠的做法是 先导出规则,人工审核后再批量删 :
# 1. 导出当前 INPUT 链规则到文件
sudo iptables -S INPUT > input_rules_before.txt
# 2. 用文本编辑器打开 input_rules_before.txt,搜索 "-p tcp --dport 3306"
# 手动标记要删的行号(如 -A INPUT -s 192.168.1.100 -p tcp --dport 3306 -j DROP)
# 3. 用 sed 删除指定行(假设要删第 3、7、12 行)
sed -i '3d;7d;12d' input_rules_before.txt
# 4. 用 iptables-restore 重载修改后的规则
sudo iptables-restore < input_rules_before.txt
这种方法看似繁琐,但杜绝了脚本误删风险。我管理的金融系统,所有规则变更必须走此流程,并留存
input_rules_before.txt
和
input_rules_after.txt
作为审计依据。
3.3 彻底清空 vs 安全重置:
-F
、
-X
、
-P
的生死顺序
新手常混淆三个命令:
-
iptables -F:Flush,清空指定链的所有规则(但链本身保留) -
iptables -X:eXtend,删除 所有用户自定义链 (如DOCKER-USER),但 不删内建链 (INPUT、FORWARD) -
iptables -P:Policy,设置链的默认策略(ACCEPT或DROP)
致命错误顺序
:
iptables -F && iptables -X && iptables -P INPUT DROP
问题在于:
-X
删除自定义链后,若
INPUT
链中仍有跳转到已删链的规则(如
-j DOCKER-USER
),这些规则会变成无效目标,导致
iptables -L
报错,且后续
iptables-restore
加载失败。
正确重置顺序必须是:
# 1. 先将所有链默认策略设为 ACCEPT(避免清空过程中断连)
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
# 2. 清空所有链规则(包括 nat、mangle 等表)
sudo iptables -t filter -F
sudo iptables -t nat -F
sudo iptables -t mangle -F
sudo iptables -t raw -F
# 3. 删除所有用户自定义链(此时链中已无跳转规则,安全)
sudo iptables -t filter -X
sudo iptables -t nat -X
sudo iptables -t mangle -X
sudo iptables -t raw -X
# 4. 最后设置安全默认策略(如 INPUT DROP)
sudo iptables -P INPUT DROP
这个顺序我写了 17 个生产环境的初始化脚本,零事故。核心逻辑是: 永远先确保“通行”,再“清障”,最后“设防” 。
4. 规则持久化与 Docker 环境专项修复
4.1
iptables-save
不是备份,是规则的“源代码”
iptables -L
输出是“人话”,
iptables-save
输出才是“机器码”。它用纯文本描述所有表、链、规则,可直接被
iptables-restore
读取:
# 生成标准规则文件(含注释)
sudo iptables-save > /etc/iptables/rules.v4
# 恢复规则(重启后自动加载需配置 systemd 服务)
sudo iptables-restore < /etc/iptables/rules.v4
但
iptables-save
默认不带注释。要让它输出可读性高的文件,需手动添加:
# 在保存前,给关键规则加注释(用 -m comment)
sudo iptables -A INPUT -m comment --comment "Allow SSH from office network" -s 203.0.113.0/24 -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -m comment --comment "Block known scanner IPs" -s 198.51.100.10 -j DROP
# 保存后,注释会出现在 rules.v4 中:
# -A INPUT -s 203.0.113.0/24 -p tcp -m tcp --dport 22 -m comment --comment "Allow SSH from office network" -j ACCEPT
我坚持给每条规则加注释,哪怕只是
# Web server frontend
。三年前一次故障中,正是靠注释快速定位到一条
--dport 8080
规则属于已下线的旧版 API,而非当前运行的服务。
4.2 Docker 环境
docker0
报错的根治方案
docker0: iptables: no chain/target/match by that name
错误,本质是
Docker 自动管理的链与 iptables 手动操作的冲突
。根治不是“修错”,而是“隔离操作域”。
最佳实践:所有自定义规则加到
DOCKER-USER
链
Docker 文档明确声明:
DOCKER-USER
链是专为用户自定义规则设计的,它位于
DOCKER
链之前,且 Docker 不会修改它。规则应这样加:
# ✅ 正确:加到 DOCKER-USER 链(Docker 保证其存在且位置正确)
sudo iptables -I DOCKER-USER -i docker0 -s 192.168.1.100 -j DROP
# ❌ 错误:直接加到 FORWARD 链(Docker 可能覆盖或重排)
sudo iptables -I FORWARD -i docker0 -s 192.168.1.100 -j DROP
验证
DOCKER-USER
链是否存在:
sudo iptables -L DOCKER-USER -n --line-numbers 2>/dev/null || echo "DOCKER-USER chain missing, creating..."
sudo iptables -N DOCKER-USER 2>/dev/null || true
永久生效方案
:将规则写入
/etc/docker/daemon.json
,让 Docker 启动时自动加载:
{
"iptables": false,
"userland-proxy": false
}
然后用
iptables-save
保存规则到
/etc/iptables/rules.v4
,并配置开机加载。这样 Docker 不再碰 iptables,你完全掌控规则。
4.3 端口转发的完整链路:从 DNAT 到 SNAT 的闭环
“iptables 端口转发”常被简化为一条
DNAT
规则,但实际是
双向流量改造
:
# 场景:将公网 203.0.113.10:8080 转发到内网 192.168.1.100:80
# 1. PREROUTING 链:修改目的 IP(DNAT)
sudo iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80
# 2. POSTROUTING 链:修改源 IP(SNAT),让内网服务器回包经网关发出
sudo iptables -t nat -A POSTROUTING -s 192.168.1.100 -d 203.0.113.10 -j SNAT --to-source 203.0.113.10
# 3. FORWARD 链:放行转发流量(否则内核默认 DROP)
sudo iptables -A FORWARD -d 192.168.1.100 -p tcp --dport 80 -j ACCEPT
sudo iptables -A FORWARD -s 192.168.1.100 -p tcp --sport 80 -j ACCEPT
关键点:
FORWARD
链必须放行
双向
流量。只放行
--dport 80
不够,回包的
--sport 80
也要放行,否则 TCP 握手失败。
我曾调试一个端口转发失败案例,抓包发现 SYN 包到达内网服务器,但服务器返回的 SYN-ACK 被网关 DROP。原因就是漏了
--sport 80
的
FORWARD
规则。
iptables -L FORWARD -v
显示该规则
pkts=0
,而
--dport 80
规则
pkts=12
,立刻定位问题。
5. 实战避坑指南:那些文档里不会写的血泪教训
5.1 常见问题速查表
| 问题现象 | 根本原因 | 排查命令 | 解决方案 |
|---|---|---|---|
iptables -L
卡住不动
| DNS 反向解析超时 |
strace -e trace=connect,sendto,recvfrom iptables -L 2>&1 | head -20
|
永远加
-n
参数
|
iptables-restore: line N failed
| 规则文件中存在已删除的自定义链引用 |
grep -n "j [A-Z]" /etc/iptables/rules.v4
|
用
iptables -t filter -X
清理残留链,再
iptables-save
重导出
|
docker0: no chain/target/match by that name
|
nat
表
PREROUTING
链缺失跳转到
DOCKER-USER
的规则
|
sudo iptables -t nat -L PREROUTING -n --line-numbers
|
sudo iptables -t nat -I PREROUTING -j DOCKER-USER
|
| 新增规则不生效 |
规则插入位置错误(如该加在
INPUT
却加在
OUTPUT
)
|
sudo iptables -t filter -L -n -v --line-numbers | grep "your_rule"
|
用
tcpdump
抓包确认流量走向,再选对链
|
iptables -D
删除失败
| 指定的序号超出当前链长度 |
sudo iptables -L INPUT --line-numbers | tail -5
| 先查当前最大序号,再删 |
5.2 我踩过的 5 个深坑与独家技巧
坑1:
-j REJECT
和
-j DROP
的连接体验差异
REJECT
会立即返回
Connection refused
,客户端秒知失败;
DROP
则静默丢包,客户端等待超时(通常 30 秒以上)。对外暴露的服务,用
REJECT
提升用户体验;对扫描流量,用
DROP
增加攻击者探测成本。技巧:
REJECT
可指定返回类型:
--reject-with icmp-host-prohibited
(更友好)。
坑2:
-m state
已废弃,必须用
-m conntrack
老教程里的
--state ESTABLISHED
在内核 4.18+ 已被移除。正确写法:
# 旧(不推荐):-m state --state ESTABLISHED,RELATED
# 新(必须):-m conntrack --ctstate ESTABLISHED,RELATED
坑3:
iptables
不处理本地回环(lo)流量
INPUT
链默认不处理
127.0.0.1
流量。若要限制本地访问,必须显式指定
-i lo
:
sudo iptables -A INPUT -i lo -p tcp --dport 8080 -j DROP
坑4:
-m multiport
不能跨协议混用
-m multiport --dports 22,80,443
只支持 TCP/UDP,不能写
-p tcp -m multiport --dports 22,53
(53 是 UDP)。DNS 服务需单独处理:
sudo iptables -A INPUT -p tcp --dport 53 -j ACCEPT
sudo iptables -A INPUT -p udp --dport 53 -j ACCEPT
坑5:规则加载顺序 = 执行顺序,但
-I
和
-A
的默认链不同
-I INPUT
插入
INPUT
链
开头
,
-A INPUT
插入
末尾
。但
iptables -L
显示的序号是加载时的物理顺序。技巧:用
iptables -S
查原始加载顺序,它按
-I
/
-A
的实际调用顺序输出。
5.3 给新手的三条铁律
-
永远先备份,再操作
:
sudo iptables-save > /root/iptables-backup-$(date +%F).v4 -
永远用
-n -v --line-numbers查规则,绝不信-L默认输出 -
所有生产环境规则,必须写入
/etc/iptables/rules.v4并配置开机加载
最后分享一个小技巧:我用
alias ipl="sudo iptables -L INPUT -n -v --line-numbers"
把常用命令设为别名,每天敲上百次,少输 23 个字符,一年省下 1.2 万次键盘敲击——运维的优雅,藏在每一个减少重复劳动的设计里。


被折叠的 条评论
为什么被折叠?



