nftables 是 Linux 内核自 3.13 起引入的新一代包过滤框架,旨在统一并替代分散多年的 iptables、ip6tables、arptables 与 ebtables。本文用一套可直接落地的实战配置,演示如何把传统 iptables 规则平滑迁移到 nftables,并给出生产可用的服务器防火墙基线。
一、为什么要从 iptables 迁移到 nftables
iptables 的最大问题在于“分散”:IPv4 用 iptables,IPv6 用 ip6tables,ARP 用 arptables,规则分散在多张表里,维护成本高;且其基于逐条线性匹配,规则越多性能越差。nftables 在内核侧用一棵统一的、可解释的语法树执行,把“表/链/规则”的抽象统一起来,并带来三大收益:
- 统一语法:一套规则同时覆盖 IPv4/IPv6(用 inet family)。
- 原子提交:先构建整张规则集再一次性提交,避免中途刷新导致连接中断。
- 内核态解释:规则在 netfilter 中以字节码运行,比 iptables 的多次内核态/用户态往返更快。
| 维度 | iptables | nftables |
|---|---|---|
| IPv4/IPv6 | 两套命令 | inet 单套 |
| 规则提交 | 逐条生效 | 整集原子提交 |
| 默认策略 | 需显式兜底 | 支持 chain 级 policy |
| 可读可维护 | 散落多表 | 单一 ruleset |
如果你在用 Docker 跑容器,默认的网络隔离正是靠 iptables/nftables 后端实现的,理解 nftables 能帮你更安全地管理容器暴露的端口。
二、环境检查与组件安装
先确认内核与用户态工具:
uname -r # 建议 >= 3.13,生产推荐 4.x+
which nft # 未安装则 apt install nftables / yum install nftables
sudo nft list ruleset # 查看当前规则集(初始为空)
lsmod | grep nf_tables # 确认内核模块已加载
现代发行版(Debian 10+/Ubuntu 20.04+/RHEL 8+)默认已带 nftables。若此前完全用 iptables,建议先确认没有服务在频繁改写规则,再切换。
三、核心概念:表、链、规则与 hook
nftables 用 family 定义地址域:ip(仅 IPv4)、ip6、inet(双栈)、arp、bridge、netdev。每个 family 下建表(table),表内建链(chain),链上挂规则(rule)。链的 hook 决定挂载点:input(入站)、output(出站)、forward(转发)、prerouting、postrouting。
# 新建一张 inet 表与基础链
sudo nft add table inet filter
sudo nft add chain inet filter input { type filter hook input priority 0; policy drop; }
sudo nft add chain inet filter forward { type filter hook forward priority 0; policy drop; }
sudo nft add chain inet filter output { type filter hook output priority 0; policy accept; }
注意:policy drop 表示默认拒绝,配置不全会把自己挡在门外,务必先放行管理通道(见下)。
四、实战:一台 Web 服务器的防火墙基线
下面给出最小可用的入站白名单:放行回环、已建立连接、SSH、HTTP/HTTPS,其余拒绝。
sudo nft add rule inet filter input iif "lo" accept
sudo nft add rule inet filter input ct state established,related accept
sudo nft add rule inet filter input tcp dport 22 accept # SSH
sudo nft add rule inet filter input tcp dport { 80, 443 } accept # Web
sudo nft add rule inet filter input ip protocol icmp accept # ping
# 末尾默认 drop 由上链 policy 兜底
4.1 用脚本文件管理规则(可版本化)
逐条敲命令不利于回滚。把规则写进脚本文件,用 nft -f 加载:
# /etc/nftables.d/web-baseline.nft
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif "lo" accept
ct state established,related accept
tcp dport 22 accept
tcp dport { 80, 443 } accept
ip protocol icmp accept
}
}
# 加载:sudo nft -f /etc/nftables.d/web-baseline.nft
配合 SSH 安全加固(参见 SSH 连接故障排查实战),把 22 端口只开放给运维网段,能进一步缩小攻击面。
五、NAT 与端口转发
nftables 同样能做 SNAT/DNAT。例如让内网机器通过本机出网(masquerade):
sudo nft add table ip nat
sudo nft add chain ip nat postrouting { type nat hook postrouting priority 100; }
sudo nft add rule ip nat postrouting oif "eth0" masquerade
# 把公网 8080 转发到内网 10.0.0.10:80
sudo nft add chain ip nat prerouting { type nat hook prerouting priority -100; }
sudo nft add rule ip nat prerouting tcp dport 8080 dnat to 10.0.0.10:80
六、与 iptables 共存及平滑迁移
担心不兼容?多数发行版提供 iptables-nft 后端,让旧的 iptables 命令底层翻译成 nftables。更稳妥的做法是用翻译工具把既有规则转成 nft 语法:
iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
# 输出:nft add rule ip filter INPUT tcp dport 22 counter accept
ip6tables-translate -A INPUT -p icmpv6 -j ACCEPT
# 输出:nft add rule ip6 filter INPUT icmpv6 type { echo-request } counter accept
建议先在新机器用 nft 写好 ruleset,确认业务连通后再把 iptables 服务停掉,避免双套规则打架。
七、生产运维:持久化、备份与回滚
规则默认只在内存,重启即丢。导出与恢复:
sudo nft list ruleset > /var/backups/ruleset-$(date +%F).nft # 备份
sudo nft -f /var/backups/ruleset-2026-10-21.nft # 恢复
sudo systemctl enable nftables # 开机加载 /etc/nftables.conf
| 场景 | 命令 |
|---|---|
| 查看规则 | sudo nft list ruleset |
| 清空全部 | sudo nft flush ruleset |
| 删除某表 | sudo nft delete table inet filter |
| 实时监控 | sudo nft monitor |
| 原子加载 | sudo nft -f file.nft |
八、常见坑与排查
| 现象 | 根因 | 对策 |
|---|---|---|
| 配置后 SSH 断开 | 默认 drop 未放行 22 | 先 accept 政策,确认连通再收紧 |
| 容器端口不通 | Docker 后端与 nftables 冲突 | 统一用 nftables 后端,避免混用 |
| 规则重启丢失 | 未持久化 | 写入 /etc/nftables.conf 并 enable 服务 |
| established 不匹配 | 缺少 conntrack 模块 | 确认 nf_conntrack 已加载 |
排查时善用 sudo nft monitor 实时观察命中,或用 nft list ruleset 核对每条规则的计数器是否增长。
九、用集合(set)批量管理 IP 与端口
前面用 { 80, 443 } 写的是匿名集合,适合一次性规则。真正运维中更推荐命名集合(named set):把允许列表、黑名单抽出来单独维护,规则里只引用集合名,改 IP 不用动规则本身。
# 定义可动态更新的命名集合
sudo nft add set inet filter allow_ssh { type ipv4_addr; flags interval; }
sudo nft add element inet filter allow_ssh { 203.0.113.0/24, 198.51.100.7 }
sudo nft add rule inet filter input tcp dport 22 ip saddr @allow_ssh accept
# 带超时的临时黑名单(秒)
sudo nft add set inet filter block_tmp { type ipv4_addr; timeout 1h; }
sudo nft add element inet filter block_tmp { 192.0.2.66 timeout 1h }
命名集合还支持 interval(网段)、timeout(自动过期)、dynamic(配合计数器做限流),是 nftables 相比 iptables 最实用的增强之一。
十、限速与连接数限制:抗暴破与 CC
默认 accept SSH 等于把 22 端口完全敞开,容易被扫号。用 limit 限制新建连接的速率,可以有效挡住暴力破解与 CC 攻击:
# 新连接每分钟最多 3 次,超出丢弃(先放再限更稳妥)
sudo nft add rule inet filter input tcp dport 22 ct state new limit rate 3/minute accept
sudo nft add rule inet filter input tcp dport 22 ct state new drop
# 单 IP 并发连接数限制
sudo nft add rule inet filter input tcp dport 443 ct state new,established meter connlimit { ip saddr limit 20/count } accept
这种原生限速不依赖 fail2ban 等外部守护进程,规则随防火墙一起加载,重启也不丢。若需要更复杂的动态封禁(如连续失败 N 次封 IP),可结合命名集合的 timeout 用脚本自动写入。
十一、与 Docker / 容器网络的协同
在 Docker 主机上启用 nftables 要特别注意:Docker 默认会向防火墙注入自己的转发规则。确认后端统一,避免规则打架:
# 查看 Docker 注入的链
sudo nft list ruleset | grep -A 8 'chain.*DOCKER'
# /etc/docker/daemon.json 指定 nftables 后端(推荐)
{ "iptables": false, "bridge-nf-call-iptables": false }
建议策略:容器端口只在宿主用 -p 127.0.0.1:8080:80 绑定到回环或前置反向代理,再由 nftables 只放行宿主的 80/443,绝不让容器端口直接暴露在公网。这样既保留 Docker 的网络隔离,又由统一防火墙守边界。
十二、落地清单
- 确认内核 >= 3.13 且 nft 可用;
- 用脚本文件管理规则,先 policy accept 调试;
- 放行 lo / established / 管理端口后再切 drop;
- 用命名集合维护 IP 白名单与临时黑名单;
- 对 SSH 等管理端口加 limit 限速抗暴破;
- 用 iptables-translate 迁移旧规则;
- 导出备份并启用 nftables 开机服务。
nftables 不是银弹,但它用统一的语法、原子提交、命名集合与内核态字节码,显著降低了 Linux 防火墙的维护成本与出错概率。把本文的基线存进版本库,配合容器网络隔离与主机入侵检测,就能用一份可审计的配置守好服务器边界。下一步可以把它接入配置管理(Ansible/Puppet)做集群统一下发,让防火墙策略像代码一样被评审和回滚。




