nftables 是 Linux 内核中的包过滤框架,也是现代 Debian、Ubuntu 等发行版用于替代 iptables 的主要防火墙工具。它可以统一管理 IPv4、IPv6、端口过滤、网络地址转换和数据包转发规则。

本文以普通服务器的主机防火墙为例,介绍 nftables 的安装、备份、端口屏蔽、默认拒绝入站、规则验证、持久化和回滚方法。

远程修改防火墙具有断开 SSH 连接的风险。不要直接复制规则到生产服务器执行;先确认实际 SSH 端口,并保留第二个 SSH 会话或云厂商控制台作为恢复通道。

基本概念

nftables 的常用层级如下:

1
2
3
4
规则集 ruleset
└── 表 table
    └── 链 chain
        └── 规则 rule
  • table:保存一组相关的链和规则
  • chain:按流量方向组织规则,例如入站、转发和出站
  • rule:匹配流量并执行 accept、drop 等动作
  • hook:将链挂载到内核网络处理流程
  • policy:数据包没有匹配任何规则时采用的默认动作

本文使用 table inet,其中的通用规则可以同时处理 IPv4 和 IPv6。只有使用 ip 或 ip6 条件时,规则才会限定为对应的协议族。

修改前检查

1. 确认当前身份和系统

1
2
id
cat /etc/os-release

本文命令适用于 Debian 和 Ubuntu,其他发行版的软件包名称、服务管理方式或默认配置可能不同。

2. 确认实际 SSH 端口

不要默认认为 SSH 一定使用 22 端口。查看 sshd 的有效配置:

1
sudo sshd -T | grep '^port '

也可以检查当前监听端口:

1
sudo ss -lntp | grep sshd

后续示例使用 22,如果服务器使用其他端口,必须替换为实际值。

3. 检查现有防火墙和网络服务

1
2
3
4
sudo nft list ruleset
sudo systemctl status nftables --no-pager
sudo systemctl status ufw --no-pager
sudo systemctl status firewalld --no-pager

同一台服务器不应由多个防火墙管理工具同时维护同一套规则。使用 nftables 前,应先弄清楚规则是否由 UFW、firewalld、Docker、Fail2ban、Kubernetes 或云平台代理生成。

Docker、Podman、Fail2ban 和容器编排工具可能动态创建防火墙规则。执行 flush ruleset 会清空整个规则集,可能导致容器网络、端口映射或封禁规则失效。

安装 nftables

1
2
3
sudo apt update
sudo apt install -y nftables
sudo systemctl enable nftables

查看版本:

1
nft --version

安装后不必立即启动服务。建议先备份现有规则、完成配置并通过语法检查,再加载规则和启动服务。

备份现有配置

1. 备份配置文件

如果 /etc/nftables.conf 已存在:

1
sudo cp -a /etc/nftables.conf "/etc/nftables.conf.bak.$(date +%F-%H%M%S)"

2. 制作可完整替换的活动规则恢复文件

1
2
3
sudo sh -c 'printf "%s\n" "flush ruleset" > /root/nftables-safe.nft'
sudo nft list ruleset | sudo tee -a /root/nftables-safe.nft > /dev/null
sudo nft -c -f /root/nftables-safe.nft

nft list ruleset 的输出可作为 nft -f 的输入,但它通常由 table 声明组成;在已有规则集上直接加载会因为同名对象已存在而不能保证恢复。文件首行的 flush ruleset 明确使恢复文件先清空当前规则集、再完整加载导出的规则。

配置文件备份和活动规则备份并不完全相同。其他服务动态添加的规则可能只存在于当前规则集中,并未写入 /etc/nftables.conf。flush ruleset 也会清掉这些动态规则,因此 Docker、Podman、Fail2ban、Kubernetes、VPN、NAT 或 UFW/firewalld 管理的主机,不能把这种全量恢复文件当作无影响的常规操作;应先设计与现有管理者兼容的恢复方案。

远程操作防锁死流程

通过 SSH 修改防火墙时,建议执行以下保护措施:

  1. 保留当前 SSH 会话,不要提前退出
  2. 再打开一个 SSH 会话用于验证新连接
  3. 确认云厂商控制台、VNC 或串行终端可以使用
  4. 加载规则前安排定时回滚
  5. 新 SSH 会话验证成功后再取消回滚任务

使用 systemd-run 安排自动回滚

先制作并验证一份已经确认可用的完整替换恢复文件:

1
2
3
sudo sh -c 'printf "%s\n" "flush ruleset" > /root/nftables-safe.nft'
sudo nft list ruleset | sudo tee -a /root/nftables-safe.nft > /dev/null
sudo nft -c -f /root/nftables-safe.nft

只有最后一条检查命令返回 0 后,才安排 5 分钟后恢复。--on-active=5m 会建立 transient .timer 和它触发的 .service;使用固定单元名便于精确取消:

1
2
3
4
5
sudo systemd-run \
  --unit=nftables-rollback \
  --on-active=5m \
  --property=Type=exec \
  /usr/sbin/nft -f /root/nftables-safe.nft

查看任务:

1
systemctl status nftables-rollback.timer --no-pager

确认新规则和新 SSH 连接都正常后,停止 timer 和尚未触发的 service:

1
2
sudo systemctl stop nftables-rollback.timer nftables-rollback.service
sudo systemctl reset-failed nftables-rollback.timer nftables-rollback.service

再检查 timer 已不存在或处于 inactive:

1
2
systemctl list-timers --all | grep nftables-rollback
systemctl status nftables-rollback.timer --no-pager

自动回滚会执行恢复文件中的 flush ruleset,然后重建其保存时的整个规则集。执行前必须确认文件存在、能通过 nft -c -f,并理解对容器、封禁、NAT 和其他防火墙管理工具的影响。

安全加载配置

每次修改 /etc/nftables.conf 后,先执行语法检查:

1
sudo nft -c -f /etc/nftables.conf

命令没有输出且退出状态为 0,表示语法检查通过。然后加载配置:

1
sudo nft -f /etc/nftables.conf

查看实际规则:

1
sudo nft list ruleset

确认规则和远程连接正常后,再启动或重启服务,使系统启动时自动加载配置:

1
sudo systemctl enable --now nftables

如果服务已经运行,修改配置后执行:

1
sudo systemctl reload nftables

若当前发行版的 nftables 服务不支持 reload,再使用:

1
sudo systemctl restart nftables

不要在没有检查结果的情况下连续执行 nft -f 和 systemctl restart。前者已经会立即加载规则,后者只是再次从配置文件加载。

示例一:仅屏蔽指定端口

下面的规则默认允许其他流量,仅屏蔽入站 TCP 和 UDP 8000 端口。由于未使用 ip 或 ip6 限定,它会同时作用于 IPv4 和 IPv6。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        tcp dport 8000 drop
        udp dport 8000 drop
    }
}

将内容保存到 /etc/nftables.conf 后,先检查再加载:

1
2
3
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo nft list chain inet filter input

这个示例适合只需要阻止单个服务端口的简单主机。如果当前系统已有复杂规则,不应直接使用 flush ruleset 覆盖,而应将规则合并到现有规则集。

示例二:默认拒绝入站流量

下面是更常见的服务器策略:

  • 默认拒绝入站流量
  • 允许本机回环流量
  • 允许已建立和相关连接
  • 允许必要的 IPv4 ICMP 和 IPv6 ICMP
  • 允许 SSH、HTTP、HTTPS 和示例端口 8000
  • 默认允许本机主动发出的流量
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy drop;

        ct state invalid drop
        ct state established,related accept
        iifname "lo" accept

        ip protocol icmp accept
        meta l4proto ipv6-icmp accept

        tcp dport { 22, 80, 443, 8000 } accept
        udp dport 8000 accept
    }

    chain forward {
        type filter hook forward priority filter; policy drop;
    }

    chain output {
        type filter hook output priority filter; policy accept;
    }
}

先将 22 替换为实际 SSH 端口,再执行语法检查:

1
sudo nft -c -f /etc/nftables.conf

安排自动回滚并保留当前 SSH 会话,然后加载配置:

1
2
sudo nft -f /etc/nftables.conf
sudo nft list ruleset

此时使用第二个终端重新建立 SSH 连接,并测试需要开放的服务端口。确认一切正常后,再取消自动回滚。

关于 forward 链

forward 链处理经过本机转发的数据包。普通 Web 或 SSH 服务器通常不需要转发,因此可以使用 policy drop。

如果服务器承担以下职责,不要直接使用该配置:

  • Docker 或 Podman 容器宿主机
  • Kubernetes 节点
  • 路由器或网关
  • VPN 转发节点
  • 使用 NAT 或端口转发的服务器

这些场景需要根据转发路径和相关服务生成的规则单独设计 forward 链。

临时添加规则

临时规则直接写入当前活动规则集,重启服务或重新加载 /etc/nftables.conf 后可能消失。

查看现有链和规则顺序

1
sudo nft -a list chain inet filter input

规则按顺序匹配。如果链前面已经有覆盖该流量的 accept 规则,把 drop 追加到链末尾可能不会生效。

在链开头插入屏蔽规则

1
2
sudo nft insert rule inet filter input tcp dport 8000 drop
sudo nft insert rule inet filter input udp dport 8000 drop

再次查看规则及其 handle:

1
sudo nft -a list chain inet filter input

删除规则时,将数字替换为实际的 handle:

1
sudo nft delete rule inet filter input handle 10

handle 可能在规则重新加载后发生变化,删除前应重新查询,不能长期保存并重复使用旧编号。

持久化规则

Debian 和 Ubuntu 的 nftables 服务通常从以下文件加载配置:

1
/etc/nftables.conf

确认配置语法正确后启用服务:

1
2
sudo nft -c -f /etc/nftables.conf
sudo systemctl enable --now nftables

验证服务状态和开机启用状态:

1
2
systemctl is-enabled nftables
systemctl status nftables --no-pager

重启服务器前,确保配置文件中的规则与当前已验证的活动规则一致。

回滚与恢复

1. 恢复配置文件备份

1
2
3
sudo cp -a /etc/nftables.conf.bak.YYYY-MM-DD-HHMMSS /etc/nftables.conf
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf

确认恢复成功后再重启服务:

1
sudo systemctl restart nftables

2. 恢复导出的活动规则

1
2
sudo nft -c -f /root/nftables-safe.nft
sudo nft -f /root/nftables-safe.nft

这里的 /root/nftables-safe.nft 必须是上文创建的、首行包含 flush ruleset 的文件。不要把未经包装的 nft list ruleset 输出当作“完整恢复”:它可以作为规则定义来源,但未先清空现有同名对象时并非可靠的替换操作。

3. 紧急清空规则

1
sudo nft flush ruleset

flush ruleset 会删除所有 nftables 表、链和规则。它可能暂时恢复远程连接,也可能破坏 Docker、Fail2ban、NAT、VPN 和容器网络,只应通过控制台或明确了解后果时作为紧急手段使用。

如果 /etc/nftables.conf 中仍有错误配置,重启 nftables 服务或服务器后,问题规则还会再次加载。

验证规则

查看完整规则集

1
sudo nft list ruleset

查看带 handle 的规则

1
sudo nft -a list ruleset

查看指定链

1
sudo nft list chain inet filter input

查看端口监听状态

1
sudo ss -lntup

防火墙允许某个端口并不代表端口一定可以访问,还需要对应服务正在监听正确的地址和端口。

从其他设备测试 TCP 端口

1
2
3
nc -vz SERVER_IP 22
nc -vz SERVER_IP 443
nc -vz SERVER_IP 8000

也可以使用:

1
curl --fail --show-error --head https://example.com/

HTTPS 测试应使用证书包含的域名,而不是直接请求 IP 地址。若 DNS 尚未切换,或要固定测试某个地址,可从另一台设备执行:

1
2
3
curl --fail --show-error --head \
  --resolve example.com:443:203.0.113.10 \
  https://example.com/

将 example.com 和 203.0.113.10 替换为真实证书名称和目标 IPv4 地址;测试 IPv6 时使用 --resolve example.com:443:[2001:db8::10]。--resolve 会保留 URL 中的域名,因此 TLS SNI 和 HTTP Host 仍与生产请求一致。

应从另一台设备测试,服务器本机访问自身不能完整反映外部入站规则是否正确。

查看服务日志

1
sudo journalctl -u nftables --no-pager -n 100

常见问题

规则加载时报语法错误

先执行:

1
sudo nft -c -f /etc/nftables.conf

根据输出的文件行号修正配置。不要为了“试一试”直接重启 nftables,否则可能在规则只加载了一部分或服务启动失败时留下不可预测的状态。

放行端口后仍无法访问

依次检查:

  • 应用是否正在监听该端口
  • 应用是否只监听 127.0.0.1
  • nftables 中是否有更早匹配的 drop 或 reject 规则
  • 云厂商安全组是否放行对应端口
  • 路由器或上级防火墙是否允许该流量
  • 域名是否解析到正确的 IPv4 和 IPv6 地址
  • 服务是否需要 TCP、UDP 或两者都需要

屏蔽规则没有生效

nftables 按规则顺序处理数据包。如果前面的规则已经接受流量,后面的 drop 不会再执行。使用以下命令检查顺序:

1
sudo nft -a list chain inet filter input

需要时使用 insert rule 将规则放到链前面,或重新整理永久配置中的规则顺序。

重启后规则消失

确认规则已经写入 /etc/nftables.conf,并检查服务是否启用:

1
2
systemctl is-enabled nftables
systemctl status nftables --no-pager

临时执行的 nft add rule 或 nft insert rule 不会自动写入配置文件。

Docker 容器网络异常

如果配置中包含 flush ruleset,加载时可能清除了 Docker 或其他容器运行时创建的动态规则。此时不要反复重启网络服务,应先确认容器运行时使用的是 iptables-nft 兼容层还是原生 nftables,并根据实际网络架构合并规则。

对容器宿主机,建议先导出完整规则集,明确 Docker 创建的表和链,再设计不会覆盖它们的独立 nftables 表。

nftables 与 UFW 或 firewalld 冲突

UFW 和 firewalld 可能通过 nftables 后端生成规则。即使你没有手动编写 nftables,它们也可能已经在维护规则集。不要同时手动覆盖规则和使用这些前端工具管理防火墙,应该选择一种主要管理方式。

注意事项

  • 修改远程服务器防火墙前,必须确认实际 SSH 端口并准备控制台恢复方式。
  • 永久配置加载前始终执行 nft -c -f /etc/nftables.conf。
  • flush ruleset 会清空所有规则,不适合直接用于已有复杂网络配置的服务器。
  • table inet 可以同时处理 IPv4 和 IPv6,但 ip 与 ip6 条件只匹配各自协议族。
  • 云安全组、系统防火墙和应用监听状态共同决定端口是否可以访问。
  • 防火墙只能控制网络流量,不能替代系统更新、强密码、密钥登录和服务自身的身份验证。