服务器运维

iptables 命令详解:四表五链、查看规则与保存配置

iptables 是 Linux 内核 netfilter 防火墙的规则管理命令:数据包在不同位置经过不同的“链”,每条链上按“表”的顺序处理,链内规则从上往下匹配,命中 ACCEPT、DROP 就停止。默认策略改成 DROP 之前,先放行 SSH 和已建立的连接;改完要保存,否则重启后规则全部丢失。

作者 顶联产品团队发布于 7 分钟阅读

iptables 是 Linux 上管理 netfilter 防火墙规则的命令。规则按“表”和“链”组织:链决定在数据包经过的哪个位置检查,进入本机走 INPUT,本机发出走 OUTPUT,经本机转发走 FORWARD,路由前后分别是 PREROUTING 和 POSTROUTING;表决定做哪类处理,filter 负责放行和拒绝,nat 负责地址转换,mangle 负责改包和打标记,raw 决定是否跳过连接跟踪。同一条链里的规则从上往下匹配,命中 ACCEPT、DROP、REJECT 这类终止动作就不再往下看,全都不匹配时执行链的默认策略。日常用得最多的是三组命令:iptables -L -n -v --line-numbers 查看规则,-A、-I、-D 增删规则,iptables-save 配合持久化服务保存规则。

四表五链:数据包按什么顺序经过

表 作用 常用动作 所在的链
raw 决定数据包是否进入连接跟踪 CT --notrack PREROUTING、OUTPUT
mangle 修改包头字段、打标记 MARK、TOS、TTL 五条链都有
nat 地址和端口转换,只处理每个连接的第一个包 DNAT、SNAT、MASQUERADE、REDIRECT PREROUTING、INPUT、OUTPUT、POSTROUTING
filter 放行或拒绝;命令里不写 -t 时操作的就是它 ACCEPT、DROP、REJECT INPUT、FORWARD、OUTPUT

另外还有一个 security 表,供 SELinux 给数据包打安全标记,一般用不到。数据包走的是下面三条路径之一,括号里是同一条链上各个表的处理顺序(省略了 security 表和很少用到的 nat 表 INPUT 链):

外部进入本机:PREROUTING(raw → mangle → nat) → 路由判断 → INPUT(mangle → filter) → 本机进程
本机发出:    本机进程 → 路由判断 → OUTPUT(raw → mangle → nat → filter) → POSTROUTING(mangle → nat)
经本机转发:  PREROUTING(raw → mangle → nat) → 路由判断 → FORWARD(mangle → filter) → POSTROUTING(mangle → nat)

这张顺序图能解释很多“规则没生效”的问题:

  • DNAT 发生在过滤之前。PREROUTING 的 nat 表已经把目标地址改成了内网地址,所以 FORWARD 链里的放行规则要写转换后的地址和端口,而不是公网地址。
  • nat 表只看每个连接的第一个包,后续的包按连接跟踪里记下的映射自动转换。改了 nat 规则后,已经建立的连接不受影响,只有新连接才按新规则走。
  • 发给本机进程的包走 INPUT,经 DNAT 转给 Docker 容器的包走 FORWARD,不经过 INPUT。所以在 INPUT 链上封端口,挡不住 Docker 发布出去的容器端口。
  • 链内顺序决定结果。终止动作一旦命中就结束;LOG、MARK 这类非终止动作执行完继续匹配下一条;跳到自定义链后没有命中任何终止动作,会回到原链接着往下走。只有内置链有默认策略,自定义链没有。

查看规则:iptables -L -n -v –line-numbers

iptables -L -n -v --line-numbers            # filter 表的全部链
iptables -L INPUT -n -v --line-numbers      # 只看 INPUT 链
iptables -t nat -L -n -v --line-numbers     # nat 表要用 -t 指定
iptables -S                                 # 以命令形式输出,便于复制、比对
iptables -S INPUT
参数 作用
-L 列出规则
-n 地址和端口按数字显示,不做反向解析
-v 显示每条规则的包数、字节数,以及 in、out 网卡
--line-numbers 显示规则编号,插入和删除时要用
-x 计数显示精确值,不缩写成 K、M、G
-t 表名 指定表,不写就是 filter

一段典型的输出:

Chain INPUT (policy DROP 8213 packets, 492K bytes)
num   pkts bytes target     prot opt in     out     source               destination
1     117K   12M ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0
2      52M   41G ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
3      206 12360 ACCEPT     tcp  --  *      *       203.0.113.0/24       0.0.0.0/0            tcp dpt:22
4    1893K  103M ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            multiport dports 80,443
5        0     0 DROP       all  --  *      *       198.51.100.23        0.0.0.0/0

逐项看:

  • 第一行的 policy:INPUT 的默认策略是 DROP,括号里是被默认策略丢掉的包数和字节数。这个数字增长很快,说明有大量未放行的流量打到这台机器,可能是扫描,也可能是你漏放了某个业务端口。
  • pkts、bytes:每条规则命中的包数和流量。第 5 条是后来用 -A 追加的封禁规则,排在放行规则后面:这个 IP 访问 80、443 时先被第 4 条放行,根本走不到第 5 条;访问其他端口时,即使没有这条规则,也会被默认策略丢掉。示例里它的计数是 0,只说明这个 IP 还没访问过其他端口;就算计数在涨,它丢掉的也是默认策略本来就会丢的包。这条规则放在末尾等于没写,要封禁一个 IP 的全部访问,必须插到放行规则前面。
  • in、out:规则限定的网卡,* 表示不限。第 1 条只匹配回环接口 lo。
  • 最后一列:附加的匹配条件,tcp dpt:22 是目标端口 22,ctstate RELATED,ESTABLISHED 是已建立连接的后续包和相关连接。

较新版本的 iptables 加 -n 时,prot 列可能显示协议号而不是名称:0 表示全部协议,6 是 tcp,17 是 udp,1 是 icmp。想知道某条规则最近有没有在命中,可以 iptables -Z INPUT 把 INPUT 链的计数清零,过几分钟再看。脚本里要判断一条规则是否已经存在,用 iptables -C INPUT -p tcp --dport 22 -j ACCEPT,存在时返回 0,不存在时返回 1,避免重复添加。

增删改规则:-A、-I、-D、-R

参数 作用 示例
-A 链 追加到链的末尾 iptables -A INPUT -p tcp --dport 443 -j ACCEPT
-I 链 [编号] 插入到指定位置,不写编号就插到第 1 条 iptables -I INPUT 3 -p tcp --dport 8080 -j ACCEPT
-D 链 编号、-D 链 规则 按编号或按内容删除 iptables -D INPUT 3
-R 链 编号 规则 替换指定编号的规则 iptables -R INPUT 4 -p tcp -m multiport --dports 80,443,8443 -j ACCEPT
-C 链 规则 检查规则是否存在 脚本里防止重复添加
-F [链] 清空规则,不改默认策略 iptables -F INPUT
-P 链 动作 设置内置链的默认策略 iptables -P INPUT DROP
-N、-X、-E 新建、删除、重命名自定义链 iptables -N MGMT
-Z [链] 计数清零 iptables -Z

常用的匹配条件:

-p tcp                              # 协议:tcp、udp、icmp;--dport、--sport 必须和 -p 一起用
-s 203.0.113.0/24 -d 10.0.0.5       # 源、目标地址,前面加 ! 表示取反
-i eth0 -o eth1                     # 入、出网卡;INPUT 链只能用 -i,OUTPUT 链只能用 -o
-m multiport --dports 80,443        # 一条规则匹配多个端口,最多 15 个
-m conntrack --ctstate NEW          # 连接状态:NEW、ESTABLISHED、RELATED、INVALID
-m iprange --src-range 10.0.0.10-10.0.0.50
-m comment --comment "monitoring"   # 备注,-L 和 -S 的输出里都能看到

几个日常例子:

# 封禁一个 IP 的全部访问:插到第 1 条
iptables -I INPUT -s 198.51.100.23 -j DROP

# 只允许监控服务器访问 node_exporter 的 9100 端口
iptables -I INPUT 3 -p tcp -s 10.10.0.5 --dport 9100 -m comment --comment "node_exporter" -j ACCEPT

# 按内容删除:把添加时的 -I 或 -A 换成 -D,其余照抄
iptables -D INPUT -s 198.51.100.23 -j DROP

两个常见的坑:

  • 按编号连续删除,编号会变。 删掉第 3 条后,原来的第 4 条变成第 3 条。要删多条时从大号往小号删,或者每删一条重新看一次 --line-numbers。
  • -A 追加到了拒绝规则后面。 CentOS 7 的 iptables-services 默认规则以 -A INPUT -j REJECT --reject-with icmp-host-prohibited 结尾,之后用 -A 追加的放行规则都排在它后面,永远匹配不到,表现为“规则加上了,端口还是不通”。这种链要用 -I 指定编号,插在 REJECT 之前。

默认策略改 DROP:先放行 SSH,最后再 DROP

把 INPUT 改成默认拒绝,是服务器防火墙的常规做法,顺序错一步就会把自己锁在外面。原则是先放行、后拒绝。以 INPUT 链原本为空为例:

# 1. 备份现有规则
iptables-save > /root/iptables-before.rules

# 2. 放行回环接口和已建立的连接,当前的 SSH 会话靠这条维持
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# 3. 放行 SSH(尽量限定来源)、业务端口和 ping
iptables -A INPUT -p tcp -s 203.0.113.0/24 --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# 4. 核对一遍,最后一步才改默认策略
iptables -L INPUT -n --line-numbers
iptables -P INPUT DROP

改完不要关掉当前窗口,另开一个终端测试 SSH 能否新建连接。更稳妥的做法是给自己留退路:Debian、Ubuntu 的 iptables 包自带 iptables-apply,它先加载规则文件,再要求你在限定时间内确认,新规则把连接断了、你没法确认,它就自动回滚到原来的规则:

iptables-apply -t 60 /etc/iptables/rules.v4

没有这个工具时,可以先安排一个定时恢复任务,确认没问题再取消(需要 atd 服务在运行):

echo 'iptables-restore < /root/iptables-before.rules' | at now + 5 minutes
atq                     # 记下任务号,确认新规则没问题后执行 atrm 任务号

iptables -F 和 iptables -A INPUT -j DROP 的区别

默认拒绝有两种写法:用 -P INPUT DROP 设默认策略,或在链末尾追加一条 -A INPUT -j DROP。两者在日常操作中的表现正好相反:

写法 好处 风险
iptables -P INPUT DROP 之后用 -A 追加的放行规则照常生效 iptables -F 只清规则、不改策略,清空后连已建立的连接也被丢弃,远程会话当场断开
iptables -A INPUT -j DROP iptables -F 会连它一起清掉,清空后变成全部放行,不会锁死 之后用 -A 追加的规则全排在它后面,全部无效,只能用 -I

所以要清空规则时,顺序是先 iptables -P INPUT ACCEPT,再 iptables -F。另外,-F 只清 filter 表,nat 表要用 iptables -t nat -F 单独清;自定义链要在清空后用 -X 删除。装了 Docker、Kubernetes 的机器不要整表清空,它们维护的链被清掉后,容器网络会中断。

保存配置:CentOS 和 Debian 的方法不同

iptables 命令改的是内核里正在生效的规则,重启就没了。保存靠两个命令加一个开机加载服务:iptables-save 把当前规则导出成文本,iptables-restore 把文本一次性加载回内核。

发行版 安装 保存当前规则 规则文件 开机加载
CentOS 7、RHEL 8 系 yum install iptables-services service iptables save /etc/sysconfig/iptables、/etc/sysconfig/ip6tables systemctl enable iptables
Rocky、AlmaLinux 9 包名为 iptables-nft-services,以 dnf search iptables 的结果为准 同上 同上 同上
Debian、Ubuntu apt install iptables-persistent netfilter-persistent save /etc/iptables/rules.v4、/etc/iptables/rules.v6 安装后 netfilter-persistent 服务自动启用

几点说明:

  • RHEL 系要先停用 firewalld:systemctl disable --now firewalld,再 systemctl enable --now iptables,两者只能留一个。
  • /etc/sysconfig/iptables-config 里的 IPTABLES_SAVE_ON_STOP 和 IPTABLES_SAVE_ON_RESTART 默认是 no,改成 yes 后,停止或重启服务时会自动保存当前规则。一般建议保持 no,把规则文件当作唯一来源,改文件后重新加载,不容易出现“内存里的规则和文件不一致”。
  • Debian 安装 iptables-persistent 时会询问是否保存当前的 IPv4、IPv6 规则;以后每次改完规则,都要再执行一次 netfilter-persistent save。

规则文件也可以直接编辑,格式就是 iptables-save 的输出:

*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -s 203.0.113.0/24 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
COMMIT

*filter 到 COMMIT 是一张表,:INPUT DROP [0:0] 是链的默认策略和计数。改完先检查语法,再加载:

iptables-restore --test < /etc/iptables/rules.v4    # 只解析不生效,出错时会指出行号
iptables-restore < /etc/iptables/rules.v4           # 整表一次性替换
iptables-restore --noflush < extra.rules            # 不清空现有规则,只追加文件里的规则

装了 Docker 的机器,iptables-save 导出的内容会包含 Docker 自己生成的链和规则,原样保存再开机加载,可能和 Docker 启动时重建的规则重复或冲突。这类机器只保存自己写的规则;要限制外部访问容器端口,规则写在 Docker 预留的 DOCKER-USER 链里。

几个常用场景的写法

管理端口只对白名单开放,用自定义链集中维护来源地址,以后增减地址只改这条链:

iptables -N MGMT
iptables -A MGMT -s 203.0.113.0/24 -j ACCEPT
iptables -A MGMT -s 10.10.0.0/24 -j ACCEPT
iptables -A MGMT -j DROP
iptables -I INPUT 3 -p tcp -m multiport --dports 22,3306,6379 -j MGMT

记录被默认策略丢弃的包,放在 INPUT 链末尾,用 limit 限制写日志的频率,避免日志被刷爆。日志写进内核日志,用 journalctl -k | grep IPT-DROP 查看:

iptables -A INPUT -m limit --limit 10/min -j LOG --log-prefix "IPT-DROP: " --log-level 4

内网服务器经本机访问外网,本机作为 NAT 网关。出口 IP 固定时也可以把 MASQUERADE 换成 -j SNAT --to-source 公网IP:

sysctl -w net.ipv4.ip_forward=1        # 要长期生效,写进 /etc/sysctl.d/ 下的配置文件
iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o eth0 -j MASQUERADE
iptables -A FORWARD -s 10.10.0.0/24 -o eth0 -j ACCEPT
iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

在管理系统里怎么落地

规则写错把自己锁在外面,是改防火墙时最常见的事故。服务器有 BMC 时,VNC 控制台走的是带外通道,不经过操作系统的网络协议栈,iptables 挡不住它:在 Toplink DCIM 的 IPMI 远程管理里打开控制台,本地登录后把默认策略改回 ACCEPT 即可;租用服务器的客户也能在客户自助端自己打开 VNC 控制台处理,不必等机房值班。主机防火墙管的是“谁能访问这台机器的哪个端口”,伪造源地址、盗用 IP 这类问题要在接入交换机上解决,DCIM 的交换机管理可以对端口开启 ARP 绑定和源地址校验。

常见问题

iptables 的规则对 IPv6 生效吗?

不生效。IPv6 由 ip6tables 单独管理,语法基本相同,保存文件分别是 /etc/sysconfig/ip6tables(RHEL 系)和 /etc/iptables/rules.v6(Debian 系)。只配了 IPv4 规则的服务器,IPv6 地址上的端口照样对外开放;给 IPv6 设默认 DROP 时,还要放行 ICMPv6,否则邻居发现失败,IPv6 会直接断网。

iptables -L 执行很慢、半天才出结果是怎么回事?

没加 -n 时,iptables 会对规则里的每个 IP 做反向 DNS 解析、把端口号换成服务名,DNS 不通或很慢时每个地址都要等超时。加上 -n 就能立即输出,日常查看建议总是带上。

DROP 和 REJECT 有什么区别,用哪个?

DROP 直接丢弃,对方收不到任何回应,只能等超时;REJECT 会回一个错误,默认是 ICMP 端口不可达,TCP 可以用 --reject-with tcp-reset 回 RST,对方立刻知道被拒绝。面向公网的默认策略多用 DROP,不给扫描器任何反馈;内网用 REJECT,应用能快速失败,排障也更直观。

想看看在你的机房里怎么用?

从一次产品演示开始,梳理你的业务链路。

查看价格
服务热线 400-112-2951

让我们聊聊你的 IDC 业务

扫码添加企业微信,预约产品演示或申请试用。

Toplink 企业微信二维码

长按保存或使用企业微信 / 微信扫描

也可致电
400-112-2951
Telegram
@TopLink88