不稳定网络下的 3 种 SSH 替代方案
比较 Mosh、Eternal Terminal 和 autossh 处理不稳定 SSH 连接:UDP、TCP 重新连接、服务器需求以及 tmux 搭配。
Mosh、Eternal Terminal 和 autossh 以三种不同的方式解决同一个故障:Mosh 通过 UDP 同步终端状态,并跟随你的 IP 变化;Eternal Terminal 通过 TCP 重连同一个会话;而 autossh 会自动重启 SSH,但每次都是一个全新的会话。
如果你曾经因为火车驶入隧道而眼看着部署过程卡在一半,你一定知道接下来会发生什么:终端锁死,你干坐着心存侥幸,一分钟后屏幕上出现 client_loop: send disconnect。咖啡馆的 Wi-Fi、手机热点、时断时续的 VPN,对会话的杀伤力都是一样的。问题不在于要不要在交互式工作中替换掉原生 SSH,而在于这三款工具中哪一款更契合你的网络环境和服务器权限级别。本文只沿一条主线进行比较:当链路中断时,你的会话究竟会发生什么。
核心要点
- Mosh 通过 UDP 同步终端状态,Eternal Terminal 通过 TCP 重连同一个会话,autossh 则每次都以全新会话重启 SSH。
- 你与服务器之间的 UDP 是否被封锁,是在 Mosh 和 Eternal Terminal 之间做选择的决定性因素。
- autossh 完全在客户端运行,服务端除了 sshd 之外不需要任何东西;但每次重连都会落到一个新的 shell 中,因此必须搭配 tmux 或 Zellij 才有实用价值。
- tmux 和 Zellij 可以叠加在这三款工具之上:它们负责让服务器上的进程保持存活,而这些工具负责让你通往服务器的通路保持畅通。
链路中断时到底断了什么?
SSH 会话运行在一条与客户端 IP 地址绑定的 TCP 连接之中,因此一旦网络中断或地址发生变化,连接就会失效,并连带终结远程 shell。TCP 通过源地址、目的地址与端口构成的四元组来标识一条连接;换到新的 Wi-Fi 网络得到新的 IP,就意味着一个服务器从未见过的新四元组。一旦 keepalive 失败或套接字报错,sshd 就会回收该 shell 及其中的所有前台进程。下面三款工具分别针对这条链条上的不同环节下手。
Mosh:基于 UDP 的状态同步
故障时的行为
Mosh 用状态同步取代了字节流模型:客户端和服务端各自持有一份屏幕快照,Mosh 的状态同步协议(State Synchronization Protocol)通过加密的 UDP 数据报使二者收敛。由于服务端只是将数据发往最近一个通过认证的数据包的源地址,跨 IP 漫游是自动完成的;合上笔记本、切换网络,会话无需任何重连步骤即可恢复。预测性本地回显(predictive local echo)会立即显示你的按键,而不必等待服务端往返。
前置条件
Mosh 需要在远程主机上安装 mosh-server 二进制文件(无需超级用户权限,也没有常驻守护进程:两端都以你的身份运行,会话结束即退出),并且 UDP 必须可达。Mosh 先通过常规 SSH 登录完成认证,随后默认使用 60000 到 61000 范围内的一个 UDP 端口(从 60001 起的第一个可用端口),防火墙必须放行。它可以将参数透传给底层的 ssh:
mosh --ssh="ssh -i ~/.ssh/other_key" user@host
局限所在
如果 UDP 被封锁,Mosh 完全无法工作;它没有 TCP 回退机制。Mosh 也只同步当前屏幕上的字符,因此回滚缓冲区(scrollback)会丢失,项目自身的 FAQ 也建议你在远端使用 screen 或 tmux。Eternal Terminal 的两款工具对比还补充指出,Mosh 无法驱动 tmux 控制模式(tmux -CC),这对 iTerm2 用户来说是个问题。项目发布页面上的最新版本是 2022 年 10 月的 1.4.0。
Eternal Terminal:通过 TCP 恢复同一会话
故障时的行为
Eternal Terminal 通过普通 TCP 重新接续连接,而不会拆掉你正在进行的工作,包括正在运行的 tmux 会话,因此一次断网只会让你暂停片刻,而不会丢失成果。由于它始终基于 TCP,它恰好能在那些让 Mosh 出局的网络中正常工作:丢弃 UDP 的企业防火墙,以及强制流量经过 TCP 代理的链路。
前置条件
Eternal Terminal 需要一个服务端守护进程。项目 README 列出了两个前提:ssh 负责握手和加密,因此 ssh user@hostname 必须先能正常工作,ET 才能工作;服务端默认监听 TCP 端口 2022,除非你自行修改。macOS 上通过 brew install et 安装,Ubuntu 上:
sudo add-apt-repository ppa:jgmath2000/et
sudo apt-get update
sudo apt-get install et
用 systemctl status et 检查守护进程状态。
局限所在
基于 TCP 的重连意味着没有漫游魔法,也没有本地回显:在高延迟链路上,打字的手感依然和 SSH 一样。而且由于它需要以 root 权限安装守护进程并开放端口,在你无法安装软件的机器上就无从谈起。
autossh:自动重启,全新会话
故障时的行为
autossh 包装 ssh 并监视它启动的进程,一旦该进程退出或失去响应就启动一个替代进程。每次重连都是一个全新的 SSH 会话,因此如果服务器上没有多路复用器,链路恢复后你原先运行的一切都已不复存在。
前置条件
autossh 在服务端除了一个正在运行的 sshd 之外别无所求,这使它成为三者中唯一可用于「你无法安装软件」的机器的方案。客户端安装:
sudo apt install autossh # Debian/Ubuntu
sudo dnf install autossh # Fedora/RHEL family (may need EPEL)
brew install autossh # macOS
局限所在
它恢复的是连接,而不是会话。它还依赖 ssh 能察觉到故障:man 手册建议配置 ServerAliveInterval 和 ServerAliveCountMax,让客户端在链路确实断掉后尽快放弃。
在上层叠加 tmux 或 Zellij
tmux 和 Zellij 并不能取代上述任何一款工具:它们让你在服务器上的进程保持存活,而 Mosh、Eternal Terminal 和 autossh 让你通往服务器的通路保持畅通,两个层次是互补的。Zellij 是一款终端多路复用器,负责管理会话、面板和标签页,其叠加在远程连接之上的方式与 tmux 相同。
autossh 加 tmux 是最低限度的可用组合,因为它在每次重启时都会重新挂载到一个持久会话上:
autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 3" \
-t user@host "tmux new -A -s main"
-M 0 关闭 autossh 的监控端口,使其依赖 ssh 自身的退出状态;tmux new -A -s main 则每次挂载到(或创建)名为 main 的会话。这种搭配对另外两款工具同样有益:tmux 补齐了 Mosh 缺失的回滚缓冲区,而 Eternal Terminal 则明确支持让 tmux 会话穿越断网。
如何在这些 SSH 替代方案中做选择?
按顺序问三个问题。你与服务器之间的 UDP 是否被封锁?如果是,Mosh 出局,Eternal Terminal 就是那个抗断的选择。你能否在服务器上安装任何东西?如果不能,autossh 加 tmux 是你唯一的选项。除此之外,按使用体验来挑:在链路质量差的环境下需要漫游和即时回显就选 Mosh,需要真正的回滚缓冲区或 tmux -CC 就选 Eternal Terminal。
| Mosh | Eternal Terminal | autossh | |
|---|---|---|---|
| 恢复模型 | 基于 UDP 的状态同步,可跨 IP 漫游 | 通过 TCP 重连同一会话 | 重启 ssh,全新会话 |
| 服务端要求 | mosh-server 二进制文件,UDP 放行 | root 守护进程,TCP 端口 2022 | 仅需 sshd |
| 主要局限 | 无 UDP 则不可用,无会话保持 | 无本地回显 | 无 tmux 则会话丢失 |
你最无法容忍的那种故障模式,决定了该选哪款工具。在开放网络上先试 Mosh,把 Eternal Terminal 留给受限网络,今天就把带命名 tmux 会话的 autossh -M 0 写进 shell alias;它能让你已经可以 ssh 登录的每一台机器都得到升级。
常见问题
Mosh 能通过 SSH 跳板机或堡垒机使用吗?
不能。Mosh 没有对 [SSH 隧道或堡垒机](https://docs.blink.sh/advanced/advanced-mosh) 的内置处理。跳板机可以承载最初的 SSH 登录,但之后加密的 UDP 流量必须直接抵达目标机器,而仅靠跳板机做不到这一点。在仅允许堡垒机访问的网络中,autossh 可以照常使用,因为它运行的是普通 ssh,而 ssh 支持 ProxyJump。
Mosh 和原生 SSH 一样安全吗?
登录过程仍然走 SSH。在此之后,Mosh 使用 [OCB3 模式的 AES-128](https://mosh.org/) 这一认证加密方案,对每一个 UDP 数据报进行加密和认证。SSH 仅用于在开始时交换密钥,因此 SSH 层面的弱点只会影响这个短暂的建立阶段,而不会波及长时间运行的 Mosh 会话。你现有的 SSH 密钥和主机验证机制照常生效。
autossh 能保持 SSH 端口转发存活吗,还是只能用于交互式 shell?
可以,持久隧道正是 autossh 最常见的用途之一。运行时加上 -N 使其不执行远程命令,加上 -M 0 关闭监控端口,再配合你惯用的 -L 或 -R 转发参数;man 手册自身的示例就是将 -f -M 0 -N 与 ServerAliveInterval 和 ServerAliveCountMax 组合使用。隧道不持有任何 shell 状态,因此「全新会话」这一局限并不适用:autossh 只需重新建立转发即可。