跳到主要内容

UFW 防火墙与防锁死流程

目标策略

公网: 80/tcp、443/tcp 允许;22/tcp 不普遍开放
tailscale0:22/tcp 允许
默认入站: deny
默认出站: allow

UFW 是 netfilter 的易用前端。规则描述的是允许或拒绝的流量,不负责启动相应服务;开放 80 并不会自动出现网站。

为什么必须分阶段

直接设置 deny incoming并启用 UFW,可能同时切断当前和未来 SSH。安全流程使用两条独立路径:

  • 一个保持打开的公网已知可用会话,用于紧急恢复;
  • 一个已验证的 OpenSSH-over-Tailscale 会话,作为最终管理路径。

已有 SSH 会话在删除入站规则后可能因连接跟踪继续存活,因此它只能作为救生绳,不能算最终验证。

先检查,不重置

[REMOTE: VPS]
sudo ufw status verbose
sudo ufw status numbered

不要为了得到“干净状态”运行 ufw reset。现有规则可能属于别的基础设施,规则编号也会随增删变化。

安全启用顺序

在已验证的 Tailscale 会话中执行,同时保留公网会话:

[REMOTE: VPS]
sudo ufw default deny incoming
sudo ufw default allow outgoing

# 临时保留公网 SSH
sudo ufw allow 22/tcp

# 未来 Web 入口
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# 最终 SSH 入口
sudo ufw allow in on tailscale0 to any port 22 proto tcp

sudo ufw --force enable
sudo ufw status verbose
sudo ufw status numbered

此时要分别建立新的公网和 Tailscale SSH 会话。两者都成功,证明 UFW 已启用但回退路径仍在。

删除通用 22

先再次阅读规则。若语义明确,优先按原规则删除,这会同时处理匹配的 IPv4/IPv6 通用规则:

[REMOTE: VPS]
sudo ufw delete allow 22/tcp
sudo ufw status verbose
sudo ufw status numbered

按编号删除时,一次通常只删除一个编号;不要记住旧编号后盲删。最终列表中应该仍有:

80/tcp ALLOW IN Anywhere
443/tcp ALLOW IN Anywhere
22/tcp on tailscale0 ALLOW IN Anywhere

从客户端做最终测试

[LOCAL: WSL2]
# 必须成功
ssh -o ConnectTimeout=10 "$VPS_USER@$TAILSCALE_IPV4"

# 必须超时或被拒绝,不能成功
ssh -o ConnectionAttempts=1 -o ConnectTimeout=5 \
"$VPS_USER@$VPS_PUBLIC_IPV4"

只有第一个通过、第二个失败,目标策略才真正成立。之后才能关闭临时保留的公网旧会话。

Docker 的重要例外

Docker 官方文档明确提醒:容器发布端口时,Docker 创建的规则可能绕过 UFW。未来的 Compose 文件如果写了:

ports:
- "8080:8080"

就不能仅凭 ufw status断言 8080 没有暴露。部署应用时应同时检查 Docker 的端口绑定、docker ps、主机监听端口和 DOCKER-USER链策略。不要通过关闭 Docker 的 iptables 管理来“修复”问题,那通常会破坏容器网络。

恢复原则

如果删除通用 22 后 Tailscale 新连接失败:

  1. 不关闭仍存活的 SSH 会话;
  2. 从该会话恢复 sudo ufw allow 22/tcp
  3. 验证新的公网公钥 SSH;
  4. 收集 UFW、tailscale0sshd状态;
  5. 停止继续收口,先查明网络路径。

恢复的目标是重新获得一条可验证路径,而不是在紧张时重置整个防火墙。