nftables:下一代 Linux 防火墙完全指南
概述
nftables 是 Linux 内核 netfilter 框架的新一代包过滤引擎,旨在取代经典的 iptables。它于 2014 年合并入 Linux 内核 3.13,经过十多年的发展,已成为所有主流 Linux 发行版的默认防火墙后端。
nftables 引入了一个全新的用户空间工具 nft,拥有更清晰的语法、更好的性能和更强大的功能。
一句话:nftables 就是"做对了的 iptables" —— 保留了 iptables 的能力,去掉了它的混乱。
核心优势
| 对比维度 | iptables | nftables |
|---|---|---|
| 语法 | 长参数命令行,每行一条规则 | 结构化语法,类似编程语言 |
| IPv4/IPv6 | 两套工具(iptables + ip6tables) | 统一处理(inet 地址族) |
| 规则集管理 | 不支持原子化替换 | 原生原子化(一次加载全部生效或全部回滚) |
| 集合与映射 | 需要 ipset 扩展 | 内置支持,性能大幅提升 |
| 批量操作 | 需 iptables-save/restore | 支持多规则一次性提交 |
| 性能 | 链式遍历,大规则集线性下降 | 集合+映射实现 O(1) 查找 |
| 调试 | 需额外模块 | 内置 nft monitor 实时追踪 |
| 向后兼容 | — | 通过 iptables-nft 提供兼容层 |
安装
查看系统是否支持
# 检查内核是否编译了 nf_tables
grep CONFIG_NF_TABLES /boot/config-$(uname -r)
# 或(更直接)
nft --version
Debian / Ubuntu
sudo apt update
# 安装 nftables(Ubuntu 22.04+ / Debian 11+ 通常已预装)
sudo apt install nftables
# 启动并启用
sudo systemctl enable nftables
sudo systemctl start nftables
RHEL / Rocky / AlmaLinux
# RHEL 8+ / Rocky 8+ 默认已安装 nftables
sudo dnf install nftables
sudo systemctl enable nftables
sudo systemctl start nftables
Arch Linux
sudo pacman -S nftables
sudo systemctl enable nftables
sudo systemctl start nftables
验证安装
nft --version
# nftables v1.0.9 (Old Doc Yak #3)
# 查看当前规则集
nft list ruleset
语法基础
iptables vs nftables 语法对比
先做一个直观对比,感受两者的差异:
iptables 风格(每条都是一个完整命令):
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -s 1.2.3.4 -j DROP
nftables 风格(更像结构化配置):
# 创建一个规则集文件,然后原子化加载
nft -f /etc/nftables.conf
# 文件内容:
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
tcp dport { ssh, http, https } accept
ip saddr 1.2.3.4 drop
}
}
核心语法结构
nftables 的规则集四层结构:
ruleset(规则集)
└── table(表)
└── chain(链)
└── rule(规则)
# 命令模板
nft add table <地址族> <表名>
nft add chain <地址族> <表名> <链名> { type <类型> hook <钩子> priority <优先级> \; }
nft add rule <地址族> <表名> <链名> <匹配条件> <动作>
地址族(Address Family)
| 地址族 | 说明 |
|---|---|
ip |
IPv4(同 iptables) |
ip6 |
IPv6(同 ip6tables) |
inet |
IPv4 + IPv6 统一处理(最常用) |
arp |
ARP 协议 |
bridge |
桥接数据包 |
netdev |
网络设备层(ingress 钩子,性能最高) |
💡 最佳实践:绝大多数场景使用
inet地址族,一条规则同时覆盖 IPv4 和 IPv6。
链类型与钩子
| 类型 | 钩子(hook) | 对应 iptables 链 |
|---|---|---|
filter |
input |
INPUT |
filter |
forward |
FORWARD |
filter |
output |
OUTPUT |
nat |
prerouting |
PREROUTING |
nat |
postrouting |
POSTROUTING |
nat |
input |
INPUT(NAT) |
nat |
output |
OUTPUT(NAT) |
route |
output |
OUTPUT(路由) |
快速上手
1. 创建规则集脚本
创建一个标准防火墙规则集文件 /etc/nftables.conf:
#!/usr/sbin/nft -f
# 清空所有规则
flush ruleset
# ===== 创建 filter 表 =====
table inet filter {
# ===== INPUT 链:入站流量 =====
chain input {
type filter hook input priority filter; policy drop;
# 允许回环接口
iif lo accept
# 允许已建立的和相关的连接
ct state established,related accept
# 允许 SSH
tcp dport 22 accept
# 允许 HTTP/HTTPS
tcp dport { 80, 443 } accept
# 允许 Ping
icmp type echo-request accept
icmpv6 type echo-request accept
# 允许 DHCP(IPv6 需要)
udp dport dhcpv6-client accept
# 记录被拒绝的包
log prefix "nftables-drop: " level info
}
# ===== FORWARD 链:转发流量(通常关闭)=====
chain forward {
type filter hook forward priority filter; policy drop;
}
# ===== OUTPUT 链:出站流量 =====
chain output {
type filter hook output priority filter; policy accept;
}
}
2. 加载规则集
# 首次加载
sudo nft -f /etc/nftables.conf
# 验证
sudo nft list ruleset
3. 使规则持久化(自启动)
# Debian / Ubuntu
sudo systemctl enable nftables
# 规则文件自动加载规则:
# /etc/nftables.conf 是默认加载文件
# 如果修改了规则,重新加载:
sudo systemctl restart nftables
# 或手动刷新
sudo nft -f /etc/nftables.conf
⚠️ 重要:
nft -f不会自动清除旧规则。如果要全量替换,在规则文件开头执行flush ruleset清空所有现有规则。
常用操作
查看规则集
# 查看完整的规则集
sudo nft list ruleset
# 查看指定表
sudo nft list table inet filter
# 查看指定链
sudo nft list chain inet filter input
# 查看规则集概要
sudo nft list ruleset | head -30
添加规则(交互式)
# 添加规则到现有链
sudo nft add rule inet filter input tcp dport 3306 accept
# 在指定位置插入规则(插入到第 3 条)
sudo nft insert rule inet filter input position 3 tcp dport 8080 accept
# 在链开头插入规则
sudo nft insert rule inet filter input tcp dport 8443 accept
删除规则
# 查看规则句柄(handle)
sudo nft --handle list ruleset
# 输出示例(注意每行末尾的 handle):
# chain input {
# ...
# tcp dport 22 accept # handle 4
# tcp dport 80 accept # handle 5
# }
# 通过 handle 删除
sudo nft delete rule inet filter input handle 5
# 清空链中所有规则
sudo nft flush chain inet filter input
# 清空表中所有规则
sudo nft flush table inet filter
保存与恢复
# 保存当前规则集到文件
sudo nft list ruleset > /etc/nftables.conf
# 从文件加载规则集
sudo nft -f /etc/nftables.conf
高级功能
1. 集合(Sets)—— 批量匹配的利器
匿名集合(直接在规则中定义):
# 一次匹配多个端口
nft add rule inet filter input tcp dport { 80, 443, 8080 } accept
# 一次匹配多个 IP
nft add rule inet filter input ip saddr { 10.0.0.1, 192.168.1.100 } accept
命名集合(可动态增删,推荐):
# 创建集合
nft add set inet filter whitelist { type ipv4_addr \; }
# 添加元素
nft add element inet filter whitelist { 192.168.1.100, 10.0.0.5 }
# 使用集合
nft add rule inet filter input ip saddr @whitelist accept
# 查看集合内容
nft list set inet filter whitelist
# 删除元素
nft delete element inet filter whitelist { 10.0.0.5 }
# 结合端口范围
nft add set inet filter web_ports { type inet_service \; }
nft add element inet filter web_ports { 80, 443, 8080, 8443 }
💡 为什么集合重要:与传统 iptables 每条规则串联相比,集合使用哈希表实现 O(1) 查找。如果你有 1000 个 IP 要屏蔽,集合比 1000 条 iptables 规则快几个数量级。
2. 映射(Maps)—— 规则->动作的动态表
# 创建映射:不同的 IP 跳转到不同的动作
nft add map inet filter jump_map { type ipv4_addr : verdict \; }
# 添加映射条目
nft add element inet filter jump_map {
192.168.1.10 : accept,
10.0.0.0/8 : drop,
203.0.113.0/24 : jump non_trusted
}
# 应用映射
nft add rule inet filter input ip saddr vmap @jump_map
3. 流量限速
# 限制 SSH 连接速率
nft add rule inet filter input \
tcp dport 22 \
ct state new \
limit rate 10/minute \
accept
# 限制 ICMP 速率
nft add rule inet filter input \
icmp type echo-request \
limit rate 5/second burst 10 packets \
accept
# 限制并发连接数
nft add rule inet filter input \
tcp dport 22 \
ct count 10 \
drop
4. NAT(网络地址转换)
# 创建 NAT 表
table ip nat {
# SNAT / MASQUERADE(共享上网)
chain postrouting {
type nat hook postrouting priority srcnat;
oif eth0 masquerade
}
# DNAT / 端口转发
chain prerouting {
type nat hook prerouting priority dstnat;
# 将 8080 转发到内网 10.0.0.2:80
tcp dport 8080 dnat to 10.0.0.2:80
}
}
# 还需要 FORWARD 链放行
table inet filter {
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
tcp dport 80 ip daddr 10.0.0.2 accept
}
}
5. 日志记录
# 规则前加日志(规则会继续匹配)
nft add rule inet filter input \
tcp dport 22 \
log prefix "SSH-ACCESS: " level info accept
# 在被拒绝的包上记录日志
nft add rule inet filter input \
log prefix "NFT-DROP: " level info \
drop
# 限制日志速率(避免日志洪水)
nft add rule inet filter input \
log prefix "NFT-DROP: " level info limit rate 3/second \
drop
6. 实时监控(类似 tcpdump 体验)
# 实时追踪新添加的规则
sudo nft monitor
# 只追踪特定表
sudo nft monitor table inet filter
# 追踪所有事件
sudo nft monitor | head -50
规则集文件最佳实践
一个完整的防火墙配置示例 /etc/nftables.conf:
#!/usr/sbin/nft -f
# 原子化加载:要么全部生效,要么全部回滚
flush ruleset
# ==================== FILTER 表 ====================
table inet filter {
# 白名单集合
set whitelist {
type ipv4_addr
elements = { 192.168.1.0/24, 10.0.0.0/8 }
}
# 黑名单集合(可从外部脚本动态更新)
set blacklist {
type ipv4_addr
flags timeout
}
# 限速集合
set ssh_attempts {
type ipv4_addr
flags timeout
timeout 60s
}
chain input {
type filter hook input priority filter; policy drop;
# 回环
iif lo accept
# 已建立的连接
ct state established,related accept
# 白名单
ip saddr @whitelist accept
# SSH(限速)
tcp dport 22 {
ct state new add @ssh_attempts { ip saddr }
ct state new limit rate over 5/minute drop
accept
}
# Web 服务
tcp dport { 80, 443 } accept
# Ping
icmp type echo-request accept
# 黑名单
ip saddr @blacklist drop
# 记录并丢弃其他
log prefix "NFT-DROP: " level info
drop
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
# ==================== NAT 表 ====================
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat;
oif eth0 masquerade
}
chain prerouting {
type nat hook prerouting priority dstnat;
# 端口转发
tcp dport 8080 dnat to 10.0.0.2:80
}
}
实际场景
场景 1:Web 服务器
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iif lo accept
ct state established,related accept
# Web 服务
tcp dport { 80, 443 } accept
# SSH(限制为仅内网或特定 IP)
ip saddr { 192.168.1.0/24, 10.0.0.0/8 } tcp dport 22 accept
# Ping
icmp type echo-request accept
icmpv6 type echo-request accept
# 监控
tcp dport 9100 ip saddr 10.0.0.0/8 accept # node_exporter
ct state invalid drop
log prefix "WEB-DROP: " level info
drop
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
场景 2:家庭网关 / 路由器
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
iif lo accept
ct state established,related accept
# 允许内网访问本机
iif br0 accept
# 外网只允许 SSH
iif ppp0 tcp dport 22 accept
# Ping
icmp type echo-request accept
drop
}
chain forward {
type filter hook forward priority filter; policy drop;
# 内网 -> 外网(允许)
iif br0 oif ppp0 accept
# 外网 -> 内网(仅已建立连接)
iif ppp0 oif br0 ct state established,related accept
}
chain output {
type filter hook output priority filter; policy accept;
}
}
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat;
oif ppp0 masquerade
}
chain prerouting {
type nat hook prerouting priority dstnat;
# 端口转发:外网 8080 → 内网 192.168.1.100:80
iif ppp0 tcp dport 8080 dnat to 192.168.1.100:80
}
}
动态更新黑名单
nftables 的最大优势之一是可以动态更新集合而不重新加载整个规则集:
# 脚本:封禁 IP
#!/bin/bash
IP=$1
nft add element inet filter blacklist { $IP }
echo "Banned $IP"
# 脚本:解封 IP
#!/bin/bash
IP=$1
nft delete element inet filter blacklist { $IP }
echo "Unbanned $IP"
# 脚本:从 fail2ban 同步到 nftables 集合
fail2ban-client get ssh ban
# 将封禁的 IP 写入 nftables 集合
与 Fail2ban 集成:
# /etc/fail2ban/action.d/nftables.conf
[Definition]
actionban = nft add element inet filter blacklist { <ip> }
actionunban = nft delete element inet filter blacklist { <ip> }
性能优化建议
# 1. 使用集合而不是多条独立规则
# ❌ 差:多条规则
nft add rule inet filter input ip saddr 10.0.0.1 drop
nft add rule inet filter input ip saddr 10.0.0.2 drop
# ✅ 好:一个集合
nft add set inet filter bad_ips { type ipv4_addr \; }
nft add element inet filter bad_ips { 10.0.0.1, 10.0.0.2 }
nft add rule inet filter input ip saddr @bad_ips drop
# 2. 映射(vmap)替代多条判断链
# ✅ 好:一个映射决定动作
nft add rule inet filter input ip saddr vmap @policy_map
# 3. 使用 inet 替代 ip + ip6
# 一条 inet 规则 = IPv4 + IPv6,减少重复
# 4. 常用规则放在链前面
# 已建立的连接放最前面(匹配了就直接 accept,不再检查后续规则)
调试与查错
# 查看规则匹配计数
sudo nft list ruleset -n
# 每条规则前的计数器显示匹配的包数和字节数
# chain input {
# type filter hook input priority filter; policy drop;
# iif lo accept # packets 1423 bytes 234567
# ct state established,related accept # packets 56789 bytes 12345678
# tcp dport 22 accept # packets 321 bytes 19234
# }
# 实时追踪数据包
sudo nft monitor trace
# 查看系统日志中的 nftables 记录
sudo journalctl -k -f | grep NFT
# 检查规则集是否有语法错误
sudo nft -c -f /etc/nftables.conf # -c 只检查不加载
# 查看 nft 版本功能
nft --help
从 iptables 迁移到 nftables
自动转换工具
# 将当前的 iptables 规则转译为 nftables 语法
sudo iptables-translate -A INPUT -p tcp --dport 80 -j ACCEPT
# nft add rule ip filter INPUT tcp dport 80 accept
# 批量转换整个规则集
sudo iptables-save > rules.v4
sudo iptables-restore-translate -f rules.v4 > rules.nft
# 将 ip6tables 规则也转译
sudo ip6tables-save > rules.v6
sudo ip6tables-restore-translate -f rules.v6 >> rules.nft
对应关系速查
| iptables | nftables |
|---|---|
iptables -A INPUT -p tcp --dport 80 -j ACCEPT |
nft add rule inet filter input tcp dport 80 accept |
iptables -A INPUT -s 1.2.3.4 -j DROP |
nft add rule inet filter input ip saddr 1.2.3.4 drop |
iptables -A FORWARD -i eth0 -o eth1 -j ACCEPT |
nft add rule inet filter forward iif eth0 oif eth1 accept |
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE |
nft add rule ip nat postrouting oif eth0 masquerade |
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.2:80 |
nft add rule ip nat prerouting tcp dport 80 dnat to 10.0.0.2:80 |
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT |
nft add rule inet filter input ct state established,related accept |
常见问题
Q:nftables 和 iptables 能共存吗?
可以,但不推荐。两者操作的是不同的内核模块(nf_tables vs ip_tables),同时使用会导致规则混乱、难以调试。
# 检查是否有 legacy iptables 规则
sudo iptables-legacy -L -n
# 禁用 iptables 确保只使用 nftables
sudo systemctl mask iptables
sudo systemctl mask ip6tables
Q:我的发行版默认 nftables,但 docker 用 iptables 怎么办?
Docker 默认使用 iptables 命令,现代发行版的 iptables 通常已经是指向 nftables 后端兼容层 iptables-nft,所以正常工作。
# 确认你的 iptables 是 nft 模式
iptables --version
# iptables v1.8.7 (nf_tables) ← OK,这是 nftables 兼容层
Q:nftables 规则不生效,哪里排查?
- 检查是否已加载:
nft list ruleset - 检查默认策略:确认 input 链的策略是
drop还是accept - 检查优先级:多个表链的优先级冲突
- 查看日志:
journalctl -k -f | grep NFT - 临时放行调试:
nft add rule inet filter input accept
Q:规则重启后没了?
# 确保服务已启用
sudo systemctl enable nftables
# 确保 /etc/nftables.conf 存在且包含你的规则
# 或者把规则保存到该文件
sudo nft list ruleset > /etc/nftables.conf
总结
| 操作 | 命令 |
|---|---|
| 查看所有规则 | sudo nft list ruleset |
| 查看特定表 | sudo nft list table inet filter |
| 添加规则 | sudo nft add rule inet filter input tcp dport 80 accept |
| 删除规则 | sudo nft delete rule inet filter input handle 5 |
| 添加集合 | nft add set inet filter blacklist { type ipv4_addr \; } |
| 添加集合元素 | nft add element inet filter blacklist { 1.2.3.4 } |
| 加载规则文件 | sudo nft -f /etc/nftables.conf |
| 保存规则 | sudo nft list ruleset > /etc/nftables.conf |
| 实时监控 | sudo nft monitor |
| 清空所有规则 | sudo nft flush ruleset |
nftables 是 Linux 防火墙的未来。如果你在搭建新系统,直接使用 nftables 就好,不要再学 iptables 了。它的结构化语法、集合映射、原子化加载和更好的性能,每一项都是 iptables 缺失了十多年的能力。学习曲线虽然比 ufw 陡,但带来的控制力也远超 ufw。