🖥️
WSL2のOpenSSH Agent Relayが動かなくなったので調査した
症状
WSL2におけるOpenSSHのAgentについては以下の記事を参照。
手元のPCでは npiperelay + socat を使ったAgent Relay構成にしているが、最近になって動かなくなった。Agentから結果が得られず、毎回ストレージの秘密鍵を開くためにパスワードが要求されるようになっていた。
何を調査したか
- 秘密鍵を移動。パスワードが聞かれなくなり、エラーになることを確認
- → Agentから毎回パスワードを聞かれているのではなく、そもそもAgentがうまく動いていない
-
ssh-add -T ~/.ssh/id_rsa.pub
は正しく動作することを確認- → 少なくとも基本的な通信経路は生きている
-
openssh-portable を自分でビルドしても再現
- → Ubuntu固有の問題ではない
- 処理を差し込んでデバッグ可能に
-
debug("...")
やdebug_f("...")
を各所に差し込む → SSH_AUTH_SOCK に対する read(2) の戻り値が0になっている箇所がある (get_agent_identities → ssh_agent_bind_hostkey → ssh_request_reply_decode → ssh_request_reply → atomicio(read))- → リクエストを送信してからレスポンスを受信するまでのどこかに問題がある (受信後のssh側の問題ではない)
-
ssh
とssh-add -T
をそれぞれstraceして、SSH_AUTH_SOCK への書き込みコールを見てみる → リクエストの中身は異なるが、書き込みパターンは同じ- → 相手側処理からレスポンスを受信するまでのどこかに問題がある (送信自体は成功している)
- straceのログからメッセージタイプを特定する →
ssh
は SSH_AGENTC_EXTENSION (extension name =session-bind@openssh.com
)、ssh-add -T
は SSH_AGENTC_SIGN_REQUEST を送っている- 仮説: 現状のWindows側のOpenSSH Serviceが
session-bind@openssh.com
に対応していない?
- 仮説: 現状のWindows側のOpenSSH Serviceが
-
ssh_agent_bind_hostkey
の呼び出し部を削除してみる → 動いた - client bindingが導入されたコミット
- Original OpenSSH https://github.com/openbsd/src/commit/798a6ad5b4968529d5def36842afad2157b5f3c1
- OpenSSH Portable https://github.com/openssh/openssh-portable/commit/e9497ecf73f3c16667288bce48d4e3d7e746fea1
- Debian package for OpenSSH https://salsa.debian.org/ssh-team/openssh/-/commit/e9497ecf73f3c16667288bce48d4e3d7e746fea1
- 仮説: この前Ubuntuをfocal (20.04) からjammy (22.04) に上げたときに導入されて壊れた?
対応
以下のパッチを作成した。 SSH_AUTH_NOBIND=1
とすることでclient hostkey bindingを無効にできる。
diff --git a/authfd.c b/authfd.c
index 76e48aab..0d94c5e3 100644
--- a/authfd.c
+++ b/authfd.c
@@ -732,6 +732,11 @@ ssh_agent_bind_hostkey(int sock, const struct sshkey *key,
{
struct sshbuf *msg;
int r;
+ char *nobind;
+
+ nobind = getenv(SSH_AUTH_NOBIND_ENV_NAME);
+ if (nobind != NULL && *nobind != '\0')
+ return 0;
if (key == NULL || session_id == NULL || signature == NULL)
return SSH_ERR_INVALID_ARGUMENT;
diff --git a/ssh.h b/ssh.h
index 8110c060..29a1dbf4 100644
--- a/ssh.h
+++ b/ssh.h
@@ -62,6 +62,8 @@
*/
#define SSH_AUTHSOCKET_ENV_NAME "SSH_AUTH_SOCK"
+#define SSH_AUTH_NOBIND_ENV_NAME "SSH_AUTH_NOBIND"
+
/*
* Environment variable for overwriting the default location of askpass
*/
Debianパッケージを作るためにまず元となるメンテナリポジトリをクローン
git clone https://salsa.debian.org/ssh-team/openssh.git debian-openssh
cd debian-openssh
git switch -d debian/1%8.9p1-3
ブランチを切る
git switch -c nobind
上記パッチを適用
git-dpm checkout-patched
patch -p1 < nobind.patch
git add authfd.c ssh.h
git commit -m "Allow skipping client hostkey binding"
git-dpm update-patches
更新時にオリジナルのパッケージで上書きされないように、changelogを足してバージョンを大きくする。たとえば今回ソースバージョンは 8.9p1-3
でUbuntuで配布されているバージョンは 8.9p1-3ubuntu0.1
なので、 u
に勝てるように 8.9p1-3znobind
とかにしておけばよいはず。
dch
ビルド
git-dpm prepare && dpkg-buildpackage -rfakeroot -us -uc
インストール
dpkg -i ../openssh-client_8.9p1-3_amd64.deb
あとはSSH_AUTH_SOCKをセットアップしている箇所で export SSH_AUTH_SOCK=1
をするようにすればOK。
Discussion
この記事をみて、WSL2とssh-agent.exeの独自ブリッジを作ってみました。
基本的にはsocat + npiperelayを自力で再実装したものですが、OpenSSHの拡張メッセージはssh-agent.exeに転送しないようにしたので、パッチをあてない素のsshでも使えます。もしよければお試しくださいー