《反向 SSH 隧道断线自愈:systemd + 看门狗的最小实践》

举报
yd_277098636 发表于 2026/11/11 08:22:00 2026/11/11
【摘要】 反向 SSH 隧道断线自愈:systemd + 看门狗的最小实践选题:运维实战。场景来自一台没有公网 IP 的云主机,需要长期对外暴露 SSH 与若干 TCP 服务端口。方案用反向 SSH 隧道 + systemd 服务 + 定时看门狗兜底,全程无人工值守。 问题:隧道一断,整条链路就失联没有公网 IP 的机器要对外提供服务,常见做法是在有公网 IP 的中转机上起 sshd,本机用 ssh...

反向 SSH 隧道断线自愈:systemd + 看门狗的最小实践

选题:运维实战。场景来自一台没有公网 IP 的云主机,需要长期对外暴露 SSH 与若干 TCP 服务端口。方案用反向 SSH 隧道 + systemd 服务 + 定时看门狗兜底,全程无人工值守。

问题:隧道一断,整条链路就失联

没有公网 IP 的机器要对外提供服务,常见做法是在有公网 IP 的中转机上起 sshd,本机用 ssh -R 把端口反向转发过去:

ssh -N -R 2222:localhost:22 -R 20443:localhost:20443 user@jump-host

这条命令本身没问题,问题在“长期运行”:

  • 网络抖动、宿主机重启、中转机 sshd 重启,都会让隧道悄悄断掉;
  • ssh -N 断了不会自己重连,ServerAliveInterval 只能发现断线,不能恢复;
  • 更麻烦的是“假活”:进程还在,但转发端口已经不通,ps 看不出问题。

我们线上真实踩过的坑:一台 VM 重置后,隧道进程消失,面板、订阅、监控全部失联,直到人工发现才恢复。解决思路不是把隧道做得更“结实”,而是接受它一定会断,给它加两层自愈。

第一层:systemd 服务 + 自动重启

把隧道命令包成 systemd 服务,让 systemd 负责拉起:

[Unit]
Description=Reverse SSH tunnel to jump host
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/local/bin/reverse-ssh-tunnel.sh
Restart=always
RestartSec=10
StartLimitIntervalSec=300
StartLimitBurst=10

[Install]
WantedBy=multi-user.target

要点:

  • Restart=always + RestartSec=10:进程退出 10 秒后自动重拉;
  • StartLimitBurst:防止配置错误时无限狂刷,给一个上限;
  • 启动脚本里先做前置检查:中转机是否可达、本地待转发端口是否在监听,不满足就退出让 systemd 重试,而不是挂一个半死不活的隧道。

光有这一层还不够。systemd 只能看到“进程是否活着”,看不到“转发是否真的通”。进程活着但转发已死的“假活”状态,需要第二层来发现。

第二层:定时看门狗,做端到端验证

看门狗是一个每 10 分钟跑一次的脚本,只做三件事:

  1. 查进程:隧道进程在不在;
  2. 查端口:中转机上的转发端口能不能连通(从本机经隧道回连一次,或直接 nc -z 探测);
  3. 查服务:端口后面真正要暴露的服务(面板、订阅接口)是否返回正常 HTTP 状态码。

任何一项失败,先 systemctl restart 隧道服务;重启后复查仍失败,再按预设的恢复流程走(重建配置、重装缺失组件)。我们的做法是把恢复逻辑写进一个幂等的 restore.sh,看门狗只负责“发现问题并触发恢复”,恢复本身由脚本负责。

# 看门狗核心逻辑(示意)
if ! pgrep -f "ssh -N -R 2222" >/dev/null; then
  systemctl restart reverse-ssh-tunnel
fi
# 端到端:经中转机回连本机 22 端口
if ! timeout 5 ssh -p 2222 -o ConnectTimeout=5 jump-host true 2>/dev/null; then
  systemctl restart reverse-ssh-tunnel
fi

关键经验:验证必须端到端。只查进程等于没查,2026 年 10 月底我们就吃过亏:dpkg -s 查包状态返回成功,实际服务根本没起来——状态查询本身也可能撒谎。看门狗的判断标准永远是“真实流量能不能走通”,而不是“某个命令的退出码”。

第三层(可选):出站受限时的 egress 代理

如果本机出站也被限制(比如只能走 HTTP CONNECT 代理出站、直连 22 端口被墙),隧道命令本身也要走代理:

ssh -o ProxyCommand="nc -X connect -x proxy-host:8080 %h %p" \
    -N -R 2222:localhost:22 user@jump-host

或者用 ProxyCommand 指向一个 TLS 封装脚本。这一层与看门狗无关,但它是隧道能在受限网络里建立的前提。把代理配置写进独立的 env 文件(600 权限),启动脚本 source 它,不要把凭据写进 systemd unit。

恢复脚本要幂等

看门狗触发的 restore.sh 必须满足幂等:跑一次和跑十次结果一样。我们的恢复脚本按段组织,每段先检查最终状态、再决定是否修复:

  • 检查服务是否 active,不 active 才重建;
  • 检查二进制是否存在,不存在才安装(先 apt,失败走本地离线 deb 兜底);
  • 检查配置文件、密钥、systemd unit 是否齐全,缺失才补写;
  • 最后统一验证:隧道进程、端口转发、服务 HTTP 状态码三项全绿才算恢复成功。

2026 年 11 月初的一次实测:VM 重置后看门狗检测到 9 个组件缺失,触发恢复,约 2 分钟内全部重建并验证通过,全程无人干预。没有幂等保证的恢复脚本不敢交给定时任务自动跑——重复执行可能把好好的配置覆盖掉。

效果与注意事项

  • 隧道断线后 10 秒内 systemd 重拉;假活状态最迟 10 分钟内被看门狗发现并重启;
  • 夜间无人值守的重置也能在下次看门狗巡检时自动恢复;
  • 注意事项:看门狗本身也要被 systemd timer 或 cron 守护,别让看门狗自己悄悄停了;恢复脚本的操作要写日志,方便事后复盘哪一段触发过。

小结

反向 SSH 隧道的自愈不需要复杂方案,三层就够:systemd 管进程生死、看门狗做端到端验证、幂等恢复脚本兜底。核心就一句话:不要相信进程状态,只相信真实流量。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。