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 新连接失败:
- 不关闭仍存活的 SSH 会话;
- 从该会话恢复
sudo ufw allow 22/tcp; - 验证新的公网公钥 SSH;
- 收集 UFW、
tailscale0、sshd状态; - 停止继续收口,先查明网络路径。
恢复的目标是重新获得一条可验证路径,而不是在紧张时重置整个防火墙。