nftables 替代 iptables 写防火墙(统一 IPv4/IPv6 + 现代语法)

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)。
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。