Skip to main content

プロキシ経由のSSHアクセス

LLMエージェントが生のSSH鍵ではなくSSHプロキシAPIを通じてリモートマシン上で安全にコマンドを実行する方法。


プロキシ経由のSSHアクセス

エージェントはリモートマシンでコマンドを実行する必要があります — コンテナをデプロイ、ログを読む、設定を直す。生のSSH秘密鍵をLLMに渡すのは悪い考えです:鍵はコンテキストウィンドウ、ログ、トランスクリプトに入り込んでしまいます。SSHプロキシはこれを解決します:資格情報を保持するのはプロキシで、エージェントはHTTPだけを話します。

モデル

LLM ──HTTPS──► SSHプロキシ ──SSH──► ターゲットマシン
                (鍵を保持、
                  監査ログ、
                  レート制限)

プロキシはシンプルなRESTサーフェスで永続セッションを公開します:

エンドポイント 目的
POST /connect?target=vps1 セッションを開く → session_id
POST /<sid>/exec {command, timeout} コマンドを実行(非同期)→ exec_id
GET /<sid>/exec/<exec_id> 出力をポーリング
SFTPエンドポイント ファイルのアップ/ダウンロード、ls、mkdir、rm

認証はBearerトークンです — 使っている他のすべてのAPIと同じパターンです。

実行パターン

# 1. 一度だけ接続し、タスク全体でセッションを再利用
SID=$(curl -s -H "Authorization: Bearer $KEY" \
  "https://ssh-proxy.example/connect?target=vps1" | jq -r .session_id)

# 2. コマンドを実行
EID=$(curl -s -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -X POST "https://ssh-proxy.example/$SID/exec" \
  -d '{"command": "docker ps --format \"{{.Names}}\"", "timeout": 120}' | jq -r .exec_id)

# 3. 結果をポーリング
curl -s -H "Authorization: Bearer $KEY" "https://ssh-proxy.example/$SID/exec/$EID"

運用規則

  1. セッションを再利用する。 コマンドごとに接続すると資源を浪費し、レート制限に触れます。session_idを保存し(例:/tmp/vps1_sid)、再利用前にハートビートさせましょう。
  2. ターゲットを名前で呼び、発見しない。 ターゲットは事前登録済みです(vps1、pi…)。プロキシが唯一の信頼源です — エージェントコードにIPアドレスをハードコードしてはいけません。
  3. ファイルにはシェルのcat/heredocよりSFTPを優先する。 execを通じたバイナリデータのエスケープはペイロードを壊します。専用のアップ/ダウンロードエンドポイントを使いましょう。
  4. コマンドをクォートする。 JSONボディは2回パースされます(HTTP + シェル)。リモートコマンドはシングルクォートで包み、1行に保ちましょう。
  5. 監査を前提とする。 すべてのexecはプロキシの監査ログに残ります — 人間がすべて読むつもりで振る舞いましょう(実際に読めます)。

execを使うべきでないとき

  • APIを持つ構造化された状態を読むとき(Docker、データベース、SynapseのMind API)— CLI出力の解析より常にAPIを優先します。
  • 長時間の対話的プロセス。execはタイムアウト付きのfire-and-forgetです。代わりにターゲット上でnohup + 状態ファイルを使いましょう。
  • 明示的な指示のない破壊的な操作すべて。プロキシが力を与えます — 許可を与えるのは人間です。

エラー回復

症状 修正
connectで401 トークン失効 — memory/settingsから読み直す。トランスクリプトからは絶対に
セッション404 セッション失効(24h上限)— 再接続
execタイムアウト timeoutを上げるかコマンドを分割;ターゲットの負荷を確認
ファイル内容の破損 SFTPの代わりにexecを使った — SFTPエンドポイントに切り替え