技术指南

nftables 防火墙上线:保住 SSH、验证规则并安全回滚

用 nftables 建立可检查、可回滚的主机入站策略,并在不触碰宿主机防火墙的隔离网络中验证双栈与端口边界。

防火墙上线不是抄一组端口规则,而是一次可能切断管理通道的网络变更。先确认谁在监听、流量经过 INPUT 还是 FORWARD、当前规则由谁维护,再把候选规则当作文件检查、加载和回滚。本文只讨论直接维护 nftables 的主机入站边界;云安全组、容器发布、VPN 与上游防护仍要分别验证。

先画出实际流量边界

在变更窗口保存 UTC 时间、命令版本和输出;先识别管理器与容器后端,不要直接粘贴修改命令
ip -brief address
ip route show
ip -6 route show
ss -lntup
sudo nft --handle list ruleset
iptables --version
systemctl is-active ufw firewalld nftables 2>/dev/null || true
docker ps --format 'table {{.Names}}\t{{.Ports}}' 2>/dev/null || true

监听地址回答进程接在哪个接口,路由回答回包从哪里走,nftables 规则回答内核在哪个 hook 作决定。云安全组在主机外,Docker 发布流量通常经过转发与地址转换;它们不会因为主机 INPUT 链看起来整洁就自动符合预期。

对象主要检查点不能替代的检查
宿主机服务监听地址、INPUT 链、IPv4/IPv6云侧入站策略与外部探测
容器发布端口绑定地址、FORWARD/NAT、Docker 后端宿主机 INPUT 规则
VPN 管理入口虚拟接口、来源网段、回程路由公网 SSH 放行
云安全组实例/网卡绑定、来源与端口主机规则和服务监听

一张表只由一个边界负责

nftables 可以在同一 hook 上挂多条 base chain。某条链给出 accept,并不保证后续链不会 drop;同优先级链的顺序也不能依赖。UFW、firewalld、Docker、Kubernetes 或代理软件可能同时维护规则,因此先确定所有者,再为人工维护的表使用独立名称。不要清空其他组件的表,也不要直接改它们生成的规则。

把候选规则写成一个可审阅文件

下面示例假设已经创建只归本次变更所有的 `inet vpscope_host` 表。`inet` 同时处理 IPv4 与 IPv6;文档地址必须替换为真实管理来源。示例允许回环、已建立连接、ICMP/ICMPv6、限定来源的 SSH 以及公开 Web 端口,其余入站由 chain policy 丢弃。

保存为候选文件;先用 `sudo nft add table inet vpscope_host` 建立空的未挂钩表,再进行检查
define mgmt_v4 = 203.0.113.10
define mgmt_v6 = 2001:db8:100::10
define ssh_port = 22

flush table inet vpscope_host

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

    iifname "lo" counter accept
    ct state invalid counter drop
    ct state established,related counter accept
    meta l4proto { icmp, ipv6-icmp } counter accept

    ip saddr $mgmt_v4 tcp dport $ssh_port counter accept
    ip6 saddr $mgmt_v6 tcp dport $ssh_port counter accept
    tcp dport { 80, 443 } counter accept
    counter
  }
}

允许 ICMPv6 不是装饰项。邻居发现、路由器通告、Packet Too Big 和多类错误消息参与地址配置与路径 MTU;只允许 echo 或一律丢弃会造成间歇性 IPv6 故障。更细的 ICMPv6 策略应按 RFC 4890 和实际主机角色逐类验证,而不是照搬 IPv4 的 ping 规则。

语法检查通过不等于策略正确

空表没有 hook,不会过滤流量;检查候选文件中是否还留有文档地址或错误 SSH 端口
sudo nft add table inet vpscope_host
sudo nft --check --file ./vpscope-host.nft
sudo nft --stateless list table inet vpscope_host
rg -n '203\.0\.113|2001:db8|ssh_port = 22' ./vpscope-host.nft

`nft --check --file` 只验证命令是否可接受,不验证你的来源 IP、端口、接口和业务假设。上线前逐项对照当前 SSH 连接、实际 `ss` 输出、云安全组和容器端口;若管理地址会变化,应先设计 VPN、跳板机或可审计的集合更新流程。

回滚只恢复自己拥有的表

在加载候选规则前生成、检查并保护回滚文件;不要把含拓扑信息的文件长期留在共享目录
work=$(mktemp -d)
chmod 0700 "$work"
{
  printf 'flush table inet vpscope_host\n'
  sudo nft --stateless list table inet vpscope_host
} > "$work/rollback.nft"
sudo nft --check --file "$work/rollback.nft"
# 失败时:sudo nft --file "$work/rollback.nft"

回滚命令也必须在隔离环境或维护窗口演练。若要定时自动回滚,先验证调度器确实会执行、取消动作可审计、重启后语义明确,并保留提供商控制台;不要临时拼一个从未运行过的 `at` 或 systemd 命令就把它当成保护。

一次加载,再从外部验证

保持原 SSH 会话不动;从第二个终端和独立网络验证后再结束变更窗口
sudo nft --file ./vpscope-host.nft
sudo nft --handle list table inet vpscope_host
ss -lntup

`nft -f` 会把文件中的命令作为一个事务提交,避免逐条 shell 命令留下半配置状态。原子提交只解决加载一致性,不会修正错误策略;验证失败时立即加载已检查的回滚文件,并记录候选版本、失败路径和 UTC 时间。

按允许、拒绝和回程三类验收

测试从哪里执行通过证据
新的 SSH 连接批准的管理来源完成握手与认证,旧会话仍可用
公开 80/443至少一个外部网络TCP/TLS/HTTP 均符合预期
未开放端口外部网络无法建立连接,规则末端 counter 增长
IPv6 路径具备 IPv6 的外部网络邻居发现、PMTU 与目标服务正常
重启后规则维护窗口重启目标表、服务与远程入口均恢复

规则 counter 证明某条路径命中过,不能单独证明请求来自攻击者或应用成功。日志也应限速并避开敏感载荷;先用外部探测证明症状,再用 counter、连接跟踪和服务日志解释原因。

容器发布端口要单独验收

Docker 当前文档明确说明它会为桥接网络的隔离和端口发布创建防火墙规则,并要求不要修改 Docker 自己创建的规则。发布端口可能绕开 UFW 常用的 INPUT/OUTPUT 路径;敏感服务优先绑定明确的主机地址,并从外部实际探测。更换 Docker 防火墙后端或关闭规则管理会改变转发行为,必须作为独立迁移处理。

同时检查容器声明、实际监听与内核规则;`0.0.0.0` 或 `[::]` 发布需要明确的公网暴露决策
docker info
docker ps --format 'table {{.Names}}\t{{.Ports}}'
ss -lntup
sudo nft --handle list ruleset

持久化要经过重启验收

发行版的 nftables service、配置入口和其他管理器可能不同。先用 `systemctl cat nftables` 与发行版文档确认实际加载文件,再让已通过外部验证的同一候选文件进入配置管理。重启测试要检查规则、监听、IPv4/IPv6、新 SSH 会话和容器网络;只看到 service active 不能证明策略已恢复。

本次隔离演练验证了什么

本批在独立 network namespace `vpscope-fw11` 中创建临时 veth、文档地址和三个 Python 监听端口,只在命名空间内加载 `inet vpscope_test` 表。2222 与 8080 可达,9090 被默认策略丢弃,IPv4 ping 与 IPv6 邻居发现/echo 正常;counter 分别记录允许与未匹配流量。故意损坏的候选文件被 `nft --check --file` 拒绝,活动规则的无状态哈希保持不变。

演练结束后已删除命名空间、veth 与监听进程,宿主机规则未被修改。它证明的是示例的语法、双栈控制流量、允许/拒绝路径、counter 和失败不生效;没有证明真实 VPS 的云安全组、SSH 来源、Docker 后端、持久化、重启或提供商控制台可用,这些仍需在目标环境逐项验收。

上线前的闭环验收

  • 已记录监听、地址、路由、现有规则、管理器与容器发布端口。
  • 提供商控制台可用,原会话保留,第二个独立会话能验证新连接。
  • 人工表有唯一所有者;没有清空或修改 Docker、UFW、firewalld 等组件的表。
  • 候选和回滚文件都通过 `nft --check --file`,文档地址、端口和接口已替换。
  • 规则通过单次 `nft --file` 加载,允许、拒绝、IPv4/IPv6 与回程路径均有外部证据。
  • counter 与日志只用于解释命中,不把扫描或高频自动等同于攻击结论。
  • 容器发布和云安全组独立核对,公网绑定是明确决策。
  • 持久化按目标发行版配置,并在维护窗口完成重启与远程恢复验收。

返回知识库