反向 SSH 隧道保活实战:systemd 与 autossh 的取舍
内网里的服务没有公网地址,运维时最省事的办法常常是反向 SSH 隧道:由内网机器主动向一台有公网地址的中转机建立连接,并在中转机上开一个端口,把流量反向转发回内网服务。隧道本身不难,难的是保活:网络抖动、NAT 会话超时、机器重启之后,隧道能不能自己回来。本文比较两种最常见的保活做法:systemd 直接托管 SSH,以及 autossh 专门负责重连,并给出取舍建议。文中示例均使用占位符,请替换为自己的账号与主机信息,不要照抄示例中的占位内容。
一、先说清反向隧道在保什么
反向隧道的典型形态是:内网主机执行一条带远程转发参数的 SSH 命令,中转机监听某个端口,外部访问该端口时,流量经已建立的 SSH 连接回到内网主机的本地服务端口。这里有三层存活问题:第一,SSH 进程本身是否还在;第二,连接是否还通,中间的 NAT 会话有没有被悄悄丢掉;第三,机器重启或网络恢复后,隧道是否能自动重建。只解决第一层是不够的,很多隧道“进程还在、端口还在,但就是不通”,原因往往出在第二层。
因此保活配置要同时回答三个问题:进程死了谁来拉起;连接假死谁来发现;重连时失败了要不要退避、重试几次。systemd 和 autossh 回答这三个问题的分工不同,这就是取舍的起点。
二、方案一:systemd 直接托管 SSH
如果机器已经使用 systemd,这是最省事的方案,不需要额外安装软件,日志、开机自启、失败重启都由系统统一管理。一个可用的服务单元大意如下:
[Unit]
Description=反向 SSH 隧道保活
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/bin/ssh -N -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -R 8080:localhost:80 USER@HOST
Restart=always
RestartSec=10
StartLimitIntervalSec=300
StartLimitBurst=5
[Install]
WantedBy=multi-user.target
几个关键参数值得逐一说明。ServerAliveInterval 让客户端定期发送保活探测,配合 ServerAliveCountMax,连续多次无响应就判定连接已死,而不是无限期挂着。ExitOnForwardFailure=yes 很重要:远程端口转发失败时让 SSH 直接退出,systemd 才能感知失败并重启;否则进程可能“成功运行”却没有转发,排障会非常困难。Restart=always 加 RestartSec 负责拉起,StartLimit 则防止在配置错误时无限快速重启,把系统日志刷爆。
这个方案的优点是简单、透明、与系统日志体系天然结合,用系统自带的日志查看命令就能看到隧道最近的启动、失败与重试记录。缺点也很直接:systemd 只能看到进程是否退出,看不到“连接假死但进程未退出”的中间状态,发现问题的速度完全依赖保活探测的间隔;另外它对端口占用、转发失败等细节的感知比较粗,适合隧道结构简单、网络相对稳定的场景。
三、方案二:autossh 专门负责重连
autossh 本质上是一个 SSH 的“监护进程”:它启动 SSH,并通过一对监控端口定期探测连接是否真的可用,一旦发现异常就杀掉旧连接、重新建立。典型用法大意如下:
AUTOSSH_GATETIME=0 autossh -M 0 -N -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -R 8080:localhost:80 USER@HOST
这里 -M 0 表示不使用传统的独立监控端口,改用 SSH 自身的保活机制配合 autossh 的重连逻辑,这在端口受限的环境里更友好;AUTOSSH_GATETIME=0 表示首次建立连接时不设等待宽限,失败立即按重连逻辑处理。autossh 的优势在于“发现假死”这件事是它的本职工作,重连更积极、更彻底,适合网络质量差、NAT 超时短、隧道需要长期挂着的场景。
它的代价是多了一层进程与配置:需要额外安装 autossh,需要理解监控端口、首次连接等待时间等参数,日志里会出现 autossh 与 SSH 两层输出,排障时要先分清是哪一层在报错。如果只是偶尔用一次的临时隧道,引入 autossh 反而是过度设计。
四、取舍:按场景选,不要按习惯选
可以按下面四条快速判断。第一,隧道只是临时调试、几天就撤掉,选 systemd 临时服务或直接前台运行加简单重启脚本即可,不必引入新工具。第二,隧道要长期在线、机器就是一台常规 Linux 服务器,且网络相对稳定,systemd 直接托管是最干净的方案,维护成本最低。第三,网络经常抖动、跨运营商、经过多层 NAT,或者隧道断了要尽快恢复,autossh 更合适,它对假死的发现与重连更主动。第四,如果团队已经用 systemd 统一管理所有服务,又想用 autossh 的重连能力,可以两者结合:用 systemd 管理 autossh 进程,autossh 负责 SSH 重连,systemd 负责开机自启与日志,这样各司其职,出了问题也容易定位到哪一层。
一个常见的误区是把两个工具的重试参数都调得很激进,结果配置一错就高频重连,反而把中转机的连接数与日志打满。重试间隔建议从十秒左右起步,配合失败次数上限与退避,让“快速恢复”和“不要雪上加霜”之间留有余地。
五、上线前必须过的排障清单
第一,确认远程转发端口的监听范围:默认情况下远程转发端口可能只监听中转机的本地回环地址,外部访问不到,这时需要结合中转机的 SSH 服务端转发策略与防火墙规则一起调整,不要只在客户端找原因。第二,确认本地服务本身是可达的,先在内网主机上直接访问本地端口,排除服务未启动、监听地址不对的问题。第三,确认密钥与权限:隧道专用密钥建议单独生成、只用于这一条隧道,私钥文件权限过松会被 SSH 拒绝,这是最常见的“昨天还好好的”原因之一。第四,看日志分层定位:先看 systemd 服务状态与最近日志,再看 SSH 的详细输出,最后才怀疑网络与防火墙。第五,演练一次真实断网恢复:拔掉网络或重启内网主机,观察隧道是否在预期时间内自动回来,没有演练过的保活都只是“理论上保活”。
安全方面同样不能省:反向隧道等于给内网服务开了一扇门,转发端口越少越好,不需要的端口不要顺手转发;中转机侧尽量限制可以访问转发端口的来源;隧道账号建议使用受限账号与专用密钥,避免使用日常登录的高权限账号;生产环境不要用口令登录,改用密钥并妥善保管私钥。
六、结论
systemd 与 autossh 不是对立关系,而是分工关系:systemd 擅长把一个进程变成系统服务,autossh 擅长判断一条 SSH 连接是不是真的还活着。网络稳定、结构简单时,systemd 直接托管已经够用;网络多变、要求快速恢复时,autossh 的价值才会显现;长期在线的重要隧道,用 systemd 管理 autossh 是兼顾可维护性与恢复能力的稳妥做法。无论选哪一种,先把保活探测、转发失败即退出、重启退避这三件事配齐,再做一次真实的断网恢复演练,隧道才算真正保活。
- 点赞
- 收藏
- 关注作者
评论(0)