概述

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 规则不生效,哪里排查?

  1. 检查是否已加载nft list ruleset
  2. 检查默认策略:确认 input 链的策略是 drop 还是 accept
  3. 检查优先级:多个表链的优先级冲突
  4. 查看日志journalctl -k -f | grep NFT
  5. 临时放行调试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。