Linux iptables 与 nftables 防火墙工程化完全指南:从一次"凌晨手抖一条 DROP 拦截业务 1 小时"看懂为什么 iptables -F 远远不够

2021 年我们公司有一台直接对外暴露的网关机一开始就用了 Linux 默认 iptables 规则谁要加端口就 INSERT 一条谁要封 IP 就 APPEND 一条一年下来 iptables -L 出来三屏某天凌晨业务突然反馈部分用户访问被拒我登上机器看 iptables 把命中规则的顺序仔细排了一遍发现是有人误把一条 DROP 加在了 ACCEPT 之前整段业务流量被拦了 1 小时用户量损失数十万这只是个开始第二种最难缠我们某天上 nftables 想现代化一下结果一些老脚本还是 iptables 命令两套规则同时存在 NAT 路径跑 iptables conntrack 跑 nftables 出现了包过得了 NAT 但被 nftables 莫名 drop 的诡异现象排查 6 小时第三种最离谱有同事一时手快直接 iptables -F 想清规则结果 SSH 连接立刻被切了因为默认 INPUT 是 DROP 我们花了 40 分钟去 IDC 控制台救机器第四种最莫名其妙同样的规则在测试环境工作正常上了生产某些请求被拒后来发现是因为生产开了 connection tracking 而我们的规则用了 state ESTABLISHED 在不同内核版本上行为不一致第五种最致命我们的 NAT 表项达到了 100 万 conntrack 表满新连接直接被丢弃业务请求大面积 timeout 但 iptables -L 看不出任何问题因为问题在 conntrack 不在 filter 我盯着这一连串问题想了很久才彻底想明白第一版错在一个根本的认知上我以为 Linux 防火墙就是 iptables INPUT DROP 默认拒绝再放白名单就完事了可这个认知是错的真正能扛业务的 Linux 防火墙是一个表与链结构理解加规则顺序设计加 conntrack 调优加 nftables 迁移加持久化与变更流程的整套工程方法论任何一环没做都可能在某次变更里把业务打断几十分钟本文从头梳理 iptables 与 nftables 的核心结构表链规则的执行顺序 conntrack 的工作机制防火墙规则集的设计原则持久化方案以及一些把 Linux 防火墙做扎实要避开的工程坑

2021 年我们公司有一台直接对外暴露的网关机一开始就用了 Linux 默认 iptables 规则 谁要加端口就 INSERT 一条 谁要封 IP 就 APPEND 一条 一年下来 iptables -L 出来三屏。某天凌晨业务突然反馈 部分用户访问被拒 我登上机器看 iptables 把命中规则的顺序仔细排了一遍 发现是有人误把一条 DROP 加在了 ACCEPT 之前 整段业务流量被拦了 1 小时 用户量损失数十万。这只是个开始。第二种最难缠 我们某天上 nftables 想现代化一下结果一些老脚本还是 iptables 命令两套规则同时存在 NAT 路径跑 iptables conntrack 跑 nftables 出现了 包过得了 NAT 但被 nftables 莫名 drop 的诡异现象排查 6 小时。第三种最离谱 有同事一时手快直接 iptables -F 想清规则结果 SSH 连接立刻被切了 因为默认 INPUT 是 DROP 我们花了 40 分钟去 IDC 控制台救机器。第四种最莫名其妙 同样的规则在测试环境工作正常 上了生产某些请求被拒 后来发现是因为生产开了 connection tracking 而我们的规则用了 -m state ESTABLISHED 在不同内核版本上行为不一致。第五种最致命 我们的 NAT 表项达到了 100 万 conntrack 表满 新连接直接被丢弃 业务请求大面积 timeout 但 iptables -L 看不出任何问题 因为问题在 conntrack 不在 filter。我盯着这一连串问题想了很久才彻底想明白第一版错在一个根本的认知上我以为 Linux 防火墙就是 iptables INPUT DROP 默认拒绝再放白名单 就完事了 可这个认知是错的真正能扛业务的 Linux 防火墙是一个 表与链结构理解 加 规则顺序设计 加 conntrack 调优 加 nftables 迁移 加 持久化与变更流程 的整套工程方法论 任何一环没做都可能在某次变更里把业务打断几十分钟本文从头梳理 iptables 与 nftables 的核心结构 表 链 规则的执行顺序 conntrack 的工作机制 防火墙规则集的设计原则 持久化方案 以及一些把 Linux 防火墙做扎实要避开的工程坑

问题背景:为什么 Linux 防火墙比想象中复杂

很多人对 iptables 的印象停留在 加几条 ACCEPT 加几条 DROP 实际生产里你会发现:

  • iptables 是 5 张表 5 条链的复杂结构:filter nat mangle raw security 每张表对应不同链 ORDER 错了规则不生效。
  • 规则按顺序匹配 一旦命中即返回:规则插入位置不对就会被前面的规则截胡 加在末尾的 DROP 永远摸不到。
  • conntrack 是隐形的国王:大部分 NAT 和有状态过滤都依赖 conntrack 表满了所有新连接拒绝。
  • iptables 与 nftables 共存可能冲突:从 RHEL 8 Ubuntu 20 开始 nftables 是默认后端 但很多脚本仍调 iptables 二者机制不同。
  • 规则持久化不是默认行为:重启即失 必须显式 save 否则一次重启所有规则丢光。
  • 策略路由 + 防火墙容易交叉污染:VPN K8s 容器网络都在写规则 没统一规划就会冲突。

一 iptables 五张表五条链的本质

理解 iptables 必须先理解它的表与链。表 是按功能分类的规则集合 链 是数据包处理流程的钩子点 包穿过链时按顺序匹配规则。

# 五张表
# raw   绕过 conntrack 用于 NOTRACK 决定不跟踪某些连接
# mangle 修改数据包字段如 TOS TTL MARK
# nat   做 SNAT DNAT MASQUERADE
# filter 主防火墙过滤 ACCEPT DROP REJECT
# security SELinux 相关较少用

# 五条链 (Netfilter hooks)
# PREROUTING  入包刚进内核 NAT 之前
# INPUT       目标为本机的包
# FORWARD     需要转发的包
# OUTPUT      本机发出的包
# POSTROUTING 出包即将离开内核 SNAT

# 包路径示意 (入本机)
# PREROUTING -> 路由决策 -> INPUT -> 本地进程 -> OUTPUT -> POSTROUTING

# 查看具体表的规则
iptables -t filter -L INPUT -n -v --line-numbers
iptables -t nat    -L PREROUTING -n -v --line-numbers
iptables -t mangle -L -n -v

必须记住 不指定 -t 默认是 filter 表 所以 iptables -L 只看到 filter 的规则 nat 规则你压根看不到。我见过很多人 排查 NAT 不通 但只看了 iptables -L 没看 iptables -t nat -L 浪费几个小时。

二 规则顺序与默认策略:第一性原理

iptables 规则按顺序匹配 一旦命中 ACCEPT DROP REJECT 这类终止性 target 后续规则就不再检查。所以规则顺序极其重要 默认策略也极其重要。

# 错误示范 先 ACCEPT 80 然后 DROP all 看起来对 但 SSH 22 永远进不来
iptables -P INPUT DROP
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -j DROP

# 正确示范 先放 loopback 再放 established 再放业务 再放 SSH 最后 DROP
iptables -F INPUT
iptables -P INPUT DROP

# 放 lo 否则本机服务互访都不通
iptables -A INPUT -i lo -j ACCEPT

# 放已建立连接 否则你出去的包回不来
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# 放业务和 SSH
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# 放 ICMP 用于联通性诊断
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/s -j ACCEPT

# 末尾 LOG 后 DROP 用于排查
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "INPUT_DROP: "
iptables -A INPUT -j DROP

关键技巧:写规则前 一定先把 INPUT 默认策略保留 ACCEPT 把所有规则准备好再切换 P 否则 SSH 一断你就上不去机器。安全的切换方式是 加一条 cron 5 分钟后自动恢复 ACCEPT 给你回退窗口:

#!/bin/bash
# 安全切换默认策略 5 分钟内不取消就回滚

trap 'iptables -P INPUT ACCEPT; echo rolled back' EXIT INT TERM

iptables -P INPUT DROP
echo "DROP active for 300 seconds press Ctrl-C in another shell to keep it"
sleep 300
# 5 分钟后如果你没在另一个 shell 里 disable trap 就会自动回滚
trap - EXIT
echo committed

这个脚本的思路是 trap 在退出时回滚 你测试一切正常后 用另一个 shell 杀掉这个脚本前 先 disable 它的 trap 这样就避免了 永久锁死自己 的灾难。

三 conntrack:隐形的国王

conntrack 是 Linux netfilter 的连接追踪子系统 几乎所有 NAT 有状态过滤都依赖它。它维护一张 conntrack 表 记录每个连接的状态 源目地址端口 协议 超时时间。表满了所有新连接直接被丢 业务大面积超时 但你 iptables -L 看不出任何异常 这是 Linux 防火墙最常见的 隐形故障 之一

# 查看 conntrack 当前条目数和上限
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max

# 实时查看 conntrack 表
conntrack -L
conntrack -L -p tcp --state ESTABLISHED | wc -l

# 按状态统计
conntrack -L | awk '{print $4}' | sort | uniq -c | sort -rn

# 内核日志看 conntrack 是否溢出
dmesg | grep -i 'nf_conntrack: table full'

# 调大 conntrack 表 NAT 网关上必须做
sysctl -w net.netfilter.nf_conntrack_max=1048576
sysctl -w net.nf_conntrack_max=1048576

# 调小超时时间 高并发短连接场景下默认 432000 秒太长
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_established=3600
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_time_wait=60
sysctl -w net.netfilter.nf_conntrack_tcp_timeout_close_wait=15

# 调整哈希桶数 表大时必须同步增大桶数
echo 262144 > /sys/module/nf_conntrack/parameters/hashsize

上面是运行时调优 但 sysctl -w 重启即失 必须把这些值固化到配置文件 否则下次机器重启又是默认 65536 NAT 网关分分钟挂掉。下面是持久化方案 通过 sysctl.d 配置文件让内核启动时自动加载。

# 持久化 sysctl 配置
cat >> /etc/sysctl.d/99-conntrack.conf <<'EOF'
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 60
EOF
sysctl --system

nf_conntrack_max 默认对小机型来说很小 比如 4GB 内存的机器默认大约 65536 一旦 NAT 流量大就秒满 必须按 实际并发连接 + 30% 余量 设。hashsize 必须同步增大 通常设为 max 的 1/4 否则哈希冲突激增 性能急剧下降。

[mermaid]flowchart TD
A[入包到达网卡] --> B[PREROUTING raw]
B --> C{是否 NOTRACK}
C -->|否| D[conntrack 表查询
新建或查到表项]
C -->|是| E[绕过 conntrack]
D --> F[PREROUTING nat
DNAT 改目标地址]
E --> F
F --> G[路由决策]
G -->|本机| H[INPUT filter
过滤规则匹配]
G -->|转发| I[FORWARD filter]
H --> J[本地进程]
I --> K[POSTROUTING nat
SNAT 改源地址]
K --> L[出网卡]

四 iptables 迁移到 nftables

nftables 是 iptables 的现代继任 RHEL 8 Ubuntu 20+ 默认后端。它的优势是 单一语法替代 ip/ip6/arp/eb 多个工具 原子规则更新 集合 set 与 map 支持 性能更好。但迁移过程中混用是常见的坑。

# 查看当前哪个后端在用
update-alternatives --display iptables  # Debian/Ubuntu
alternatives --display iptables          # RHEL/CentOS

# 列出 nftables 规则
nft list ruleset

# 一个完整的 nftables 规则示例 等价于上面 iptables 那段
nft -f - <<'EOF'
table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        iif lo accept
        ct state established,related accept
        tcp dport { 22, 80, 443 } accept
        ip protocol icmp limit rate 5/second accept
        log prefix "INPUT_DROP: " limit rate 5/minute
        drop
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}
EOF

# 把 iptables 规则翻译成 nftables 用于辅助迁移
iptables-save > /tmp/v4.rules
iptables-restore-translate -f /tmp/v4.rules > /tmp/v4.nft

# 持久化 nftables 规则 (systemd 服务)
nft list ruleset > /etc/nftables.conf
systemctl enable --now nftables

迁移注意 别让 iptables 和 nftables 同时管理同一个 hook 否则规则顺序难以预测。我们的做法是 一台机器上要么全用 iptables 要么全切 nftables 切的时候彻底清掉 iptables 规则 systemctl disable iptables 再 enable nftables。

五 规则持久化与版本控制

iptables 规则在内存里 重启即失。生产必须做持久化。常见方案有 iptables-persistent 包 自己写 service netfilter-persistent。更进一步 规则集应该进版本控制 每次变更走 PR 审查。

# Debian/Ubuntu 用 iptables-persistent
apt install iptables-persistent
# 包安装时会问是否保存当前规则 后续要保存运行
netfilter-persistent save

# RHEL/CentOS 7 自带 iptables service
service iptables save
systemctl enable iptables

# 手动保存为文件
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6

# 还原
iptables-restore < /etc/iptables/rules.v4

# 推荐 把规则写成幂等脚本 通过 Ansible 推送
cat > /etc/firewall/rules.sh <<'EOF'
#!/bin/bash
set -eu
iptables -F
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -j DROP
EOF
chmod +x /etc/firewall/rules.sh

# 把 rules.sh 纳入 Git 仓库变更走 PR 审查
# 推送时先 dry-run 验证 再 atomically apply

规则集进 Git 是关键的工程实践 不仅是备份 更是变更可追溯 谁加了哪条 为什么加 都有记录。再配合 CI 比如 syntax check + 端到端连通性测试 才能避免凌晨手抖加错规则。

六 Linux 防火墙的工程坑:那些文档里学不到的

讲完原理来说几个真实生产里踩过的坑。第一个坑是 容器和宿主防火墙互相干扰 Docker 会自动在 iptables 里加一堆 DOCKER 链 一旦你 iptables -F 容器网络直接挂 必须 systemctl restart docker 让它重新加规则。第二个坑是 Kubernetes 的 kube-proxy iptables 模式会写几百上千条规则 高规模集群 iptables 链路长到查表性能下降 建议大集群切到 ipvs 模式。第三个坑是 IPv6 规则要分别管 ip6tables 和 iptables 是两套独立规则 你只写了 v4 没写 v6 一旦业务用 IPv6 防火墙形同虚设。

# 检查 IPv6 是否启用
sysctl net.ipv6.conf.all.disable_ipv6

# 必须给 v6 也写一套基础策略
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables-save > /etc/iptables/rules.v6

# 容器场景排查 看 DOCKER 链是否存在
iptables -t nat -L DOCKER -n
iptables -L DOCKER-USER -n

# Docker 推荐 在 DOCKER-USER 链里加自定义规则不要动 DOCKER 链
iptables -I DOCKER-USER -i eth0 -p tcp --dport 8080 -j DROP

# K8s 大集群切 ipvs 模式
# 在 kube-proxy ConfigMap 里设 mode: "ipvs"
ipvsadm -L -n  # 查看 ipvs 规则

第四个坑是 dropped 包的诊断没日志 你不知道什么包被 DROP 了 必须在 DROP 之前加 LOG 限速避免日志爆炸 然后 journalctl -k 看内核日志。第五个坑是 防火墙变更没有灰度 一刀切应用整套规则 一个错就全员断网 必须按环境分级 先测试机 再灰度机 最后生产 而且每次变更都走 工单 + Review + Rollback Plan 流程

关键概念速查

概念 含义 工程价值
filter 表 主防火墙过滤 ACCEPT DROP 主战场
nat 表 地址端口转换 NAT 网关核心
mangle 表 修改包字段 MARK QoS 路由
raw 表 绕过 conntrack NOTRACK 决定不跟踪
PREROUTING 链 入包刚进内核 DNAT 钩子
POSTROUTING 链 出包将离开 SNAT MASQUERADE 钩子
conntrack 连接追踪表 有状态过滤 NAT 依赖
nftables iptables 现代继任 RHEL8 Ubuntu20+ 默认
DOCKER-USER 链 Docker 留的扩展点 自定义规则正确位置
kube-proxy iptables K8s 服务转发 大集群建议切 ipvs

避坑清单

  1. iptables -F 之前一定确认默认策略不是 DROP 否则 SSH 立刻断。
  2. 切换默认策略到 DROP 必须有 trap 回滚机制 给自己留 5 分钟救命窗口。
  3. 不指定 -t 默认是 filter 表 排查 NAT 不通必须看 -t nat。
  4. conntrack 表必须监控 表满了所有新连接挂 但 iptables -L 看不出问题。
  5. NAT 网关必须调高 nf_conntrack_max 和 hashsize 默认值往往不够。
  6. iptables 与 nftables 不要同时管同一个 hook 否则规则顺序不可预测。
  7. 规则必须显式持久化 重启即失 别指望默认会保存。
  8. 规则集进 Git 走 PR 流程 别凌晨直接 SSH 手敲。
  9. IPv6 规则要单独管 别只写 v4 然后业务通 v6 防火墙白做。
  10. 容器场景在 DOCKER-USER 链加规则不要碰 DOCKER 链 否则 Docker 重启会冲掉。

总结

Linux 防火墙这事 很多人的直觉是 加几条 iptables 就完事了 这其实是把 我会写 iptables -A 和 我能在生产保住业务不被防火墙打断 混为一谈。前者是会用命令 后者是懂一整套 netfilter 工程体系。中间隔着的是 表与链结构 规则顺序 conntrack 调优 nftables 迁移 持久化策略 变更流程 容器与 K8s 集成 IPv6 兼容 整整一套工程方法论。

从原型到生产 你需要做的事远不止 iptables -A INPUT 这一条命令。你要懂五张表五条链怎么排序 要会调 conntrack 要会做 nftables 迁移 要会写幂等脚本 要进 Git 走 PR 要给 IPv6 也写一套 要应对容器和 K8s 的规则交叉 要做变更灰度。每一项单独看都不复杂 但它们组合在一起 才是一个能扛业务的防火墙体系。少任何一项 都可能在某次变更里 把你刚才省下的运维时间 连本带利地还回去 而且通常以 全员断网 的形式还。

我经常用一个比喻来理解 Linux 防火墙 它有点像高级写字楼的门禁系统。iptables 规则是门禁卡权限 conntrack 是已经在楼里的人的登记表 默认策略是 默认放行还是默认拒绝 持久化是断电后还认不认这套设置 nftables 是新一代的智能门禁 你不能因为楼里装了一套门禁就觉得安全 你还要管谁能进 进去后能去哪些楼层 已经在的人怎么管 断电了系统是不是还认账 这才是一整套门禁工程。

这套架构最难的地方在于 它的复杂度在 normal traffic 时几乎完全暴露不了。你看 iptables 跑着挺好 conntrack 也没满 业务也都正常 你觉得防火墙没什么。但真正流量峰值或者某次错误变更时 你才发现 99% 的复杂度都在 那 1% 的极端 case 里 conntrack 满 规则顺序冲突 IPv6 没规则 Docker 重启冲掉。建议任何想做 Linux 网络工程的团队 上线前一定要做一次故障演练 故意 iptables -F 看看业务挂多久 故意把 conntrack 打满看看监控有没有报 千万别等真实故障发生才学会 那时候你已经被业务老板追到办公桌前了。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
技术教程

LLM Prompt 注入与安全防御工程化完全指南:从一次"用户用法语翻译诱导 GPT-4 吐出整段 system prompt"看懂为什么 prompt 加固远远不够

2026-5-24 15:03:46

技术教程

RAG 检索增强生成工程化完全指南:从一次"200 万案例库 embedding 升级后检索质量暴跌"看懂为什么 cosine 相似度远远不够

2026-5-24 15:15:14

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索