Skip to content
Chris Yuan | Blog
Go back

mosh 連不上 Dropbear?加 --no-ssh-pty 就通:排查過程與全域設定的安全性

Edit page

在跑 Dropbear 的主機上用 mosh,client 會一直連不上,看起來像是 mosh-server 沒有啟動。實際上 mosh 根本不需要常駐 server:問題出在 mosh 預設用 ssh -n -tt 做 bootstrap,Dropbear 吃不下這個矛盾的旗標組合,會在 MOSH CONNECT 訊息送達前就關閉連線。解法是加上 --no-ssh-pty,而且這個 flag 對一般的 OpenSSH 主機沒有副作用,可以放心設成全域 alias。

Table of contents

Open Table of contents

起因:以為是 mosh-server 沒有自動啟動

我有一台 Raspberry Pi 3 跑 DietPi,當 homelab 的 subnet router(走 Tailscale)。DietPi 預設的 SSH server 是輕量的 Dropbear,不是 OpenSSH。在上面裝了 mosh 之後,從 Mac 直接 mosh dietpi@homelab-subnetrouter 連不上,第一直覺是「mosh server 沒有自動啟動,大概要補一個 systemd service」。

這個直覺是錯的,而且錯得很有代表性,所以值得記錄下來。

mosh 本來就沒有常駐 daemon

mosh 的架構跟 sshd 完全不同:

  1. mosh client 先透過一般的 SSH 登入遠端(Dropbear 也支援)。
  2. 這條 SSH session 幫你臨時啟動一個專屬的 mosh-server 程序,它挑一個 UDP port(預設 60000-61000 範圍)監聽,並印出一行 MOSH CONNECT <port> <key>
  3. client 解析到這行後,SSH 連線就功成身退,之後全部走 UDP + AES 加密通道。

所以 mosh-server 是每次連線按需啟動、session 結束就消失的,設定開機自動啟動的 service 不但沒必要,也沒有意義。如果 mosh 連不上,方向應該是查 bootstrap 過程哪裡壞掉,而不是去補 service。

排查:mosh-server 和 UDP 通道都是好的

在 DietPi 上逐項檢查,全部正常:

甚至手動驗證了整條路:在遠端跑 mosh-server new -p 60001 拿到 key,從 Mac 用 MOSH_KEY=... mosh-client <ip> 60001 直連 UDP,完全可以連上。也就是說 mosh-server 和 UDP 通道都是好的,壞掉的只有 mosh wrapper 自動做的那段 SSH bootstrap。

根因:ssh -n -tt 遇上 Dropbear

看 mosh 的 wrapper script(它其實是一個 perl script)就真相大白:

my @sshopts = ( '-n' );
if ($ssh_pty) {
    push @sshopts, '-tt';
}

mosh 固定用 -n(stdin 接到 /dev/null),預設又加 -tt(強制配置遠端 pty)。「不開 stdin」加上「強制互動式 pty」是個矛盾的組合。OpenSSH server 容忍它,但 Dropbear 會在 mosh-server 的輸出送回 client 之前就把連線關掉,client 等不到 MOSH CONNECT,看起來就像 server 沒起來。

這是個老問題,上游早就知道(mosh issue #819),mosh 1.3.0 就為此加了 --no-ssh-pty 選項,作用單純:不要加 -tt

mosh --no-ssh-pty dietpi@homelab-subnetrouter   # 這樣就通了

其他解法的評估

我選擇保留 Dropbear,在 client 端把 flag 固定下來。

全域加上 --no-ssh-pty 安全嗎

安全,這是這次研究中最值得記下的結論。--no-ssh-pty 唯一的作用是不要求遠端 pty,而 bootstrap 階段本來就不需要 pty:遠端只是執行 mosh-server new 這個非互動指令,真正互動用的 pty 是之後 mosh-server 在 UDP session 裡自己配置的。對 OpenSSH 主機:

唯一的例外是登入流程中有「在 server 端執行、且需要 tty 的互動式程式」的主機,典型如 Duo 的 login_duo forced command、bastion 登入選單。mosh 1.4 把 pty 改成預設開啟就是為了這類情境。沒有這種主機的話,全域加 flag 就是安全的。

跟既有 alias 整合的小坑

我在 oh-my-zsh 的 custom aliases 裡本來就有 alias mosh='mosh --ssh="ssh -A"'(agent forwarding),直接改成:

alias mosh='mosh --ssh="ssh -A" --no-ssh-pty'

順帶一提一個 zsh 小坑:如果既有 alias 還在,又在 .zshrcmosh() { ... } 定義同名 function,zsh 在定義 function 時會先展開 alias,直接 parse error。同名的 alias 和 function 只能擇一,或先 unalias 再定義。

結語

「mosh 連不上」的第一反應常常是去找 server 端的 service 設定,但 mosh 的設計哲學就是沒有 daemon。遇到連不上,先理解工具的架構再排查,這次的答案是 client 端一個 mosh 1.3.0 就加好的 flag。


Edit page
Share this post on:Link copied!

Next Post
Claude Code skill 瘦身靠猜還是靠量?skill-jedi 經驗法則對上 skill-optimizer 量測管線