{"title":"Accesso SSH via proxy","slug":"ssh-proxy-access","category":"llm-cookbook","summary":"Come un agente LLM esegue comandi su macchine remote in sicurezza tramite un'API proxy SSH invece di chiavi SSH grezze.","audience":["llm"],"tags":["cookbook","ssh","proxy","infrastructure","remote-execution"],"difficulty":"intermediate","updated":"2026-09-26","word_count":390,"read_minutes":2,"llm_context":"NON tenere MAI chiavi private SSH grezze come LLM. Usa un'API HTTP proxy SSH:\n1) POST /connect?target=<name> → session_id (persistente, sopravvive ore).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → output a fine esecuzione.\n4) Usa gli endpoint SFTP per up/download di file invece di cat/heredoc.\nI target sono nomi pre-registrati (es. \"vps1\"), non IP — il proxy è l'unico\nposto in cui vivono le credenziali.\n","lang":"it","translated":true,"requested_lang":"it","content_markdown":"\n# Accesso SSH via proxy\n\nGli agenti devono eseguire comandi su macchine remote — deployare un container, leggere un log, correggere una config. Consegnare chiavi private SSH grezze a un LLM è una cattiva idea: le chiavi finiscono in finestre di contesto, log e trascrizioni. Un proxy SSH risolve il problema: le credenziali le detiene lui, l'agente parla solo HTTP.\n\n## Il modello\n\n```\nLLM ──HTTPS──► Proxy SSH ──SSH──► macchine target\n                (detiene le chiavi,\n                 audit log,\n                 limiti di rate)\n```\n\nIl proxy espone sessioni persistenti con una superficie REST semplice:\n\n| Endpoint | Scopo |\n|---|---|\n| `POST /connect?target=vps1` | aprire una sessione → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | eseguire un comando (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | interrogare l'output |\n| Endpoint SFTP | up/download di file, `ls`, `mkdir`, `rm` |\n\nL'autenticazione è un token Bearer — lo stesso schema di ogni altra API che usi.\n\n## Il pattern di esecuzione\n\n```bash\n# 1. connetti una volta, riusa la sessione per tutto il task\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. esegui un comando\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. interroga il risultato\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Regole di ingaggio\n\n1. **Riusa le sessioni.** Connettersi per ogni comando spreca risorse e fa scattare i limiti di rate. Salva il `session_id` (es. `/tmp/vps1_sid`) e fai heartbeat prima di riutilizzarlo.\n2. **Nomina i target, non scoprili.** I target sono pre-registrati (`vps1`, `pi`, …). Il proxy è la fonte unica di verità — non hardcodare mai indirizzi IP nel codice agente.\n3. **Preferisci SFTP a cat/heredoc di shell per i file.** L'escaping di dati binari via `exec` corrompe i payload. Usa gli endpoint dedicati di upload/download.\n4. **Virgoletta i comandi.** Il corpo JSON viene parsato due volte (HTTP + shell). Avvolgi i comandi remoti in apici singoli e tienili su una riga.\n5. **Assumi l'auditing.** Ogni exec finisce nell'audit log del proxy — comportati come se l'umano leggesse tutto (può farlo).\n\n## Quando NON usare exec\n\n- Leggere stato strutturato che ha una API (Docker, database, la Mind API di Synapse) — preferisci sempre la API al parsing dell'output CLI.\n- Processi interattivi di lunga durata. `exec` è fire-and-forget con timeout; usa `nohup` + un file di stato sul target.\n- Qualsiasi cosa distruttiva senza istruzione esplicita. Il proxy ti dà il potere — l'umano ti dà il permesso.\n\n## Recupero errori\n\n| Sintomo | Rimedio |\n|---|---|\n| 401 al connect | token scaduto — rileggilo da memory/settings, mai da una trascrizione |\n| sessione 404 | sessione scaduta (limite 24h) — riconnetti |\n| timeout dell'exec | alza `timeout` o spezza il comando; verifica il carico del target |\n| contenuto file corrotto | hai usato `exec` invece di SFTP — passa agli endpoint SFTP |\n","content_html":"<h1>Accesso SSH via proxy</h1>\n<p>Gli agenti devono eseguire comandi su macchine remote — deployare un container, leggere un log, correggere una config. Consegnare chiavi private SSH grezze a un LLM è una cattiva idea: le chiavi finiscono in finestre di contesto, log e trascrizioni. Un proxy SSH risolve il problema: le credenziali le detiene lui, l&#39;agente parla solo HTTP.</p>\n<h2>Il modello</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► Proxy SSH ──SSH──► macchine target\n                (detiene le chiavi,\n                 audit log,\n                 limiti di rate)</code></pre><p>Il proxy espone sessioni persistenti con una superficie REST semplice:</p>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>Scopo</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>aprire una sessione → <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>eseguire un comando (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>interrogare l&#39;output</td>\n</tr>\n<tr>\n<td>Endpoint SFTP</td>\n<td>up/download di file, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>L&#39;autenticazione è un token Bearer — lo stesso schema di ogni altra API che usi.</p>\n<h2>Il pattern di esecuzione</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. connetti una volta, riusa la sessione per tutto il task</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. esegui un comando</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. interroga il risultato</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>Regole di ingaggio</h2>\n<ol>\n<li><strong>Riusa le sessioni.</strong> Connettersi per ogni comando spreca risorse e fa scattare i limiti di rate. Salva il <code>session_id</code> (es. <code>/tmp/vps1_sid</code>) e fai heartbeat prima di riutilizzarlo.</li>\n<li><strong>Nomina i target, non scoprili.</strong> I target sono pre-registrati (<code>vps1</code>, <code>pi</code>, …). Il proxy è la fonte unica di verità — non hardcodare mai indirizzi IP nel codice agente.</li>\n<li><strong>Preferisci SFTP a cat/heredoc di shell per i file.</strong> L&#39;escaping di dati binari via <code>exec</code> corrompe i payload. Usa gli endpoint dedicati di upload/download.</li>\n<li><strong>Virgoletta i comandi.</strong> Il corpo JSON viene parsato due volte (HTTP + shell). Avvolgi i comandi remoti in apici singoli e tienili su una riga.</li>\n<li><strong>Assumi l&#39;auditing.</strong> Ogni exec finisce nell&#39;audit log del proxy — comportati come se l&#39;umano leggesse tutto (può farlo).</li>\n</ol>\n<h2>Quando NON usare exec</h2>\n<ul>\n<li>Leggere stato strutturato che ha una API (Docker, database, la Mind API di Synapse) — preferisci sempre la API al parsing dell&#39;output CLI.</li>\n<li>Processi interattivi di lunga durata. <code>exec</code> è fire-and-forget con timeout; usa <code>nohup</code> + un file di stato sul target.</li>\n<li>Qualsiasi cosa distruttiva senza istruzione esplicita. Il proxy ti dà il potere — l&#39;umano ti dà il permesso.</li>\n</ul>\n<h2>Recupero errori</h2>\n<table>\n<thead>\n<tr>\n<th>Sintomo</th>\n<th>Rimedio</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 al connect</td>\n<td>token scaduto — rileggilo da memory/settings, mai da una trascrizione</td>\n</tr>\n<tr>\n<td>sessione 404</td>\n<td>sessione scaduta (limite 24h) — riconnetti</td>\n</tr>\n<tr>\n<td>timeout dell&#39;exec</td>\n<td>alza <code>timeout</code> o spezza il comando; verifica il carico del target</td>\n</tr>\n<tr>\n<td>contenuto file corrotto</td>\n<td>hai usato <code>exec</code> invece di SFTP — passa agli endpoint 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":390,"readMinutes":2,"llmContext":"NON tenere MAI chiavi private SSH grezze come LLM. Usa un'API HTTP proxy SSH:\n1) POST /connect?target=<name> → session_id (persistente, sopravvive ore).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → output a fine esecuzione.\n4) Usa gli endpoint SFTP per up/download di file invece di cat/heredoc.\nI target sono nomi pre-registrati (es. \"vps1\"), non IP — il proxy è l'unico\nposto in cui vivono le credenziali.\n","requestedLang":"it","contentMarkdown":"\n# Accesso SSH via proxy\n\nGli agenti devono eseguire comandi su macchine remote — deployare un container, leggere un log, correggere una config. Consegnare chiavi private SSH grezze a un LLM è una cattiva idea: le chiavi finiscono in finestre di contesto, log e trascrizioni. Un proxy SSH risolve il problema: le credenziali le detiene lui, l'agente parla solo HTTP.\n\n## Il modello\n\n```\nLLM ──HTTPS──► Proxy SSH ──SSH──► macchine target\n                (detiene le chiavi,\n                 audit log,\n                 limiti di rate)\n```\n\nIl proxy espone sessioni persistenti con una superficie REST semplice:\n\n| Endpoint | Scopo |\n|---|---|\n| `POST /connect?target=vps1` | aprire una sessione → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | eseguire un comando (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | interrogare l'output |\n| Endpoint SFTP | up/download di file, `ls`, `mkdir`, `rm` |\n\nL'autenticazione è un token Bearer — lo stesso schema di ogni altra API che usi.\n\n## Il pattern di esecuzione\n\n```bash\n# 1. connetti una volta, riusa la sessione per tutto il task\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. esegui un comando\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. interroga il risultato\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Regole di ingaggio\n\n1. **Riusa le sessioni.** Connettersi per ogni comando spreca risorse e fa scattare i limiti di rate. Salva il `session_id` (es. `/tmp/vps1_sid`) e fai heartbeat prima di riutilizzarlo.\n2. **Nomina i target, non scoprili.** I target sono pre-registrati (`vps1`, `pi`, …). Il proxy è la fonte unica di verità — non hardcodare mai indirizzi IP nel codice agente.\n3. **Preferisci SFTP a cat/heredoc di shell per i file.** L'escaping di dati binari via `exec` corrompe i payload. Usa gli endpoint dedicati di upload/download.\n4. **Virgoletta i comandi.** Il corpo JSON viene parsato due volte (HTTP + shell). Avvolgi i comandi remoti in apici singoli e tienili su una riga.\n5. **Assumi l'auditing.** Ogni exec finisce nell'audit log del proxy — comportati come se l'umano leggesse tutto (può farlo).\n\n## Quando NON usare exec\n\n- Leggere stato strutturato che ha una API (Docker, database, la Mind API di Synapse) — preferisci sempre la API al parsing dell'output CLI.\n- Processi interattivi di lunga durata. `exec` è fire-and-forget con timeout; usa `nohup` + un file di stato sul target.\n- Qualsiasi cosa distruttiva senza istruzione esplicita. Il proxy ti dà il potere — l'umano ti dà il permesso.\n\n## Recupero errori\n\n| Sintomo | Rimedio |\n|---|---|\n| 401 al connect | token scaduto — rileggilo da memory/settings, mai da una trascrizione |\n| sessione 404 | sessione scaduta (limite 24h) — riconnetti |\n| timeout dell'exec | alza `timeout` o spezza il comando; verifica il carico del target |\n| contenuto file corrotto | hai usato `exec` invece di SFTP — passa agli endpoint SFTP |\n","contentHtml":"<h1>Accesso SSH via proxy</h1>\n<p>Gli agenti devono eseguire comandi su macchine remote — deployare un container, leggere un log, correggere una config. Consegnare chiavi private SSH grezze a un LLM è una cattiva idea: le chiavi finiscono in finestre di contesto, log e trascrizioni. Un proxy SSH risolve il problema: le credenziali le detiene lui, l&#39;agente parla solo HTTP.</p>\n<h2>Il modello</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► Proxy SSH ──SSH──► macchine target\n                (detiene le chiavi,\n                 audit log,\n                 limiti di rate)</code></pre><p>Il proxy espone sessioni persistenti con una superficie REST semplice:</p>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>Scopo</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>aprire una sessione → <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>eseguire un comando (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>interrogare l&#39;output</td>\n</tr>\n<tr>\n<td>Endpoint SFTP</td>\n<td>up/download di file, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>L&#39;autenticazione è un token Bearer — lo stesso schema di ogni altra API che usi.</p>\n<h2>Il pattern di esecuzione</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. connetti una volta, riusa la sessione per tutto il task</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. esegui un comando</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. interroga il risultato</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>Regole di ingaggio</h2>\n<ol>\n<li><strong>Riusa le sessioni.</strong> Connettersi per ogni comando spreca risorse e fa scattare i limiti di rate. Salva il <code>session_id</code> (es. <code>/tmp/vps1_sid</code>) e fai heartbeat prima di riutilizzarlo.</li>\n<li><strong>Nomina i target, non scoprili.</strong> I target sono pre-registrati (<code>vps1</code>, <code>pi</code>, …). Il proxy è la fonte unica di verità — non hardcodare mai indirizzi IP nel codice agente.</li>\n<li><strong>Preferisci SFTP a cat/heredoc di shell per i file.</strong> L&#39;escaping di dati binari via <code>exec</code> corrompe i payload. Usa gli endpoint dedicati di upload/download.</li>\n<li><strong>Virgoletta i comandi.</strong> Il corpo JSON viene parsato due volte (HTTP + shell). Avvolgi i comandi remoti in apici singoli e tienili su una riga.</li>\n<li><strong>Assumi l&#39;auditing.</strong> Ogni exec finisce nell&#39;audit log del proxy — comportati come se l&#39;umano leggesse tutto (può farlo).</li>\n</ol>\n<h2>Quando NON usare exec</h2>\n<ul>\n<li>Leggere stato strutturato che ha una API (Docker, database, la Mind API di Synapse) — preferisci sempre la API al parsing dell&#39;output CLI.</li>\n<li>Processi interattivi di lunga durata. <code>exec</code> è fire-and-forget con timeout; usa <code>nohup</code> + un file di stato sul target.</li>\n<li>Qualsiasi cosa distruttiva senza istruzione esplicita. Il proxy ti dà il potere — l&#39;umano ti dà il permesso.</li>\n</ul>\n<h2>Recupero errori</h2>\n<table>\n<thead>\n<tr>\n<th>Sintomo</th>\n<th>Rimedio</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 al connect</td>\n<td>token scaduto — rileggilo da memory/settings, mai da una trascrizione</td>\n</tr>\n<tr>\n<td>sessione 404</td>\n<td>sessione scaduta (limite 24h) — riconnetti</td>\n</tr>\n<tr>\n<td>timeout dell&#39;exec</td>\n<td>alza <code>timeout</code> o spezza il comando; verifica il carico del target</td>\n</tr>\n<tr>\n<td>contenuto file corrotto</td>\n<td>hai usato <code>exec</code> invece di SFTP — passa agli endpoint 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"]}