{"title":"SSH-доступ через прокси","slug":"ssh-proxy-access","category":"llm-cookbook","summary":"Как LLM-агент безопасно выполняет команды на удалённых машинах через HTTP-API SSH-прокси вместо сырых SSH-ключей.","audience":["llm"],"tags":["cookbook","ssh","proxy","infrastructure","remote-execution"],"difficulty":"intermediate","updated":"2026-09-26","word_count":342,"read_minutes":2,"llm_context":"НИКОГДА не держите сырые приватные SSH-ключи как LLM. Используйте HTTP-API SSH-прокси:\n1) POST /connect?target=<name> → session_id (постоянная, живёт часами).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → вывод по готовности.\n4) Для загрузки/скачивания файлов используйте SFTP-эндпоинты вместо cat/heredoc.\nЦели — предзарегистрированные имена (напр. \"vps1\"), не IP — прокси — единственное\nместо, где живут учётные данные.\n","lang":"ru","translated":true,"requested_lang":"ru","content_markdown":"\n# SSH-доступ через прокси\n\nАгентам нужно выполнять команды на удалённых машинах — развернуть контейнер, прочитать лог, поправить конфиг. Давать сырые приватные SSH-ключи LLM — плохая идея: ключи попадают в окна контекста, логи и транскрипты. SSH-прокси решает это: учётные данные хранит он, агент говорит только по HTTP.\n\n## Модель\n\n```\nLLM ──HTTPS──► SSH-прокси ──SSH──► целевые машины\n                (хранит ключи,\n                 журнал аудита,\n                 лимиты частоты)\n```\n\nПрокси отдаёт постоянные сессии через простой REST-поверхность:\n\n| Эндпоинт | Назначение |\n|---|---|\n| `POST /connect?target=vps1` | открыть сессию → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | выполнить команду (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | опросить вывод |\n| SFTP-эндпоинты | загрузка/скачивание файлов, `ls`, `mkdir`, `rm` |\n\nАутентификация — Bearer-токен, тот же паттерн, что и во всех остальных API, которые вы используете.\n\n## Паттерн выполнения\n\n```bash\n# 1. подключитесь один раз, переиспользуйте сессию для всей задачи\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. выполните команду\nEID=$(curl -s -H \"Authorization: Bearer $KEY\" -H \"Content-Type: application/json\" \\\n  -X POST \"https://ssh-proxy.example/$SID/exec\" \\\n  -d '{\"command\": \"docker ps --format \\\"{{.Names}}\\\"\", \"timeout\": 120}' | jq -r .exec_id)\n\n# 3. опросите результат\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Правила поведения\n\n1. **Переиспользуйте сессии.** Подключение на каждую команду тратит ресурсы и ловит лимиты частоты. Сохраните `session_id` (напр. `/tmp/vps1_sid`) и проверяйте его heartbeat перед повторным использованием.\n2. **Называйте цели, а не открывайте их.** Цели предзарегистрированы (`vps1`, `pi`, …). Прокси — единственный источник истины; никогда не зашивайте IP-адреса в код агента.\n3. **Предпочитайте SFTP вместо cat/heredoc для файлов.** Экранирование бинарных данных через `exec` портит полезную нагрузку. Используйте выделенные эндпоинты загрузки/скачивания.\n4. **Кавычьте свои команды.** Тело JSON парсится дважды (HTTP + shell). Оборачивайте удалённые команды в одинарные кавычки и держите их в одну строку.\n5. **Считайте, что аудит ведётся.** Каждый exec попадает в журнал аудита прокси — ведите себя так, будто человек читает всё (он может).\n\n## Когда exec использовать НЕ стоит\n\n- Чтение структурированного состояния, у которого есть API (Docker, базы данных, Mind API Synapse) — всегда предпочитайте API разбору вывода CLI.\n- Долгие интерактивные процессы. `exec` — это fire-and-forget с таймаутом; вместо этого используйте `nohup` + файл статуса на целевой машине.\n- Любое деструктивное действие без явной инструкции. Прокси даёт вам власть — человек даёт вам разрешение.\n\n## Восстановление после ошибок\n\n| Симптом | Решение |\n|---|---|\n| 401 при connect | токен истёк — перечитайте его из memory/settings, никогда из транскрипта |\n| сессия 404 | сессия истекла (лимит 24 ч) — переподключитесь |\n| таймаут exec | поднимите `timeout` или разделите команду; проверьте нагрузку целевой машины |\n| испорченное содержимое файла | вы использовали `exec` вместо SFTP — перейдите на SFTP-эндпоинты |\n","content_html":"<h1>SSH-доступ через прокси</h1>\n<p>Агентам нужно выполнять команды на удалённых машинах — развернуть контейнер, прочитать лог, поправить конфиг. Давать сырые приватные SSH-ключи LLM — плохая идея: ключи попадают в окна контекста, логи и транскрипты. SSH-прокси решает это: учётные данные хранит он, агент говорит только по HTTP.</p>\n<h2>Модель</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► SSH-прокси ──SSH──► целевые машины\n                (хранит ключи,\n                 журнал аудита,\n                 лимиты частоты)</code></pre><p>Прокси отдаёт постоянные сессии через простой REST-поверхность:</p>\n<table>\n<thead>\n<tr>\n<th>Эндпоинт</th>\n<th>Назначение</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>открыть сессию → <code>session_id</code></td>\n</tr>\n<tr>\n<td><code>POST /&lt;sid&gt;/exec</code> <code>{command, timeout}</code></td>\n<td>выполнить команду (async) → <code>exec_id</code></td>\n</tr>\n<tr>\n<td><code>GET /&lt;sid&gt;/exec/&lt;exec_id&gt;</code></td>\n<td>опросить вывод</td>\n</tr>\n<tr>\n<td>SFTP-эндпоинты</td>\n<td>загрузка/скачивание файлов, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>Аутентификация — Bearer-токен, тот же паттерн, что и во всех остальных API, которые вы используете.</p>\n<h2>Паттерн выполнения</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. подключитесь один раз, переиспользуйте сессию для всей задачи</span>\nSID=$(curl -s -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$KEY</span>&quot;</span> \\\n  <span class=\"hljs-string\">&quot;https://ssh-proxy.example/connect?target=vps1&quot;</span> | jq -r .session_id)\n\n<span class=\"hljs-comment\"># 2. выполните команду</span>\nEID=$(curl -s -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$KEY</span>&quot;</span> -H <span class=\"hljs-string\">&quot;Content-Type: application/json&quot;</span> \\\n  -X POST <span class=\"hljs-string\">&quot;https://ssh-proxy.example/<span class=\"hljs-variable\">$SID</span>/exec&quot;</span> \\\n  -d <span class=\"hljs-string\">&#x27;{&quot;command&quot;: &quot;docker ps --format \\&quot;{{.Names}}\\&quot;&quot;, &quot;timeout&quot;: 120}&#x27;</span> | jq -r .exec_id)\n\n<span class=\"hljs-comment\"># 3. опросите результат</span>\ncurl -s -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$KEY</span>&quot;</span> <span class=\"hljs-string\">&quot;https://ssh-proxy.example/<span class=\"hljs-variable\">$SID</span>/exec/<span class=\"hljs-variable\">$EID</span>&quot;</span></code></pre><h2>Правила поведения</h2>\n<ol>\n<li><strong>Переиспользуйте сессии.</strong> Подключение на каждую команду тратит ресурсы и ловит лимиты частоты. Сохраните <code>session_id</code> (напр. <code>/tmp/vps1_sid</code>) и проверяйте его heartbeat перед повторным использованием.</li>\n<li><strong>Называйте цели, а не открывайте их.</strong> Цели предзарегистрированы (<code>vps1</code>, <code>pi</code>, …). Прокси — единственный источник истины; никогда не зашивайте IP-адреса в код агента.</li>\n<li><strong>Предпочитайте SFTP вместо cat/heredoc для файлов.</strong> Экранирование бинарных данных через <code>exec</code> портит полезную нагрузку. Используйте выделенные эндпоинты загрузки/скачивания.</li>\n<li><strong>Кавычьте свои команды.</strong> Тело JSON парсится дважды (HTTP + shell). Оборачивайте удалённые команды в одинарные кавычки и держите их в одну строку.</li>\n<li><strong>Считайте, что аудит ведётся.</strong> Каждый exec попадает в журнал аудита прокси — ведите себя так, будто человек читает всё (он может).</li>\n</ol>\n<h2>Когда exec использовать НЕ стоит</h2>\n<ul>\n<li>Чтение структурированного состояния, у которого есть API (Docker, базы данных, Mind API Synapse) — всегда предпочитайте API разбору вывода CLI.</li>\n<li>Долгие интерактивные процессы. <code>exec</code> — это fire-and-forget с таймаутом; вместо этого используйте <code>nohup</code> + файл статуса на целевой машине.</li>\n<li>Любое деструктивное действие без явной инструкции. Прокси даёт вам власть — человек даёт вам разрешение.</li>\n</ul>\n<h2>Восстановление после ошибок</h2>\n<table>\n<thead>\n<tr>\n<th>Симптом</th>\n<th>Решение</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 при connect</td>\n<td>токен истёк — перечитайте его из memory/settings, никогда из транскрипта</td>\n</tr>\n<tr>\n<td>сессия 404</td>\n<td>сессия истекла (лимит 24 ч) — переподключитесь</td>\n</tr>\n<tr>\n<td>таймаут exec</td>\n<td>поднимите <code>timeout</code> или разделите команду; проверьте нагрузку целевой машины</td>\n</tr>\n<tr>\n<td>испорченное содержимое файла</td>\n<td>вы использовали <code>exec</code> вместо SFTP — перейдите на SFTP-эндпоинты</td>\n</tr>\n</tbody></table>\n","urls":{"html":"/docs/llm-cookbook/ssh-proxy-access","text":"/docs/llm-cookbook/ssh-proxy-access?format=text","json":"/docs/llm-cookbook/ssh-proxy-access?format=json","llm":"/docs/llm-cookbook/ssh-proxy-access?format=llm"},"translations_available":["en","zh","hi","es","fr","ar","pt","ru","ja","de","it","ko","nl","pl","tr","sv","vi","th","id","uk"],"wordCount":342,"readMinutes":2,"llmContext":"НИКОГДА не держите сырые приватные SSH-ключи как LLM. Используйте HTTP-API SSH-прокси:\n1) POST /connect?target=<name> → session_id (постоянная, живёт часами).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → вывод по готовности.\n4) Для загрузки/скачивания файлов используйте SFTP-эндпоинты вместо cat/heredoc.\nЦели — предзарегистрированные имена (напр. \"vps1\"), не IP — прокси — единственное\nместо, где живут учётные данные.\n","requestedLang":"ru","contentMarkdown":"\n# SSH-доступ через прокси\n\nАгентам нужно выполнять команды на удалённых машинах — развернуть контейнер, прочитать лог, поправить конфиг. Давать сырые приватные SSH-ключи LLM — плохая идея: ключи попадают в окна контекста, логи и транскрипты. SSH-прокси решает это: учётные данные хранит он, агент говорит только по HTTP.\n\n## Модель\n\n```\nLLM ──HTTPS──► SSH-прокси ──SSH──► целевые машины\n                (хранит ключи,\n                 журнал аудита,\n                 лимиты частоты)\n```\n\nПрокси отдаёт постоянные сессии через простой REST-поверхность:\n\n| Эндпоинт | Назначение |\n|---|---|\n| `POST /connect?target=vps1` | открыть сессию → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | выполнить команду (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | опросить вывод |\n| SFTP-эндпоинты | загрузка/скачивание файлов, `ls`, `mkdir`, `rm` |\n\nАутентификация — Bearer-токен, тот же паттерн, что и во всех остальных API, которые вы используете.\n\n## Паттерн выполнения\n\n```bash\n# 1. подключитесь один раз, переиспользуйте сессию для всей задачи\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. выполните команду\nEID=$(curl -s -H \"Authorization: Bearer $KEY\" -H \"Content-Type: application/json\" \\\n  -X POST \"https://ssh-proxy.example/$SID/exec\" \\\n  -d '{\"command\": \"docker ps --format \\\"{{.Names}}\\\"\", \"timeout\": 120}' | jq -r .exec_id)\n\n# 3. опросите результат\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Правила поведения\n\n1. **Переиспользуйте сессии.** Подключение на каждую команду тратит ресурсы и ловит лимиты частоты. Сохраните `session_id` (напр. `/tmp/vps1_sid`) и проверяйте его heartbeat перед повторным использованием.\n2. **Называйте цели, а не открывайте их.** Цели предзарегистрированы (`vps1`, `pi`, …). Прокси — единственный источник истины; никогда не зашивайте IP-адреса в код агента.\n3. **Предпочитайте SFTP вместо cat/heredoc для файлов.** Экранирование бинарных данных через `exec` портит полезную нагрузку. Используйте выделенные эндпоинты загрузки/скачивания.\n4. **Кавычьте свои команды.** Тело JSON парсится дважды (HTTP + shell). Оборачивайте удалённые команды в одинарные кавычки и держите их в одну строку.\n5. **Считайте, что аудит ведётся.** Каждый exec попадает в журнал аудита прокси — ведите себя так, будто человек читает всё (он может).\n\n## Когда exec использовать НЕ стоит\n\n- Чтение структурированного состояния, у которого есть API (Docker, базы данных, Mind API Synapse) — всегда предпочитайте API разбору вывода CLI.\n- Долгие интерактивные процессы. `exec` — это fire-and-forget с таймаутом; вместо этого используйте `nohup` + файл статуса на целевой машине.\n- Любое деструктивное действие без явной инструкции. Прокси даёт вам власть — человек даёт вам разрешение.\n\n## Восстановление после ошибок\n\n| Симптом | Решение |\n|---|---|\n| 401 при connect | токен истёк — перечитайте его из memory/settings, никогда из транскрипта |\n| сессия 404 | сессия истекла (лимит 24 ч) — переподключитесь |\n| таймаут exec | поднимите `timeout` или разделите команду; проверьте нагрузку целевой машины |\n| испорченное содержимое файла | вы использовали `exec` вместо SFTP — перейдите на SFTP-эндпоинты |\n","contentHtml":"<h1>SSH-доступ через прокси</h1>\n<p>Агентам нужно выполнять команды на удалённых машинах — развернуть контейнер, прочитать лог, поправить конфиг. Давать сырые приватные SSH-ключи LLM — плохая идея: ключи попадают в окна контекста, логи и транскрипты. SSH-прокси решает это: учётные данные хранит он, агент говорит только по HTTP.</p>\n<h2>Модель</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► SSH-прокси ──SSH──► целевые машины\n                (хранит ключи,\n                 журнал аудита,\n                 лимиты частоты)</code></pre><p>Прокси отдаёт постоянные сессии через простой REST-поверхность:</p>\n<table>\n<thead>\n<tr>\n<th>Эндпоинт</th>\n<th>Назначение</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>открыть сессию → <code>session_id</code></td>\n</tr>\n<tr>\n<td><code>POST /&lt;sid&gt;/exec</code> <code>{command, timeout}</code></td>\n<td>выполнить команду (async) → <code>exec_id</code></td>\n</tr>\n<tr>\n<td><code>GET /&lt;sid&gt;/exec/&lt;exec_id&gt;</code></td>\n<td>опросить вывод</td>\n</tr>\n<tr>\n<td>SFTP-эндпоинты</td>\n<td>загрузка/скачивание файлов, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>Аутентификация — Bearer-токен, тот же паттерн, что и во всех остальных API, которые вы используете.</p>\n<h2>Паттерн выполнения</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. подключитесь один раз, переиспользуйте сессию для всей задачи</span>\nSID=$(curl -s -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$KEY</span>&quot;</span> \\\n  <span class=\"hljs-string\">&quot;https://ssh-proxy.example/connect?target=vps1&quot;</span> | jq -r .session_id)\n\n<span class=\"hljs-comment\"># 2. выполните команду</span>\nEID=$(curl -s -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$KEY</span>&quot;</span> -H <span class=\"hljs-string\">&quot;Content-Type: application/json&quot;</span> \\\n  -X POST <span class=\"hljs-string\">&quot;https://ssh-proxy.example/<span class=\"hljs-variable\">$SID</span>/exec&quot;</span> \\\n  -d <span class=\"hljs-string\">&#x27;{&quot;command&quot;: &quot;docker ps --format \\&quot;{{.Names}}\\&quot;&quot;, &quot;timeout&quot;: 120}&#x27;</span> | jq -r .exec_id)\n\n<span class=\"hljs-comment\"># 3. опросите результат</span>\ncurl -s -H <span class=\"hljs-string\">&quot;Authorization: Bearer <span class=\"hljs-variable\">$KEY</span>&quot;</span> <span class=\"hljs-string\">&quot;https://ssh-proxy.example/<span class=\"hljs-variable\">$SID</span>/exec/<span class=\"hljs-variable\">$EID</span>&quot;</span></code></pre><h2>Правила поведения</h2>\n<ol>\n<li><strong>Переиспользуйте сессии.</strong> Подключение на каждую команду тратит ресурсы и ловит лимиты частоты. Сохраните <code>session_id</code> (напр. <code>/tmp/vps1_sid</code>) и проверяйте его heartbeat перед повторным использованием.</li>\n<li><strong>Называйте цели, а не открывайте их.</strong> Цели предзарегистрированы (<code>vps1</code>, <code>pi</code>, …). Прокси — единственный источник истины; никогда не зашивайте IP-адреса в код агента.</li>\n<li><strong>Предпочитайте SFTP вместо cat/heredoc для файлов.</strong> Экранирование бинарных данных через <code>exec</code> портит полезную нагрузку. Используйте выделенные эндпоинты загрузки/скачивания.</li>\n<li><strong>Кавычьте свои команды.</strong> Тело JSON парсится дважды (HTTP + shell). Оборачивайте удалённые команды в одинарные кавычки и держите их в одну строку.</li>\n<li><strong>Считайте, что аудит ведётся.</strong> Каждый exec попадает в журнал аудита прокси — ведите себя так, будто человек читает всё (он может).</li>\n</ol>\n<h2>Когда exec использовать НЕ стоит</h2>\n<ul>\n<li>Чтение структурированного состояния, у которого есть API (Docker, базы данных, Mind API Synapse) — всегда предпочитайте API разбору вывода CLI.</li>\n<li>Долгие интерактивные процессы. <code>exec</code> — это fire-and-forget с таймаутом; вместо этого используйте <code>nohup</code> + файл статуса на целевой машине.</li>\n<li>Любое деструктивное действие без явной инструкции. Прокси даёт вам власть — человек даёт вам разрешение.</li>\n</ul>\n<h2>Восстановление после ошибок</h2>\n<table>\n<thead>\n<tr>\n<th>Симптом</th>\n<th>Решение</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 при connect</td>\n<td>токен истёк — перечитайте его из memory/settings, никогда из транскрипта</td>\n</tr>\n<tr>\n<td>сессия 404</td>\n<td>сессия истекла (лимит 24 ч) — переподключитесь</td>\n</tr>\n<tr>\n<td>таймаут exec</td>\n<td>поднимите <code>timeout</code> или разделите команду; проверьте нагрузку целевой машины</td>\n</tr>\n<tr>\n<td>испорченное содержимое файла</td>\n<td>вы использовали <code>exec</code> вместо SFTP — перейдите на SFTP-эндпоинты</td>\n</tr>\n</tbody></table>\n","translationsAvailable":["en","zh","hi","es","fr","ar","pt","ru","ja","de","it","ko","nl","pl","tr","sv","vi","th","id","uk"]}