[v0.1/SSH Review] SSH_AUTH_SOCK 間接化とエージェント可観測性の欠如 #40

Closed
opened 2026-09-03 16:30:51 +09:00 by natsukium · 1 comment
Owner

現状

  • Unix の daemon は SSH_AUTH_SOCK<socket>.agent(socket と同ディレクトリの symlink)に間接化している(crates/felis-daemon/src/serve.rsSSH_AUTH_SOCK 処理、relay.rs の carrier block)。relay が持つ SSH_AUTH_SOCK(sshd が forward したもの)を daemon が symlink 先に付け替える。途中切断中は agent 不在、次の felis --host で修復される(how-to/attach-over-ssh.md "Your SSH agent survives it too")。
  • この symlink は暗黙: 設定キーなし、無効化手段なし、daemon status に現れない。複数 window が異なる ssh -A 接続で繋いだとき最後に dial したものが勝つ(last-wins)。どちらが勝ったかログにも出ない。
  • Windows の OpenSSH agent は名前付きパイプで固定名のため間接化不要と docs に明記。Unix のみで発動する分岐が serve.rs にハードコード。
  • SSH_AUTH_SOCK 以外の forwarded env(DISPLAY, XDG_RUNTIME_DIR 等)は間接化されないが、relay の carrier block は std::env::vars_os() 全量(4096 entries / 1MiB cap)を送る。daemon は SpawnArgs.env_base と合わせて解決するが、agent 以外の値は捨てられる。

問い(1.0 でしか変えられない)

この agent 間接化を暗黙の symlink として凍結するか? 明示的な control surface(config / daemon status / ログ)に昇格させるか?

破壊的変更案

  • 案A: 現行の symlink 間接化を維持し、可観測性を足す。 daemon statusresources または新 agent 行で agent_symlink -> /tmp/.../agent.123 の有無と指し先を報告。tracing::info で symlink 更新時にログ。docs/reference/cli.md daemon status 表に agent 行を追加。
  • 案B: 間接化を opt-in にする。 config.toml または daemon 起動引数で forward_agent = true|false を持たせ、false では SSH_AUTH_SOCK をそのまま子に渡す(切断で失われるが明示的)。デフォルト true で現行互換だが、1.0 でキー名を決めておくことで将来無効化できる。
  • 案C: 間接化をやめ、明示的な felis agent verb にする。 felis --host remote sessions 実行時に毎回 SSH_AUTH_SOCK を再配布するのではなく、ユーザが felis agent forward を打ったときのみ daemon の symlink を更新。暗黙の last-wins をなくす。

判定基準

  • how-to/attach-over-ssh.md の "Your SSH agent survives it too" が reference/cli.mddaemon statusreference/ipc.md の carrier block 記述と矛盾しないこと(現状 status に agent が出ない)。
  • ForwardAgent yes~/.ssh/config に書いたユーザが、git push が一度切断しても次の felis --host で直ることを daemon status やログで確認できること。
  • principle 3(explicit over implicit)に照らして、暗黙の symlink が許容されるか。

対象ファイル

  • crates/felis-daemon/src/serve.rs (SSH_AUTH_SOCK 解決), crates/felis-daemon/src/relay.rs (capture_environment), crates/felis-transport/src/preface.rs (CarrierBlock), docs/how-to/attach-over-ssh.md, docs/reference/cli.md (daemon status), docs/reference/ipc.md (FRLY block), docs/explanation/architecture/ipc.md

cc @natsukium

## 現状 - Unix の daemon は `SSH_AUTH_SOCK` を `<socket>.agent`(socket と同ディレクトリの symlink)に間接化している(`crates/felis-daemon/src/serve.rs` の `SSH_AUTH_SOCK` 処理、`relay.rs` の carrier block)。relay が持つ `SSH_AUTH_SOCK`(sshd が forward したもの)を daemon が symlink 先に付け替える。途中切断中は agent 不在、次の `felis --host` で修復される(`how-to/attach-over-ssh.md` "Your SSH agent survives it too")。 - この symlink は暗黙: 設定キーなし、無効化手段なし、`daemon status` に現れない。複数 window が異なる `ssh -A` 接続で繋いだとき最後に dial したものが勝つ(last-wins)。どちらが勝ったかログにも出ない。 - Windows の OpenSSH agent は名前付きパイプで固定名のため間接化不要と docs に明記。Unix のみで発動する分岐が `serve.rs` にハードコード。 - `SSH_AUTH_SOCK` 以外の forwarded env(`DISPLAY`, `XDG_RUNTIME_DIR` 等)は間接化されないが、relay の carrier block は `std::env::vars_os()` 全量(4096 entries / 1MiB cap)を送る。daemon は `SpawnArgs.env_base` と合わせて解決するが、agent 以外の値は捨てられる。 ## 問い(1.0 でしか変えられない) この agent 間接化を暗黙の symlink として凍結するか? 明示的な control surface(config / `daemon status` / ログ)に昇格させるか? ## 破壊的変更案 - **案A: 現行の symlink 間接化を維持し、可観測性を足す。** `daemon status` の `resources` または新 `agent` 行で `agent_symlink -> /tmp/.../agent.123` の有無と指し先を報告。`tracing::info` で symlink 更新時にログ。`docs/reference/cli.md` daemon status 表に `agent` 行を追加。 - **案B: 間接化を opt-in にする。** `config.toml` または daemon 起動引数で `forward_agent = true|false` を持たせ、false では `SSH_AUTH_SOCK` をそのまま子に渡す(切断で失われるが明示的)。デフォルト true で現行互換だが、1.0 でキー名を決めておくことで将来無効化できる。 - **案C: 間接化をやめ、明示的な `felis agent` verb にする。** `felis --host remote sessions` 実行時に毎回 `SSH_AUTH_SOCK` を再配布するのではなく、ユーザが `felis agent forward` を打ったときのみ daemon の symlink を更新。暗黙の last-wins をなくす。 ## 判定基準 - `how-to/attach-over-ssh.md` の "Your SSH agent survives it too" が `reference/cli.md` の `daemon status` と `reference/ipc.md` の carrier block 記述と矛盾しないこと(現状 status に agent が出ない)。 - `ForwardAgent yes` を `~/.ssh/config` に書いたユーザが、`git push` が一度切断しても次の `felis --host` で直ることを `daemon status` やログで確認できること。 - principle 3(explicit over implicit)に照らして、暗黙の symlink が許容されるか。 ## 対象ファイル - `crates/felis-daemon/src/serve.rs` (`SSH_AUTH_SOCK` 解決), `crates/felis-daemon/src/relay.rs` (`capture_environment`), `crates/felis-transport/src/preface.rs` (`CarrierBlock`), `docs/how-to/attach-over-ssh.md`, `docs/reference/cli.md` (daemon status), `docs/reference/ipc.md` (FRLY block), `docs/explanation/architecture/ipc.md` cc @natsukium
Author
Owner

Triaged as resolved by the existing design record. docs/explanation/security-model.md already specifies the daemon-owned stable agent path, newest-live/restore behavior, same-UID trust boundary, rejected alternatives, and revisit trigger. No observed operator failure currently earns another config or IPC surface. If #26 finds that admission refusals cannot be diagnosed without agent state, that concrete status requirement can be added there; the speculative surface does not remain separate.

Triaged as resolved by the existing design record. `docs/explanation/security-model.md` already specifies the daemon-owned stable agent path, newest-live/restore behavior, same-UID trust boundary, rejected alternatives, and revisit trigger. No observed operator failure currently earns another config or IPC surface. If #26 finds that admission refusals cannot be diagnosed without agent state, that concrete status requirement can be added there; the speculative surface does not remain separate.
Sign in to join this conversation.
No description provided.