1228 字
6 分钟
autossh 还活着,反向端口却消失了:一次 SSH 隧道故障复盘

这次故障的表象很简单:SSH 登录突然失败,排查发现 VPS 的 4240 端口没有监听。重启 NEC8 上的 autossh 服务后,登录恢复了。

真正值得复盘的是:为什么一个设置了自动重连的隧道,会在网络恢复后留下一个“进程还在、端口却不工作”的半故障状态?

故障现象#

这条连接由 NEC8 主动连向 VPS,并在 VPS 上建立反向端口转发:

客户端 → VPS:4240 → SSH 反向转发 → NEC8:22

因此,VPS 的 4240 是否在监听,决定了外部能不能通过这条隧道访问 NEC8 的 SSH 服务。故障时该端口不可用;重启 NEC8 上的 nec8-autossh.service 后,连接重新建立,问题暂时消失。

从日志找到故障边界#

NEC8 的 systemd 日志记录了网络和转发状态的变化。网络中断时,SSH 连接先报超时和 Network is unreachable。网络恢复后,日志又出现了关键提示:

Warning: remote port forwarding failed for listen port 4240

这条信息说明 SSH 客户端尝试恢复连接时,VPS 没有成功建立 4240 的远端转发。之后日志没有显示 autossh 再次启动 SSH 子进程,直到人工重启服务。

现有证据无法确定 VPS 为什么拒绝了转发:例如旧 SSH 会话是否仍占着端口,需要 VPS 端当时的监听状态和 SSH 日志才能确认。但故障机制已经比较清楚:SSH 会话的存活状态,不等于反向端口转发仍然健康。

为什么 autossh 没能兜底#

服务使用 autossh -M 0,并配置了 SSH keepalive:

ServerAliveInterval=30
ServerAliveCountMax=3

这套配置能帮助发现 SSH 会话长时间无响应,并在 SSH 进程退出后由 autossh 重连。但 -M 0 关闭了 autossh 自己的监控端口;如果 SSH 进程没有退出,只是远端转发建立失败,autossh 不一定能识别这个状态。

所以仅仅“进程存在”或“systemd 显示 active”不足以证明隧道可用。需要检查实际业务路径。

补上端到端健康检查#

我在 NEC8 增加了 systemd oneshot 检查服务和 timer,每分钟从 NEC8 通过 SSH 到 VPS,在 VPS 本机连接 127.0.0.1:4240,并读取返回内容,确认拿到的是 NEC8 SSH 服务的 banner。这样检查覆盖了整条转发路径,而不只是确认 VPS 上某个 TCP 端口能够 accept。

恢复策略如下:

  • 单次探测最多等待 5 秒;SSH 使用批处理模式,不会等待交互输入。
  • 连续失败 3 次后,由检查服务重启 NEC8 上的隧道服务。
  • 重启后设置 120 秒冷却期,避免故障期间每分钟重复重启。
  • 探测恢复后清零连续失败次数;计数和重启时间保存在 /run,重启主机后自动重新开始。
  • 检查结果和恢复动作写入 systemd journal。

健康检查只负责发现并重建 NEC8 发起的隧道。如果 VPS 本身不可达,重启本地隧道无法修复 VPS 故障;但网络恢复后,后续检查会继续尝试恢复连接。

部署时遇到的坑#

检查服务需要以 root 权限重启 systemd 系统服务,但 SSH 私钥属于日常用户。最初的 unit 同时启用了 NoNewPrivileges=yes,这会阻止 root 进程通过 runuser 切换到持有密钥的用户,健康检查因此失败。

修正后由 root 启动受限的检查脚本,再用 runuser 以日常用户身份运行 SSH 探测;脚本和 unit 文件由 root 管理,用户私钥仍保持原有权限。部署时也验证了 systemd unit,并分别执行了手动检查和一次定时检查。

验证结果与后续观察#

部署后的手动检查成功读取到 NEC8 的 SSH banner,失败计数清零;随后 timer 触发的周期检查也以 status=0/SUCCESS 结束。整个验证过程中,现有隧道没有被误重启。

后续可通过以下命令查看定时器和检查记录:

Terminal window
systemctl status nec8-autossh-healthcheck.timer
journalctl -u nec8-autossh-healthcheck.service
journalctl -t nec8-autossh-health

这次改动没有通过人为破坏隧道来演练“三次失败后自动重启”,因此阈值触发路径尚未在生产隧道上做故障注入。当前确认的是健康路径和 timer 周期路径正常。若需要验证恢复动作本身,应该安排一个可控维护窗口,再做专门演练。

复盘#

这次事故提醒我,自动重连机制仍然需要独立的健康判据。autossh 负责 SSH 进程级重连,但业务真正依赖的是 VPS 上的远端端口能否一路转发到 NEC8 的 sshd。把检查点放到实际访问路径上,才能覆盖“进程还活着、功能已经失效”的状态。

autossh 还活着,反向端口却消失了:一次 SSH 隧道故障复盘
https://blog.xqcherry.top/posts/operations/autossh-reverse-tunnel-port-not-listening-postmortem/
作者
xqcherry
发布于
2026-10-21
许可协议
CC BY-NC-SA 4.0