Skip to content

open-remote-ssh: SSH接続レイヤーをssh2ライブラリからssh CLI方式に置き換える #132

@j4rviscmd

Description

@j4rviscmd

概要

open-remote-ssh の SSH 接続レイヤーを ssh2 Node.js ライブラリから OS の ssh CLI バイナリ spawn 方式に置き換え、Microsoft Remote-SSH 相当の堅牢性と認証互換性を実現する。

背景・動機

フィジビリティ調査結果

Microsoft Remote-SSH (ms-vscode-remote.remote-ssh) は以下の理由で Coderm では利用不可:

  1. ライセンス制約: プロプライエタリライセンスにより VS Code fork での使用が明確に禁止
  2. ソース非公開: microsoft/vscode-remote-release は issue tracker のみ。ソースコードは非公開
  3. 技術的非互換: Microsoft CDN にCodermのcommitハッシュに対応するサーバーバイナリが存在しない

現在の open-remote-ssh の制約

open-remote-ssh は ssh2 Node.js ライブラリを使用しており、以下の制約がある:

項目 ssh CLI (Remote-SSH) ssh2 ライブラリ (open-remote-ssh)
FIDO2/YubiKey (ed25519-sk) ×
Kerberos/GSSAPI ×
SSH config 完全互換 ○ (OS ssh が解釈) △ (自前パース)
ControlMaster 多重化 ×
再接続の堅牢性 高い 低い
Match ディレクティブ
セキュリティ更新追従 OS ssh 更新で自動 npm パッケージ依存

実装計画

Phase 1: SSH CLI でコマンド実行 (MVP)

効果: FIDO2/Kerberos 対応即時解禁、SSH config 完全互換

  • SSHCli クラス新規実装
    • ControlMaster ソケット管理 (-o ControlMaster=auto -o ControlPath=...)
    • exec() メソッド: ControlMaster 経由のコマンド実行
    • SSH config の解釈を OS ssh に完全委譲
  • serverSetup.tsinstallCodeServer() を ssh CLI 経由に変更
  • SSH_ASKPASS 環境変数による VS Code パスワード入力 UI 連携

Phase 2: SSH CLI でポートフォワーディング

効果: 再接続の堅牢性向上

  • ssh -L でローカルポートフォワード
  • ControlMaster 共有による高速・並列転送
  • ソケットパス対応 (-L /path/to/socket:...)

Phase 3: ssh2 ライブラリ完全除去

効果: 依存削減、保守コスト削減

  • ssh2, socks, simple-socks 依存削除
  • SSHConnection クラス → SSHCli クラスに完全置換
  • ProxyJump は ssh CLI に委譲(自前実装削除)
  • 認証ハンドラー (getSSHAuthHandler) 全削除

Phase 4: 自動ポート検知 (オプション)

効果: Remote-SSH 相当の UX

  • リモートの ss -tlnp 定期ポーリング
  • 新規リスニングポート検知→自動フォワード提案

技術的設計

class SSHCli {
  private controlPath: string;
  
  constructor(private host: string, private options: SSHCliOptions) {
    this.controlPath = `/tmp/coderm-ssh-${host}-${randomId}`;
  }

  async connect(): Promise<void> {
    spawn("ssh", [
      "-o", "ControlMaster=auto",
      "-o", `ControlPath=${this.controlPath}`,
      "-o", "ControlPersist=600",
      "-N", this.host
    ]);
  }

  async exec(command: string): Promise<ExecResult> {
    return spawn("ssh", [
      "-o", `ControlPath=${this.controlPath}`,
      this.host, command
    ]);
  }

  async forward(localPort: number, remotePort: number): Promise<void> {
    spawn("ssh", [
      "-o", `ControlPath=${this.controlPath}`,
      "-L", `${localPort}:127.0.0.1:${remotePort}`,
      "-N", this.host
    ]);
  }
}

リスクと対策

リスク 対策
Windows で ssh CLI なし Windows OpenSSH プリチェック、なければ ssh2 フォールバック
ControlMaster が macOS/Linux のみ Windows は ControlMaster 非対応のためフォールバック必要
認証プロンプト SSH_ASKPASS で VS Code UI に接続
ssh stdout/stderr パース バージョン差異に対応するエラーハンドリング

優先度

Phase 1 だけで最大の価値(全認証方式対応)が得られるため、まず Phase 1 を実装。

対象リポジトリ

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions