WSLというものを知り面白そうと飛びついたのですがハマったのでおさらい。
WSL自体の導入は省きます。遭遇した事象はWSLのubuntuへのSSH接続時にTeratermやPuttyではつながるがコマンドプロンプトからのsshやVScodeからの接続では失敗していました。
事前準備として実行していたのは下記のもの。
1.OpenSSH導入
PowerShell(管理者権限)で状況を確認
Get-WindowsCapability -Online | Where-Object Name -like 'OpenSSH*'
導入済みだと以下のように表示される。
Name : OpenSSH.Client~~~~0.0.1.0
State : Installed
Name : OpenSSH.Server~~~~0.0.1.0
State : Installed
Installedになっていなければ続けてこちら
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
ステータス確認
Get-Service -Name sshd
Status Name DisplayName
Running sshd OpenSSH SSH Server
Runningでないときはこちら
Start-Service sshd
この後に確認用のコマンドを打つとRunningになる
(止めたいときはStop-Service sshd)
2.公開鍵認証のキーペア作成
ローカル環境(WindowsのPowerShell、管理者)でssh-keygen -t ed25519を実行。
※この時にパスフレーズを入力するとなんだかセキュリティ的にいいと聞いたので入れています。
あとは公開鍵のほう.pubが付いているほうをWSL側の/home/Connect-user/.ssh配下に
authorized_keysというファイル名で保存するだけ。
ディレクトリやファイルの権限は正式にはこれらしい。
/home/Connect-user/ 755
/home/Connect-user/.ssh 700
/home/Connect-user/.ssh/authorized_keys 600
3.ssh-agentでパスフレーズを秘密鍵にロードする
PowerShell(管理者権限)でStart-Service ssh-agent
ssh-add C:\Users\Local-user\.ssh\id_ed25519
この時に2.でキーペアを作った時のパスフレーズを一回だけ入力する。
ssh-add -lで入ってるかどうかも確認できます。
4.ローカルの.ssh\config準備
内容はこんな感じです
Host ubuntu
HostName 127.0.0.1
User Connect-user
Port 22
ForwardAgent yes
IdentityFile C:/Users/Local-user/.ssh/id_ed25519
■接続
個人的にはこれで十分でしたがコマンドプロンプトからのsshで弾かれる意味が分からなくて
調べてみました、あとVScodeからも。
VScodeのRemoteから試したときのエラーはこんな感じで何のことやら分かりませんでした。
[15:40:32.156] Terminal shell path: C:\WINDOWS\System32\cmd.exe [15:40:32.349] > [15:40:32.349] Got some output, clearing connection timeout [15:40:34.404] > ssh: connect to host xxx.xxx.xxx.xxx port 22: Connection refused [15:40:34.419] > プロセスが、存在しないパイプに書き込もうとしました。 [15:40:35.681] "install" terminal command done [15:40:35.682] Install terminal quit with output: プロセスが、存在しないパイプに書き込もうとしました。 [15:40:35.682] Received install output: プロセスが、存在しないパイプに書き込もうとしました。 [15:40:35.682] WARN: $PLATFORM is undefined in installation script output. Errors may be dropped. [15:40:35.682] Failed to parse remote port from server output [15:40:35.682] Resolver error: Error
■原因
"プロセスが、存在しないパイプに書き込もうとしました"でいろいろ探していたら
WSL2 は Hyper-V上で仮想ネットワークを使用するためポートフォワーディングが必要ということでした。
この辺はあまり理解してないですがそういうもんかと思って次のコマンドで設定しました。
PowerShell(管理者権限)netsh interface portproxy add v4tov4 listenaddress=127.0.0.1 listenport=2222 connectaddress=aaa.bbb.ccc.ddd connectport=22
aaa.bbb.ccc.dddはubuntu側のhostname -Iで表示されるIPアドレスです。
これで直るのかと思っていましたが結果は変わらずでした。
この時に4.で作った.ssh\configのPortは2222に変更しています。
じゃあ何が問題なのかとこのポートフォワーディング設定ってちゃんと反映されてるの?ということで
netsh interface portproxy show allで確認しました。
するとこんな結果が出ます。
ipv4 をリッスンする: ipv4 に接続する:
Address Port Address Port
127.0.0.1 2222 aaa.bbb.ccc.ddd 22
この時に思ったのがそういえばubuntu側の/etc/ssh/sshd_configに
ipv6の指定をした気がする!でした。
探してみたらありました。
AddressFamily inet6
これを作るときも意識していたのは
PasswordAuthentication no
PubkeyAuthentication yes
この二つだけでほかの設定はほとんど気にしてなかったです。
じゃあこれを4でも6でも大丈夫なようにとanyに変えました。
AddressFamily any
変更後は下記のコマンドで反映させます。
sudo systemctl daemon-reexec
sudo systemctl restart ssh
リッスンしてるのかどうか確認用に次の出力結果も見ます。
sudo ss -tlnp | grep :22
LISTEN 0 4096 [::]:22 [::]:*
users:(("sshd",pid=579,fd=3),("systemd",pid=1,fd=121))
あれ?むしろIPv6でLISTENしてるみたいだけどどういう事態?
というかポートフォワーディングしてるんだから2222でIPv4じゃないとダメなのでは?と
混乱しつつさらに調べます。
なぜIPv6でLISTENするようになったのかは不明ですがひとまずsshd_configの
Port 2222のエントリ追加が必要なことは分かりました。
ではクライアント側はIPv4なのにリモートがIPv6なのはどう直すのか。
全然知りませんでしたがsystemdのssh.socket機能というものがあり
頑張って編集していた/etc/ssh/sshd_configより優先されるようです。
実際は次のコマンドで確認しました。
systemctl cat ssh.socket
結果は以下のもので、ぱっと見大丈夫そうです。
# /usr/lib/systemd/system/ssh.socket
[Unit]
Description=OpenBSD Secure Shell server socket
Before=sockets.target ssh.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Socket]
ListenStream=0.0.0.0:22
ListenStream=[::]:22
BindIPv6Only=ipv6-only
Accept=no
FreeBind=yes
[Install]
WantedBy=sockets.target
RequiredBy=ssh.service
# /run/systemd/generator/ssh.socket.d/addresses.conf
# Automatically generated by sshd-socket-generator
[Socket]
ListenStream=
ListenStream=[::]:2222
ListenStream=[::]:22
ListenStream=0.0.0.0:2222
ListenStream=0.0.0.0:22
何でつながらないんだよ!と思いつつ一旦確認します。
sudo ss -tlnp | grep :22
LISTEN 0 4096 [::]:22 [::]:*
まあ何もしてないんだから変わらないよね、ということで設定があってるんだから
systemdの再起動と反映をすればIPv4でもLISTENするんじゃないかということで次の4つを実行。
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo systemctl restart ssh
すると、、、
sudo ss -tlnp | grep :22
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1381,fd=6),("systemd",pid=1,fd=65))
LISTEN 0 4096 0.0.0.0:2222 0.0.0.0:* users:(("sshd",pid=1381,fd=5),("systemd",pid=1,fd=64))
LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=1381,fd=4),("systemd",pid=1,fd=61))
LISTEN 0 4096 [::]:2222 [::]:* users:(("sshd",pid=1381,fd=3),("systemd",pid=1,fd=60))
4も6もLISTENしています!
あとは一回コマンドプロンプトからssh -p 2222 Connect-user@127.0.0.1で
Fingerprintの応答にyesを返して試合終了です。
最初はWSL2の仕組み上、ポートフォワーディングが必要じゃないかと考えて設定していましたが、
SSHサーバーがIPv4とIPv6の両方で正しくリッスンするように設定を整えた結果、
VSCodeやcmd.exeからも直接接続できるようになりました。
したがって、今回のケースではポートフォワーディングは結果的に不要だったようです。
ここまでの調査と設定変更は大変でしたが、非常に良い学びになりました!
