Appearance
15|iptables 防火墙
服务起了、端口也监听了,从外部却访问不了——一查是防火墙把端口挡了。Linux 上做主机侧包过滤的,老一点的是 iptables,新的是 nftables。但 iptables 仍然广泛应用——老系统、容器网络(Kubernetes 的 kube-proxy 就生成大量 iptables 规则)、各种技术文档都依赖它。
本篇讲 iptables 在 Linux 网络栈里的位置、表/链/规则的核心模型,再给生产环境的标准规则模板、远程改防火墙的保命操作,以及 NAT 等进阶用法。
一、netfilter、iptables、firewalld、nftables 的关系
这几个名字经常被混用,先一次性理清:
- netfilter — Linux 内核中处理数据包的框架,提供"钩子点"让规则能在数据包经过网络栈时被检查
- iptables — 用户态工具,把规则写入 netfilter,操作老的 xtables 表/链模型
- nftables — netfilter 的新一代用户态工具(2014 起逐步取代 iptables),用全新的表/链结构,效率更高
- firewalld — 更高层的防火墙管理服务,底层可以使用 iptables 或 nftables 作为后端
- ufw — Ubuntu 的简化防火墙前端,底层也是 iptables
关键概念:你敲的 iptables 命令只是把规则配置写入内核 netfilter,真正在数据包流转时做匹配的是内核。这解释了几个常见现象:
- iptables 规则改完立即生效(内核运行时变更,无需重启)
- 重启系统后规则全部丢失(规则在内存中,不在磁盘),必须显式
iptables-save持久化 iptables -V查看的是用户态工具版本,内核 netfilter 的能力可能更新
当前主流发行版的默认状态
| 发行版 | 默认前端 | 实际后端 |
|---|---|---|
| CentOS 7 / RHEL 7 | firewalld 或 iptables 服务 | iptables (xtables) |
| RHEL 8+ / Rocky 8+ | firewalld | nftables(向后兼容 iptables 命令) |
| Ubuntu 18.04 | iptables / ufw | iptables (xtables) |
| Ubuntu 20.04+ | iptables / ufw / nftables | nftables(默认 iptables 命令实际是 iptables-nft) |
| Debian 11+ | nftables | nftables |
RHEL 8+ 和 Ubuntu 20.04+ 上,即使你敲的是 iptables 命令,实际操作的可能是 nftables——这是兼容层 iptables-nft 的作用。验证:
bash
$ iptables -V
iptables v1.8.7 (nf_tables)
# 括号里看到 nf_tables 表示后端实际是 nftables
# 看到 legacy 表示是经典 xtables 后端混用 iptables-legacy 和 iptables-nft 会出现"规则看起来加上了但不生效"——iptables-legacy -L 和 iptables-nft -L 看的是两套独立规则。统一用一种,生产环境选定后写入文档明确约束。
二、确认本机由谁管理防火墙
任何修改之前,必须先确认本机当前由哪个工具在管:
bash
# 看 iptables 版本和后端
iptables -V
# 看 firewalld 是否运行(RHEL 系)
systemctl status firewalld
# 看 ufw 是否启用(Ubuntu)
ufw status
# 直接看当前 iptables 规则
iptables -L -n -v
iptables -t nat -L -n -v多个工具同时启用会互相覆盖。常见冲突场景:
- 服务器装了 firewalld(开机自启),你手动
iptables -A加的规则,firewalld reload 时被清掉 - Docker 启动时会自动添加 iptables 规则(管理容器网络),如果直接
iptables -F清空规则,Docker 网络就坏了 - Kubernetes 节点上 kube-proxy 会大量生成 iptables 规则,人工添加的规则可能被冲掉
生产环境的最佳实践:整个团队约定使用一套工具(iptables / firewalld / nftables 三选一),不允许混用。在 init 阶段就明确启用其中一个、禁用其他:
bash
# 选择 iptables 直接管理,禁用 firewalld
systemctl stop firewalld
systemctl disable firewalld
systemctl mask firewalld # mask 表示彻底禁用,无法被其他服务依赖触发启动
systemctl enable iptables # RHEL 7
# 或 RHEL 8+:
yum install iptables-services -y
systemctl enable iptables ip6tables三、表、链、规则的三层模型
iptables 的规则组织成三个层次:表(按功能分类)、链(报文经过内核网络栈的检查点)、规则(匹配条件 + 动作)。
四张表
| 表 | 用途 | 使用频率 |
|---|---|---|
filter | 包过滤(放行/阻止),默认表 | 极高,90% 的规则都在这 |
nat | 地址转换(SNAT、DNAT、MASQUERADE) | 高,网关、端口转发场景 |
mangle | 修改包头字段(TOS、TTL、MSS、mark) | 低,QoS、流量整形场景 |
raw | 在连接跟踪之前处理 | 低,排除某些流量不做 conntrack |
操作其他表必须 -t <表名>,不指定默认是 filter:
bash
iptables -L # 等同于 iptables -t filter -L
iptables -t nat -L # 看 nat 表
iptables -t mangle -L # 看 mangle 表五条内置链
链对应报文经过内核网络栈的不同位置:
| 链 | 触发时机 |
|---|---|
PREROUTING | 报文刚到网卡,还未做路由决策 |
INPUT | 路由决策后,目标是本机 |
FORWARD | 路由决策后,需要转发到别的机器(本机作为网关) |
OUTPUT | 本机进程发出的报文 |
POSTROUTING | 报文即将离开网卡 |
报文经过哪些链取决于方向
| 流量方向 | 经过的链 |
|---|---|
| 外部 → 本机服务 | PREROUTING → INPUT |
| 本机 → 外部 | OUTPUT → POSTROUTING |
| 外部 → 本机转发 → 外部 | PREROUTING → FORWARD → POSTROUTING |
排查端口不通的第一步:确定流量方向,据此选择查哪条链。
- 别人访问本机服务不通 → 查
INPUT(filter 表) - 本机连不上外部 → 查
OUTPUT(filter 表) - 本机作为网关转发不通 → 查
FORWARD(filter 表) - 端口转发不通 → 查
PREROUTING(nat 表)+FORWARD(filter 表)
四、规则的查看与基本结构
查看命令
bash
iptables -L -n -v # filter 表,所有链
iptables -L INPUT -n -v --line-numbers # 看 INPUT 链,带行号
iptables -S # 输出格式接近命令本身,适合复制粘贴
iptables -t nat -L -n -v # NAT 表
iptables -t nat -L PREROUTING -n -v # NAT 表的 PREROUTING 链参数含义:-L list、-n 不解析名称(快很多)、-v 显示计数器(packets / bytes)、-S 以规则定义形式输出。
计数器:排查规则是否生效的关键
-v 输出里每条规则前的 pkts / bytes 是该规则匹配到的累计包数和字节数。计数器持续为 0 说明这条规则从未被命中——可能是因为:
- 流量方向理解错了(在错误的链里加规则)
- 表选错了(应在 nat 表里加但写到了 filter 表)
- 规则顺序错了,被前面的规则提前匹配掉
- 上游网络就把包丢了,根本没到本机
排查"规则改了但没效果"的标准动作:先看计数器,不要凭感觉调规则。
bash
# 清零计数器,然后产生测试流量,再看哪些规则被命中
iptables -Z # 清零 filter 表所有计数器
iptables -Z INPUT 3 # 清零 INPUT 第 3 条
# ... 产生测试流量 ...
iptables -L INPUT -n -v规则的基本语法
bash
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/24 -j ACCEPT逐段:
-A INPUT— append 到 INPUT 链末尾(-I INPUT 1表示插入到第 1 条位置)-p tcp— 匹配 TCP 协议--dport 22— 目的端口 22-s 10.0.0.0/24— 源 IP 在该网段-j ACCEPT— 目标(jump 到的 target)是 ACCEPT
常用匹配条件:
| 条件 | 含义 |
|---|---|
-p tcp/udp/icmp | 协议 |
--dport N / --sport N | 目的端口 / 源端口 |
--dports N,M,K | 多端口(需要 -m multiport) |
-s <CIDR> / -d <CIDR> | 源 IP / 目的 IP |
-i <网卡> / -o <网卡> | 入站网卡 / 出站网卡 |
-m conntrack --ctstate ... | 连接状态匹配 |
-m set --match-set <name> | ipset 集合匹配 |
-m time --timestart 09:00 --timestop 18:00 | 时间窗口匹配 |
常用动作(target):
| 动作 | 行为 | 客户端看到 |
|---|---|---|
ACCEPT | 放行 | 正常通信 |
DROP | 静默丢弃 | 连接超时(几十秒) |
REJECT | 拒绝并回复 ICMP/RST | 立即 Connection refused / unreachable |
LOG | 记录到 syslog,不阻断匹配继续 | 客户端无感知 |
RETURN | 从当前链返回到调用者 | 内部跳转,无外部行为 |
<自定义chain名> | 跳转到自定义链 | 内部跳转 |
DROP vs REJECT 的选择
| 场景 | 推荐 | 原因 |
|---|---|---|
| 公网入口 | DROP | 不回复任何信息,扫描器无法判断端口是否存在(不暴露服务列表) |
| 内网排错环境 | REJECT | 失败立即返回,不至于让应用等到超时,排查效率更高 |
| 阻止恶意 IP | DROP | 不消耗本机资源回包 |
| 限制本机用户访问某服务 | REJECT --reject-with icmp-port-unreachable | 给应用准确的错误,避免应用日志写满超时记录 |
五、最小可用规则集模板
一个生产可用的最小规则集,放行 SSH、Web 服务,禁止其他入站:
bash
#!/bin/bash
# 标准基础规则集
# 1. 清空当前规则(只在初始配置时,远程操作前必须备份当前规则!)
iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD
# 2. 默认策略先设 ACCEPT,所有规则配置完再改 DROP
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# 3. 放行已建立和相关的连接(关键!没有这条本机访问外网也会断)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 4. 放行本机回环(很多本地服务依赖 127.0.0.1 通信)
iptables -A INPUT -i lo -j ACCEPT
# 5. 放行 ICMP(可选,生产可只放行 echo-reply)
iptables -A INPUT -p icmp -j ACCEPT
# 6. 放行 SSH(限制源 IP 更安全)
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
# 7. 放行业务端口
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 8. 记录被丢弃的包(便于排查,但限速避免日志爆炸)
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables-dropped: " --log-level 4
# 9. 默认策略改为 DROP(必须最后做)
iptables -P INPUT DROP
# 10. 持久化
iptables-save > /etc/sysconfig/iptables # RHEL
# 或 iptables-save > /etc/iptables/rules.v4 # Debian/Ubuntu几个关键约束
约束 1:ESTABLISHED,RELATED 必须在 INPUT 链最前面
如果默认策略是 DROP,没有这条规则,本机主动发起的连接(浏览器、curl、yum、git)的返回包会被丢弃——本机彻底不能访问外网。这是新手最容易踩的坑。
约束 2:默认策略 DROP 必须最后设置
bash
# 错误顺序:先 DROP 再添加 ACCEPT 规则
iptables -P INPUT DROP # SSH 立刻断
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # 已经断了,加这条也没用
# 正确顺序:先确保所有放行规则到位,最后改默认策略
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# ... 其他规则 ...
iptables -P INPUT DROP约束 3:lo 必须放行
很多本地服务通过 127.0.0.1 通信(数据库 socket、本地 RPC、监控指标采集),没有这条规则,这些通信全部失败。
六、远程改防火墙的保命流程
远程通过 SSH 改防火墙规则是和远程改网络配置同级的高风险操作。一条 iptables -P INPUT DROP 或一条错误的拒绝规则,立刻把自己关在外面。
第 1 步:备份当前规则
任何修改前,先把当前规则保存一份:
bash
iptables-save > /tmp/iptables-backup-$(date +%Y%m%d-%H%M%S).rules
iptables -t nat -L -n -v > /tmp/iptables-nat-backup-$(date +%Y%m%d-%H%M%S).log第 2 步:开 tmux 保持会话
bash
tmux new -s fw如果 SSH 因防火墙错误断开,tmux 会话仍在后台运行,自动恢复脚本能继续执行。
第 3 步:准备定时回滚脚本
这是最重要的保命手段——用 at 设置 10 分钟后自动恢复原规则,如果 10 分钟内确认配置 OK,取消这个任务:
bash
# 准备 10 分钟后自动恢复
echo "iptables-restore < /tmp/iptables-backup-$(date +%Y%m%d-%H%M%S).rules" \
| at now + 10 minutes
# atq 看到任务编号,例如 7
# 改规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# ... 其他规则 ...
iptables -P INPUT DROP
# 验证新规则:从另一台机器尝试 SSH 连接
# 验证通过 → 取消自动恢复
atrm 7
# 持久化
iptables-save > /etc/sysconfig/iptables10 分钟内不取消,系统会自动把规则恢复到操作前的状态。即使 SSH 完全断开,等够 10 分钟就能重新登录。
简化版本(无 at 时):
bash
# 5 分钟后无条件恢复(除非这个进程被 kill)
nohup bash -c "sleep 300 && iptables-restore < /tmp/iptables-backup.rules" &
FALLBACK_PID=$!
# 改规则、测试...
# 验证 OK 后取消
kill $FALLBACK_PID第 4 步:不在默认策略 DROP 状态下做编辑
远程操作时,保持默认策略 ACCEPT,通过具体的 DROP 规则实现限制——这样即使中间步骤漏写,SSH 也不会立刻断。所有规则添加完、验证完之后,最后一步才改默认策略:
bash
# 推荐流程
iptables -P INPUT ACCEPT # 保持宽松状态
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
# ... 全部规则添加完毕 ...
iptables -A INPUT -j DROP # 最后一条:DROP 所有未匹配
# 不改 -P INPUT,保持 ACCEPT。最后那条 -A INPUT -j DROP 起到同样效果
# 但更安全——这是一条普通规则,而不是默认策略如果发现某条规则写错了,可以用 -D 删除那条规则,而不是改默认策略——一切错误都可以局部修复,不会"一键自杀"。
七、连接状态跟踪 conntrack
iptables 通过连接跟踪(conntrack)识别一个包属于哪个连接、处于什么状态。
五种连接状态
| 状态 | 含义 |
|---|---|
NEW | 新连接的第一个包 |
ESTABLISHED | 属于已经建立的连接 |
RELATED | 与已有连接相关(如 FTP 数据通道、ICMP 错误响应) |
INVALID | 无法归类的异常包 |
UNTRACKED | 显式标记为不跟踪(raw 表 NOTRACK target) |
ESTABLISHED,RELATED 是几乎所有 INPUT 链放行规则的第一条:
bash
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT老写法是 -m state --state ...,新写法是 -m conntrack --ctstate ...,功能等价,新规则统一用 conntrack。
conntrack 表本身的容量限制
conntrack 跟踪每个连接占用一个条目,容量上限 net.netfilter.nf_conntrack_max。具体问题和处理见 第 14 讲 网络诊断与排错 第八节"conntrack 表满"。
看 conntrack 跟踪的当前连接
bash
# 装 conntrack-tools
yum install conntrack-tools -y
apt install conntrack -y
# 看当前所有跟踪的连接
conntrack -L
# 看某个具体连接
conntrack -L -p tcp --dport 80
# 删除某个跟踪条目(用于强制中断某个连接)
conntrack -D --orig-src 1.2.3.4八、NAT:SNAT、MASQUERADE、DNAT
NAT 修改包的源地址或目的地址,典型场景:
| 场景 | 链 + 表 | target |
|---|---|---|
| 内网共享一个公网 IP 上网(NAT 网关) | -t nat POSTROUTING | MASQUERADE 或 SNAT |
| 外部访问到内网某机器(端口映射) | -t nat PREROUTING | DNAT |
| 修改本机对外访问的源 IP | -t nat POSTROUTING | SNAT |
MASQUERADE:动态源 IP 转换
MASQUERADE 是 SNAT 的特殊形式,自动使用出口网卡当前的 IP 作为转换后的源地址。适合出口 IP 可能变化的场景(动态 IP、ADSL):
bash
# 让 10.0.0.0/24 网段的流量从 eth0 出去时,源 IP 改为 eth0 的 IP
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADESNAT:固定源 IP 转换
如果出口 IP 已知且固定,SNAT 比 MASQUERADE 性能稍好(不需要每个包都查询接口 IP):
bash
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j SNAT --to-source 203.0.113.1DNAT:端口转发
把外部访问本机 8080 的流量,转发到内网 10.0.0.10:80:
bash
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.10:80启用 IP 转发
做 NAT 或转发,必须启用 IP 转发功能:
bash
# 临时启用
sysctl -w net.ipv4.ip_forward=1
# 持久化
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/99-ip-forward.conf
sysctl --system漏掉这一步是 NAT 配置最常见的失败原因——iptables 规则配得完全正确,但因为 ip_forward=0,内核不转发包,表现为"规则计数器是 0,完全没生效"。
DNAT 后,FORWARD 链也要放行
DNAT 在 PREROUTING 改写目的地后,包还要经过 FORWARD 链才能转发出去。filter 表 FORWARD 链如果默认 DROP,DNAT 仍然不工作:
bash
# DNAT
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 10.0.0.10:80
# 同时在 filter FORWARD 放行(关键,否则被默认 DROP 拦掉)
iptables -A FORWARD -p tcp -d 10.0.0.10 --dport 80 -m conntrack --ctstate NEW -j ACCEPT九、自定义 chain:让规则可维护
当 INPUT 链规则超过几十条,管理就很费劲。自定义 chain 让规则按业务/逻辑分组,主链只做分发:
bash
# 1. 创建自定义 chain
iptables -N WEB-INPUT
iptables -N SSH-INPUT
iptables -N ADMIN-INPUT
# 2. 主链按目标端口分发到对应 chain
iptables -A INPUT -p tcp --dport 80 -j WEB-INPUT
iptables -A INPUT -p tcp --dport 443 -j WEB-INPUT
iptables -A INPUT -p tcp --dport 22 -j SSH-INPUT
iptables -A INPUT -p tcp -m multiport --dports 3306,5432,6379 -j ADMIN-INPUT
# 3. 各业务 chain 内部写具体规则
# Web 服务对所有人开放
iptables -A WEB-INPUT -j ACCEPT
# SSH 只允许内网
iptables -A SSH-INPUT -s 10.0.0.0/8 -j ACCEPT
iptables -A SSH-INPUT -j LOG --log-prefix "ssh-denied: "
iptables -A SSH-INPUT -j DROP
# 数据库类只允许应用服务器网段
iptables -A ADMIN-INPUT -s 10.1.0.0/16 -j ACCEPT
iptables -A ADMIN-INPUT -j DROP
# 4. 删除整个自定义 chain
iptables -F WEB-INPUT # 先清空
iptables -X WEB-INPUT # 再删除好处:
- 主 INPUT 链只有十几条分发规则,一眼看清结构
- 调整 Web 访问策略,只需在 WEB-INPUT 中改,不影响其他业务
- 出问题时可以独立看某个 chain 的计数器,定位精准
十、ipset:大量 IP 的高性能匹配
需要根据几千上万个 IP 做匹配(黑名单、白名单、地理 IP 限制)时,不要写几千条 iptables 规则——每个包要逐条匹配,性能急剧下降。
ipset 把 IP 集合存储在内核哈希表里,iptables 用一条规则就能匹配整个集合,O(1) 查找复杂度。
bash
# 装 ipset
yum install ipset -y
apt install ipset -y
# 创建一个 IP 集合
ipset create blacklist hash:ip maxelem 1000000
# 加 IP(可批量从文件加)
ipset add blacklist 1.2.3.4
ipset add blacklist 5.6.7.8
while read ip; do ipset add blacklist "$ip"; done < blacklist.txt
# iptables 用一条规则匹配整个集合
iptables -I INPUT 1 -m set --match-set blacklist src -j DROP
# 查看集合内容
ipset list blacklist
# 删除 IP
ipset del blacklist 1.2.3.4
# 持久化(集合本身要保存)
ipset save > /etc/ipset.conf
# 恢复
ipset restore < /etc/ipset.conf集合类型选择
| 类型 | 存储 | 用途 |
|---|---|---|
hash:ip | 单个 IP | IP 黑/白名单 |
hash:net | CIDR 网段 | 网段黑/白名单(国家 IP 段) |
hash:ip,port | IP + 端口 | 限制某 IP 访问特定端口 |
hash:mac | MAC 地址 | MAC 过滤 |
地理 IP 限制的标准模板:
bash
# 只允许中国大陆 IP 访问 22 端口
ipset create cn-cidr hash:net maxelem 100000
# 从 GeoIP 数据导入中国 IP 段
while read cidr; do ipset add cn-cidr "$cidr"; done < cn-cidr-list.txt
iptables -A INPUT -p tcp --dport 22 -m set --match-set cn-cidr src -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP性能数据:同样阻止 10 万个 IP,纯 iptables 规则会让每个包平均匹配 5 万条规则,CPU 直接吃满;改用 ipset,匹配复杂度从 O(N) 降到 O(1),CPU 几乎无影响。
十一、LOG target:排查规则的关键
LOG target 把匹配到的包记录到 syslog,不阻断,继续匹配后面的规则。这是排查"我的规则到底匹配了什么"的核心工具:
bash
# 在 DROP 之前加一条 LOG,看到底什么包被丢了
iptables -A INPUT -j LOG --log-prefix "iptables-dropped: " --log-level 4
iptables -A INPUT -j DROP
# 日志查看
journalctl -k -f | grep "iptables-dropped"
# 或
tail -f /var/log/kern.log | grep "iptables-dropped"LOG 必须限速,否则会撑爆日志
恶意扫描或者突发流量下,LOG 规则一秒钟可能产生几千条日志,瞬间把 /var/log 占满。生产环境的 LOG 规则必须配 -m limit 限速:
bash
iptables -A INPUT -m limit --limit 5/min --limit-burst 10 \
-j LOG --log-prefix "iptables-dropped: " --log-level 4--limit 5/min 表示每分钟最多记录 5 条,--limit-burst 10 是初始突发额度。这样既能看到代表性的丢包样本,又不会撑爆磁盘。
十二、持久化:重启不丢规则
iptables 规则存在内核内存,重启后丢失。必须显式保存到配置文件。
RHEL / CentOS / Rocky
RHEL 7:
bash
iptables-save > /etc/sysconfig/iptables
systemctl enable iptablesRHEL 8+:
bash
yum install iptables-services -y
iptables-save > /etc/sysconfig/iptables
systemctl enable iptables ip6tables或者用 firewalld(推荐):
bash
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --reload
# firewalld 自动持久化,无需额外保存Debian / Ubuntu
bash
apt install iptables-persistent -y
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
systemctl enable netfilter-persistent配置文件作为版本管理
iptables-save 输出的格式是文本,可以直接放进 Git 做版本管理。比"手工执行命令"更可靠:
bash
# 提交当前规则到 Git
iptables-save > firewall-rules-$(hostname).conf
git add firewall-rules-$(hostname).conf
git commit -m "update firewall: allow port 8080 for app rollout"
# 部署时
iptables-restore < firewall-rules-$(hostname).conf十三、向 nftables 迁移
iptables 在新发行版上正在被 nftables 替代。nftables 的优势:
- 单一工具替代 iptables/ip6tables/arptables/ebtables 四个
- 规则结构更清晰,支持原生集合、字典、多维表达式
- 性能更好(尤其规则数量大时)
- 原子规则更新(整批替换,不会出现中间状态)
命令对照
| iptables | nftables |
|---|---|
iptables -L | nft list ruleset |
iptables -A INPUT -p tcp --dport 22 -j ACCEPT | nft add rule inet filter input tcp dport 22 accept |
iptables-save > file | nft list ruleset > file |
iptables-restore < file | nft -f file |
iptables-translate:自动转换
iptables-translate 命令(随 nftables 提供)可以把现有 iptables 命令翻译成 nftables 语法,辅助迁移:
bash
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# 输出:
# nft 'add rule ip filter INPUT tcp dport 22 counter accept'何时迁移
- 现在:RHEL 8+、Debian 11+、Ubuntu 22.04+ 上的新项目可以直接用 nftables
- 过渡期:存量 iptables 规则在 RHEL 8+ 上通过
iptables-nft兼容层工作,无需立即迁移 - 不要混用:在同一台机器上不要同时用 iptables-legacy 和 nftables(包括 iptables-nft),会出现"看不见对方规则"的现象
十四、易踩的陷阱速查
| 现象 | 根因 | 处理 |
|---|---|---|
-P INPUT DROP 后 SSH 立刻断 | 默认策略改 DROP 时,放行 SSH 规则未生效 | 远程修改用本讲第六节的保命流程 |
| 本机连不上外网,但 INPUT 链里放行了出站 | INPUT 链没有 ESTABLISHED,RELATED 规则,返回包被丢 | 第一条加 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT |
| 规则改完没生效,计数器始终 0 | 流量方向错(查错链)或表错(规则在 filter 但应在 nat) | 看计数器、确认流量方向、对照表/链选择 |
| DNAT 规则正确但流量不通 | net.ipv4.ip_forward = 0 或 FORWARD 链 DROP | 启用 ip_forward 并放行 FORWARD |
| iptables 命令看不到 Docker/K8s 规则 | RHEL 8+ 上 iptables -V 显示 legacy,但 Docker 用 nf_tables | 统一使用 iptables-nft,或全部走 nftables |
iptables -F 清空后 Docker 网络坏 | Docker 规则被清掉 | 不要无脑 -F,重启 Docker 重建规则:systemctl restart docker |
| LOG 规则后日志爆磁盘 | 没加 -m limit 限速 | LOG 规则必须配 --limit 5/min 这样的限速 |
| 重启后规则丢失 | 没持久化 | 改完立刻 iptables-save 持久化 |
| 几千条规则后 CPU 高 | 包按规则顺序逐条匹配,O(N) 复杂度 | 大量 IP 匹配改用 ipset |
| 临时排查时加的规则忘了删 | 没有规则生命周期管理 | 临时规则在注释里写来源和到期时间,用 iptables -A INPUT ... -m comment --comment "tempXXX" 标记 |