{"title":"SSH-toegang via proxy","slug":"ssh-proxy-access","category":"llm-cookbook","summary":"Hoe een LLM-agent veilig commando's uitvoert op externe machines via een SSH-proxy-API in plaats van rauwe SSH-sleutels.","audience":["llm"],"tags":["cookbook","ssh","proxy","infrastructure","remote-execution"],"difficulty":"intermediate","updated":"2026-09-26","word_count":363,"read_minutes":2,"llm_context":"Houd N O O I T rauwe SSH-privésleutels als LLM. Gebruik een SSH-proxy-HTTP-API:\n1) POST /connect?target=<name> → session_id (persistent, overleeft uren).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → output wanneer klaar.\n4) Gebruik SFTP-endpoints voor bestandsup-/download in plaats van cat/heredoc.\nTargets zijn vooraf geregistreerde namen (bijv. \"vps1\"), geen IP's — de proxy is\nde enige plek waar credentials leven.\n","lang":"nl","translated":true,"requested_lang":"nl","content_markdown":"\n# SSH-toegang via proxy\n\nAgenten moeten commando's uitvoeren op externe machines — een container deployen, een log lezen, een config repareren. Rauwe SSH-privésleutels aan een LLM geven is een slecht idee: sleutels belanden in contextvensters, logs en transcripties. Een SSH-proxy lost dit op: hij bezit de credentials, de agent spreekt alleen HTTP.\n\n## Het model\n\n```\nLLM ──HTTPS──► SSH-proxy ──SSH──► doelmachines\n                (bezit sleutels,\n                 auditlog,\n                 ratelimieten)\n```\n\nDe proxy stelt persistente sessies bloot met een eenvoudig REST-oppervlak:\n\n| Endpoint | Doel |\n|---|---|\n| `POST /connect?target=vps1` | een sessie openen → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | een commando uitvoeren (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | output opvragen |\n| SFTP-endpoints | bestandsup-/download, `ls`, `mkdir`, `rm` |\n\nAuth is een Bearer-token — hetzelfde patroon als elke andere API die je gebruikt.\n\n## Het uitvoeringspatroon\n\n```bash\n# 1. verbind een keer, hergebruik de sessie voor de hele taak\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. voer een commando uit\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. vraag het resultaat op\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Gedragsregels\n\n1. **Hergebruik sessies.** Per commando verbinden verspilt bronnen en triggert ratelimieten. Bewaar de `session_id` (bijv. `/tmp/vps1_sid`) en geef hem een heartbeat vóór hergebruik.\n2. **Noem targets, ontdek ze niet.** Targets zijn vooraf geregistreerd (`vps1`, `pi`, …). De proxy is de enige bron van waarheid — hardcode nooit IP-adressen in agentcode.\n3. **Geef SFTP de voorkeur boven shell cat/heredoc voor bestanden.** Escapen van binaire data via `exec` corrumpeert payloads. Gebruik de dedicated upload/download-endpoints.\n4. **Quote je commando's.** De JSON-body wordt twee keer geparsed (HTTP + shell). Wikkel externe commando's in enkele aanhalingstekens en houd ze éénregelig.\n5. **Reken op auditing.** Elke exec komt in het auditlog van de proxy — gedraag je alsof de mens alles leest (dat kan).\n\n## Wanneer exec NIET gebruiken\n\n- Gestructureerde staat lezen die een API heeft (Docker, databases, de Synapse Mind API) — geef altijd de API de voorkeur boven CLI-output parsen.\n- Langlopende interactieve processen. `exec` is fire-and-forget met timeout; gebruik `nohup` + een statusbestand op de target.\n- Alles destructiefs zonder expliciete instructie. De proxy geeft je de kracht — de mens geeft je de toestemming.\n\n## Foutenherstel\n\n| Symptoom | Oplossing |\n|---|---|\n| 401 bij connect | token verlopen — lees hem opnieuw uit memory/settings, nooit uit een transcript |\n| sessie 404 | sessie verlopen (24h-limiet) — verbind opnieuw |\n| exec-timeout | verhoog `timeout` of splits het commando; controleer de belasting van de target |\n| gecorrumpeerde bestandsinhoud | je gebruikte `exec` in plaats van SFTP — schakel over naar de SFTP-endpoints |\n","content_html":"<h1>SSH-toegang via proxy</h1>\n<p>Agenten moeten commando&#39;s uitvoeren op externe machines — een container deployen, een log lezen, een config repareren. Rauwe SSH-privésleutels aan een LLM geven is een slecht idee: sleutels belanden in contextvensters, logs en transcripties. Een SSH-proxy lost dit op: hij bezit de credentials, de agent spreekt alleen HTTP.</p>\n<h2>Het model</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► SSH-proxy ──SSH──► doelmachines\n                (bezit sleutels,\n                 auditlog,\n                 ratelimieten)</code></pre><p>De proxy stelt persistente sessies bloot met een eenvoudig REST-oppervlak:</p>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>Doel</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>een sessie openen → <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>een commando uitvoeren (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>output opvragen</td>\n</tr>\n<tr>\n<td>SFTP-endpoints</td>\n<td>bestandsup-/download, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>Auth is een Bearer-token — hetzelfde patroon als elke andere API die je gebruikt.</p>\n<h2>Het uitvoeringspatroon</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. verbind een keer, hergebruik de sessie voor de hele taak</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. voer een commando uit</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. vraag het resultaat op</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>Gedragsregels</h2>\n<ol>\n<li><strong>Hergebruik sessies.</strong> Per commando verbinden verspilt bronnen en triggert ratelimieten. Bewaar de <code>session_id</code> (bijv. <code>/tmp/vps1_sid</code>) en geef hem een heartbeat vóór hergebruik.</li>\n<li><strong>Noem targets, ontdek ze niet.</strong> Targets zijn vooraf geregistreerd (<code>vps1</code>, <code>pi</code>, …). De proxy is de enige bron van waarheid — hardcode nooit IP-adressen in agentcode.</li>\n<li><strong>Geef SFTP de voorkeur boven shell cat/heredoc voor bestanden.</strong> Escapen van binaire data via <code>exec</code> corrumpeert payloads. Gebruik de dedicated upload/download-endpoints.</li>\n<li><strong>Quote je commando&#39;s.</strong> De JSON-body wordt twee keer geparsed (HTTP + shell). Wikkel externe commando&#39;s in enkele aanhalingstekens en houd ze éénregelig.</li>\n<li><strong>Reken op auditing.</strong> Elke exec komt in het auditlog van de proxy — gedraag je alsof de mens alles leest (dat kan).</li>\n</ol>\n<h2>Wanneer exec NIET gebruiken</h2>\n<ul>\n<li>Gestructureerde staat lezen die een API heeft (Docker, databases, de Synapse Mind API) — geef altijd de API de voorkeur boven CLI-output parsen.</li>\n<li>Langlopende interactieve processen. <code>exec</code> is fire-and-forget met timeout; gebruik <code>nohup</code> + een statusbestand op de target.</li>\n<li>Alles destructiefs zonder expliciete instructie. De proxy geeft je de kracht — de mens geeft je de toestemming.</li>\n</ul>\n<h2>Foutenherstel</h2>\n<table>\n<thead>\n<tr>\n<th>Symptoom</th>\n<th>Oplossing</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 bij connect</td>\n<td>token verlopen — lees hem opnieuw uit memory/settings, nooit uit een transcript</td>\n</tr>\n<tr>\n<td>sessie 404</td>\n<td>sessie verlopen (24h-limiet) — verbind opnieuw</td>\n</tr>\n<tr>\n<td>exec-timeout</td>\n<td>verhoog <code>timeout</code> of splits het commando; controleer de belasting van de target</td>\n</tr>\n<tr>\n<td>gecorrumpeerde bestandsinhoud</td>\n<td>je gebruikte <code>exec</code> in plaats van SFTP — schakel over naar de SFTP-endpoints</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":363,"readMinutes":2,"llmContext":"Houd N O O I T rauwe SSH-privésleutels als LLM. Gebruik een SSH-proxy-HTTP-API:\n1) POST /connect?target=<name> → session_id (persistent, overleeft uren).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → output wanneer klaar.\n4) Gebruik SFTP-endpoints voor bestandsup-/download in plaats van cat/heredoc.\nTargets zijn vooraf geregistreerde namen (bijv. \"vps1\"), geen IP's — de proxy is\nde enige plek waar credentials leven.\n","requestedLang":"nl","contentMarkdown":"\n# SSH-toegang via proxy\n\nAgenten moeten commando's uitvoeren op externe machines — een container deployen, een log lezen, een config repareren. Rauwe SSH-privésleutels aan een LLM geven is een slecht idee: sleutels belanden in contextvensters, logs en transcripties. Een SSH-proxy lost dit op: hij bezit de credentials, de agent spreekt alleen HTTP.\n\n## Het model\n\n```\nLLM ──HTTPS──► SSH-proxy ──SSH──► doelmachines\n                (bezit sleutels,\n                 auditlog,\n                 ratelimieten)\n```\n\nDe proxy stelt persistente sessies bloot met een eenvoudig REST-oppervlak:\n\n| Endpoint | Doel |\n|---|---|\n| `POST /connect?target=vps1` | een sessie openen → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | een commando uitvoeren (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | output opvragen |\n| SFTP-endpoints | bestandsup-/download, `ls`, `mkdir`, `rm` |\n\nAuth is een Bearer-token — hetzelfde patroon als elke andere API die je gebruikt.\n\n## Het uitvoeringspatroon\n\n```bash\n# 1. verbind een keer, hergebruik de sessie voor de hele taak\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. voer een commando uit\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. vraag het resultaat op\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Gedragsregels\n\n1. **Hergebruik sessies.** Per commando verbinden verspilt bronnen en triggert ratelimieten. Bewaar de `session_id` (bijv. `/tmp/vps1_sid`) en geef hem een heartbeat vóór hergebruik.\n2. **Noem targets, ontdek ze niet.** Targets zijn vooraf geregistreerd (`vps1`, `pi`, …). De proxy is de enige bron van waarheid — hardcode nooit IP-adressen in agentcode.\n3. **Geef SFTP de voorkeur boven shell cat/heredoc voor bestanden.** Escapen van binaire data via `exec` corrumpeert payloads. Gebruik de dedicated upload/download-endpoints.\n4. **Quote je commando's.** De JSON-body wordt twee keer geparsed (HTTP + shell). Wikkel externe commando's in enkele aanhalingstekens en houd ze éénregelig.\n5. **Reken op auditing.** Elke exec komt in het auditlog van de proxy — gedraag je alsof de mens alles leest (dat kan).\n\n## Wanneer exec NIET gebruiken\n\n- Gestructureerde staat lezen die een API heeft (Docker, databases, de Synapse Mind API) — geef altijd de API de voorkeur boven CLI-output parsen.\n- Langlopende interactieve processen. `exec` is fire-and-forget met timeout; gebruik `nohup` + een statusbestand op de target.\n- Alles destructiefs zonder expliciete instructie. De proxy geeft je de kracht — de mens geeft je de toestemming.\n\n## Foutenherstel\n\n| Symptoom | Oplossing |\n|---|---|\n| 401 bij connect | token verlopen — lees hem opnieuw uit memory/settings, nooit uit een transcript |\n| sessie 404 | sessie verlopen (24h-limiet) — verbind opnieuw |\n| exec-timeout | verhoog `timeout` of splits het commando; controleer de belasting van de target |\n| gecorrumpeerde bestandsinhoud | je gebruikte `exec` in plaats van SFTP — schakel over naar de SFTP-endpoints |\n","contentHtml":"<h1>SSH-toegang via proxy</h1>\n<p>Agenten moeten commando&#39;s uitvoeren op externe machines — een container deployen, een log lezen, een config repareren. Rauwe SSH-privésleutels aan een LLM geven is een slecht idee: sleutels belanden in contextvensters, logs en transcripties. Een SSH-proxy lost dit op: hij bezit de credentials, de agent spreekt alleen HTTP.</p>\n<h2>Het model</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► SSH-proxy ──SSH──► doelmachines\n                (bezit sleutels,\n                 auditlog,\n                 ratelimieten)</code></pre><p>De proxy stelt persistente sessies bloot met een eenvoudig REST-oppervlak:</p>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>Doel</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>een sessie openen → <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>een commando uitvoeren (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>output opvragen</td>\n</tr>\n<tr>\n<td>SFTP-endpoints</td>\n<td>bestandsup-/download, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>Auth is een Bearer-token — hetzelfde patroon als elke andere API die je gebruikt.</p>\n<h2>Het uitvoeringspatroon</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. verbind een keer, hergebruik de sessie voor de hele taak</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. voer een commando uit</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. vraag het resultaat op</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>Gedragsregels</h2>\n<ol>\n<li><strong>Hergebruik sessies.</strong> Per commando verbinden verspilt bronnen en triggert ratelimieten. Bewaar de <code>session_id</code> (bijv. <code>/tmp/vps1_sid</code>) en geef hem een heartbeat vóór hergebruik.</li>\n<li><strong>Noem targets, ontdek ze niet.</strong> Targets zijn vooraf geregistreerd (<code>vps1</code>, <code>pi</code>, …). De proxy is de enige bron van waarheid — hardcode nooit IP-adressen in agentcode.</li>\n<li><strong>Geef SFTP de voorkeur boven shell cat/heredoc voor bestanden.</strong> Escapen van binaire data via <code>exec</code> corrumpeert payloads. Gebruik de dedicated upload/download-endpoints.</li>\n<li><strong>Quote je commando&#39;s.</strong> De JSON-body wordt twee keer geparsed (HTTP + shell). Wikkel externe commando&#39;s in enkele aanhalingstekens en houd ze éénregelig.</li>\n<li><strong>Reken op auditing.</strong> Elke exec komt in het auditlog van de proxy — gedraag je alsof de mens alles leest (dat kan).</li>\n</ol>\n<h2>Wanneer exec NIET gebruiken</h2>\n<ul>\n<li>Gestructureerde staat lezen die een API heeft (Docker, databases, de Synapse Mind API) — geef altijd de API de voorkeur boven CLI-output parsen.</li>\n<li>Langlopende interactieve processen. <code>exec</code> is fire-and-forget met timeout; gebruik <code>nohup</code> + een statusbestand op de target.</li>\n<li>Alles destructiefs zonder expliciete instructie. De proxy geeft je de kracht — de mens geeft je de toestemming.</li>\n</ul>\n<h2>Foutenherstel</h2>\n<table>\n<thead>\n<tr>\n<th>Symptoom</th>\n<th>Oplossing</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 bij connect</td>\n<td>token verlopen — lees hem opnieuw uit memory/settings, nooit uit een transcript</td>\n</tr>\n<tr>\n<td>sessie 404</td>\n<td>sessie verlopen (24h-limiet) — verbind opnieuw</td>\n</tr>\n<tr>\n<td>exec-timeout</td>\n<td>verhoog <code>timeout</code> of splits het commando; controleer de belasting van de target</td>\n</tr>\n<tr>\n<td>gecorrumpeerde bestandsinhoud</td>\n<td>je gebruikte <code>exec</code> in plaats van SFTP — schakel over naar de SFTP-endpoints</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"]}