iptables 用了 20 年但有几个老问题:IPv4 / IPv6 / arptables / ebtables
四套不同命令、规则插入慢、语法古老。nftables 是它的现代继任者:
- 单一
nft命令统一所有协议 - 表达式 + 集合(set / map)让规则更短
- 增量加规则不重建整张表,性能好
- 类似 iptables 的 chains / rules,迁移有学习成本但不大
装 + 启用
sudo apt install -y nftables
sudo systemctl enable --now nftables
Debian 11+ / Ubuntu 22.04+ 默认就有。
一个完整的服务器防火墙
/etc/nftables.conf:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
# 允许 SSH 的源(管理 IP 段)
set admin_ips {
type ipv4_addr
elements = { 192.0.2.10, 198.51.100.0/24 }
}
chain input {
type filter hook input priority filter; policy drop;
# 1. 已建立连接 / 相关连接放行
ct state established,related accept
ct state invalid drop
# 2. 本地 loopback
iif lo accept
# 3. ICMP / ICMPv6(ping、路径 MTU 探测等)
ip protocol icmp accept
meta nfproto ipv6 icmpv6 accept
# 4. SSH 仅允许 admin_ips
tcp dport 22 ip saddr @admin_ips accept
# 5. 公开服务
tcp dport { 80, 443 } accept
# 6. WireGuard
udp dport 51820 accept
# 7. 日志后丢弃(限速避免刷屏)
limit rate 5/minute log prefix "nft drop input: "
counter drop
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
启用:
sudo nft -c -f /etc/nftables.conf # 语法检查
sudo systemctl restart nftables
sudo nft list ruleset
关键语法点
table inet ...中inet表示同时处理 IPv4 + IPv6(这是 nftables 最大优势)policy drop= 默认拒绝(白名单模式)ct state established,related accept= 连接跟踪放行set admin_ips= 命名集合,规则里@admin_ips引用,
改 IP 不动规则dport { 80, 443 }= 内联集合limit rate 5/minute log ...= 限速日志
命令行操作(运行时)
# 看现有规则
sudo nft list ruleset
sudo nft list table inet filter
# 给集合加 IP(不需要重写整套规则)
sudo nft add element inet filter admin_ips '{ 203.0.113.5 }'
sudo nft delete element inet filter admin_ips '{ 198.51.100.0/24 }'
# 加一条临时规则
sudo nft add rule inet filter input tcp dport 8080 accept
# 删某条规则
sudo nft -a list ruleset # -a 显示 handle 编号
sudo nft delete rule inet filter input handle 12
NAT 表(让内网通过本机出公网)
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat;
}
chain postrouting {
type nat hook postrouting priority srcnat;
# 内网 10.0.0.0/24 出口走 eth0 做 masquerade
ip saddr 10.0.0.0/24 oifname "eth0" masquerade
}
}
# 端口转发:把 80 转给内网 10.0.0.5
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat;
iifname "eth0" tcp dport 80 dnat to 10.0.0.5:80
}
}
限速 / 防 DDoS
chain input {
type filter hook input priority filter; policy drop;
# 新 SSH 连接限速:每分钟同 IP 最多 6 次
tcp dport 22 ct state new \
limit rate over 6/minute \
counter drop
# SYN flood 防护
tcp flags syn tcp option maxseg size 1-535 drop
tcp flags & (syn|rst|ack) == syn \
limit rate 100/second burst 50 packets accept
# ...其它规则
}
集合 + map 高级用法
# 不同源 IP → 不同处理
map verdict_map {
type ipv4_addr : verdict
elements = {
192.0.2.10 : accept,
198.51.100.20 : drop,
}
}
chain input {
ip saddr vmap @verdict_map
}
持久化
# 当前规则保存到 /etc/nftables.conf
sudo nft list ruleset > /etc/nftables.conf
# 或者用 systemd
sudo systemctl restart nftables # 会读 /etc/nftables.conf
服务重启 / 机器重启后 /etc/nftables.conf 被加载。
从 iptables 迁移
# 看现有 iptables 规则
sudo iptables-save > /tmp/v4.rules
sudo ip6tables-save > /tmp/v6.rules
# 转 nftables
sudo iptables-restore-translate -f /tmp/v4.rules > /tmp/v4.nft
sudo ip6tables-restore-translate -f /tmp/v6.rules > /tmp/v6.nft
# 看生成的,决定要不要手动整理(自动转出来语法生硬)
实际生产建议手写 nftables,不直接转。iptables 规则积累的"历史包袱"
不该带进来。
ufw / firewalld 怎么办
它们底层会用 iptables 或 nftables(取决于 distro 版本)。
不要 ufw / firewalld + 手写 nftables 混用 —— 不同工具会互相覆盖。
选一个:
- 简单 / 不需要进阶规则:ufw(前面那篇)
- 需要 NAT / 限速 / map / 大规模规则:直接 nftables
调试
# 实时看哪条规则被命中(packet/byte count)
sudo nft list ruleset | grep counter
# 看 drop 日志
sudo journalctl -k -f | grep 'nft drop'
# tcpdump 抓没通过的包
sudo tcpdump -i eth0 -nn 'host 1.2.3.4'
踩过的坑
- 把自己 ban 了:从 console / out-of-band 进,
nft flush ruleset清空,
重新写规则。提前准备一个 5 分钟自动恢复脚本:
bash (sleep 300 && sudo nft flush ruleset) & - iptables-nft(在 RHEL 8+ / Debian 11+)让 iptables 命令实际写
nftables。这是过渡兼容;新写规则直接用 nft。 inet表的规则对 IPv4 和 IPv6 都生效;如果你 IPv6 没用,规则也会
消耗 conntrack。可以分别建ip filter+ip6 filter而不是inet。- container(Docker / K8s)默认绕过 nftables。Docker 会自己写
iptables / nftables 规则,与你的规则可能冲突。生产容器集群单独
规划网络策略(Calico / Cilium)。
登录后参与评论。