- ポートがLISTENINGなのにpingが通らないときは、SSHではなくネットワーク側の問題。原因はゲストWi-Fiの端末間遮断でした。
- SSHは通信を始める前に、鍵交換・ホスト鍵・暗号方式を順に合意します。合意できない項目が変わるたびに、エラー文も変わります。
- 古い方式を拒否しているのはXP側ではなく、接続する側の新しいOpenSSH。許可の追加は接続先ごとに閉じ込めます。
- 公開鍵が無視されたのは、鍵そのものではなく署名アルゴリズムの不一致でした。

1. 「つながらない」の原因は下の層から順に現れる
手元に2000年代のノートパソコンがあって、Windows XPが入ったまま今も動きます。 ネットには出さず、家のLANの中だけで遊ぶ実験機にすることにしました。 メインのMacからSSHで入れれば、XPの画面を見なくてもcmd.exeを触れる。



XP側にfreeSSHdを入れて、ポート番号を1156に変えて起動しました。 Macから接続すると、しばらく待たされてタイムアウト。 XP側で待ち受け状態を確認すると、こう出ます。
netstat -an | find "1156"
TCP 192.168.x.x:1156 0.0.0.0:0 LISTENING
Code language: JavaScript (javascript)
LISTENINGは接続を受け付けている状態なので、サーバーは動いています。 では何が邪魔をしているのか。
1.1. ARPの応答がないときは、SSH以前の問題
Mac側からpingを打つと、9回投げて9回とも返ってきませんでした。 ncでポートを叩くとHost is down。 そこでARPテーブルを見ます。
arp -a | grep 192.168.x.x
your-xxxxxxxx (192.168.x.x) at (incomplete) on en0 ifscope [ethernet]
Code language: Bash (bash)
ARP(Address Resolution Protocol:アドレス解決プロトコル)は、IPアドレスから相手の機器固有のMACアドレスを調べる仕組みです。 incompleteは、問い合わせを投げたのに誰も答えなかった状態を指します。
つまりSSHどころか、同じネットワーク上にXPを見つけられていない。 XPのファイアウォールを疑ってnetsh firewallを叩く前に、ここで止まっていたわけです。
1.2. 同じWi-Fi名でも同じネットワークとは限らない
犯人はゲストWi-Fiでした。 XPを、来客用に用意していたSSIDにつないでいたのです。
多くのルーターのゲスト用SSIDには、AP isolationやクライアント間通信の遮断と呼ばれる機能が有効になっています。 インターネットには出られるけれど、同じSSIDにつながった端末同士は互いに見えない。 プリンタやNASが見つからないときの定番の原因でもあります。
XPを通常のWi-Fiにつなぎ直したら、その場でSSHが応答しました。 ただし今度は、別のエラーが出ます。
2. SSHは通信を始める前に4つの合意をしている
Unable to negotiate with 192.168.x.x port 1156: no matching key exchange method found.
Their offer: diffie-hellman-group1-sha1,diffie-hellman-group14-sha1
Code language: CSS (css)
negotiateは交渉という意味です。 SSHは暗号化した通信を始める前に、どの方式を使うかを双方で決めます。 決めるのは、鍵交換のKEX(Key Exchange:鍵交換方式)、サーバーを識別するホスト鍵、通信内容を隠す暗号方式、改ざんを検出するMACの4つ。
2.1. エラーが3回変わるのは、合意が順番に進んでいるから
まずgroup14を許可して再接続すると、次はこう言われます。
no matching host key type found. Their offer: ssh-rsa,ssh-dss
ssh-rsaを許可して再接続。
no matching cipher found.
Their offer: aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,...
aes128-cbcを許可すると、ようやくホスト鍵のフィンガープリント確認が出て、パスワードを聞かれました。
3回続けて拒否されると心が折れそうになりますが、これは同じ場所で足踏みしているのではありません。 合意の項目を1つ通すたびに次の項目へ進んでいて、エラー文が変わること自体が前進の証拠になっています。 blowfish-cbcや3des-cbcが並ぶ提示リストは、2000年代のSSHサーバーとしてはごく標準的な顔ぶれ。
2.2. 拒否しているのは古い側ではなく新しい側
このやりとりで意外だったのは、エラーを出しているのがXPではなくMacだったことです。 XPは「これなら使えます」と自分の持ち札を提示しているだけ。 それを見て「その方式は受け付けません」と席を立っているのが、現代のOpenSSHのほうです。
OpenSSHは、安全性が落ちたと判断した方式を既定の許可リストから外していきます。 ssh-rsaのSHA-1署名は、OpenSSH 8.8で既定無効になりました。 使えなくなったのではなく、使うと明示しない限り使わない、という扱いに変わっています。
3. 古い方式の許可は、その接続先だけに閉じ込める
-oが3つ並んだコマンドを毎回打つのはさすがに面倒なので、~/.ssh/configに書きます。
Host winxp
HostName 192.168.x.x
User user
Port 1156
KexAlgorithms +diffie-hellman-group14-sha1
HostKeyAlgorithms +ssh-rsa
Ciphers +aes128-cbc
Code language: CSS (css)
これでssh winxpだけで届くようになりました。 Host winxpは自分で決める別名で、実際の接続先はHostNameのほうです。
値の頭に付けた+には意味があります。 +は既定のリストを置き換えず、指定した方式を末尾に追加する指定です。 +なしで書くと既定を丸ごと差し替えてしまい、そのホストへは古い方式しか使えなくなります。
書く場所も大事で、Host *のような全接続共通のブロックには絶対に入れないでください。 2000年代の暗号方式を、現代のサーバーとの通信にまで持ち込むことになります。 古い実機と遊ぶための設定は、その実機の名前の下だけに置く。
4. 公開鍵が使われなかったのは、署名アルゴリズムのせいだった
パスワードを毎回打つのも面倒なので、公開鍵認証に切り替えます。 ここでまた2つ引っかかりました。
4.1. freeSSHdの公開鍵は authorized_keys ではない
OpenSSHのサーバーなら、公開鍵は~/.ssh/authorized_keysに追記します。 freeSSHdの方式は違っていて、設定画面で指定したPublic key folderの中に、ユーザー名と同じ名前のファイルを置きます。 ユーザー名がuserなら、ファイル名も拡張子なしのuser。 中身は.pubファイルの中身をそのまま1行入れます。
鍵の種類にも制約がありました。 Ed25519はOpenSSH 6.5で導入された比較的新しい方式なので、2009年に更新の止まったサーバーが知っているはずがない。 ssh-keygen -t rsa -b 2048でこの接続専用のRSA鍵を作り直しました。
4.2. ログの一行が原因を指していた
鍵を置いたのに、まだパスワードを聞かれます。 ssh -vvvで詳細ログを見ると、該当箇所は2行でした。
debug1: Offering public key: /Users/xxx/.ssh/id_rsa_winxp RSA SHA256:... explicit
debug1: send_pubkey_test: no mutual signature algorithm
Code language: HTTP (http)
鍵ファイルは読まれていて、提示しようとしています。 止まっているのは、その鍵で署名するときのアルゴリズムが双方で一致しない点。 RSA鍵という点は共通でも、署名をSHA-1で作るかSHA-2で作るかは別の設定で、そこが噛み合っていませんでした。
PubkeyAcceptedAlgorithms +ssh-rsa
この1行をHost winxpに足したら、パスワードを聞かれずにログインできました。
4.3. SFTPが使えずUSBメモリで運んだ
公開鍵をSFTPで送ろうとしたら、接続はできるのにこう出ます。
Connected to winxp.
realpath .: Failure
Need cwd
freeSSHd側でSFTPのホームディレクトリを設定していなかったので、カレントディレクトリを取得できていない状態です。 設定すれば直るはずですが、運ぶのは公開鍵1行だけ。 USBメモリを挿したほうが早くて、そちらで済ませました。
5. つながらない理由が5回変わったこと
振り返ると、5つの原因はすべて違う層にありました。 Wi-Fiのアクセスポイント、鍵交換、ホスト鍵、暗号方式、署名アルゴリズム。 1つずつしかエラーに出てこないので、「まだ何かあるのか」と思いながら一段ずつ登ることになります。
普段は一発でつながってしまうので、SSHがこれだけの手順を踏んでいることを意識しません。 20年前の実装を相手にすると、その手順が失敗の順番として一つずつ姿を現す。 つながらない相手ほど、仕組みをよく見せてくれます。