{"title":"Accès SSH via proxy","slug":"ssh-proxy-access","category":"llm-cookbook","summary":"Comment un agent LLM exécute des commandes sur des machines distantes en toute sécurité via une API de proxy SSH au lieu de clés SSH brutes.","audience":["llm"],"tags":["cookbook","ssh","proxy","infrastructure","remote-execution"],"difficulty":"intermediate","updated":"2026-09-26","word_count":418,"read_minutes":2,"llm_context":"Ne garde JAMAIS de clés privées SSH brutes en tant que LLM. Utilise une API HTTP de proxy SSH :\n1) POST /connect?target=<name> → session_id (persistante, survit des heures).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → sortie quand c'est fini.\n4) Utilise les endpoints SFTP pour l'upload/download de fichiers au lieu de cat/heredoc.\nLes targets sont des noms pré-enregistrés (ex. \"vps1\"), pas des IPs — le proxy est\nle seul endroit où vivent les identifiants.\n","lang":"fr","translated":true,"requested_lang":"fr","content_markdown":"\n# Accès SSH via proxy\n\nLes agents doivent exécuter des commandes sur des machines distantes — déployer un conteneur, lire un log, corriger une config. Remettre des clés privées SSH brutes à un LLM est une mauvaise idée : les clés finissent dans les fenêtres de contexte, les logs et les transcriptions. Un proxy SSH résout cela : il détient les identifiants, l'agent ne parle qu'en HTTP.\n\n## Le modèle\n\n```\nLLM ──HTTPS──► Proxy SSH ──SSH──► machines cibles\n                (détient les clés,\n                 journal d'audit,\n                 limites de débit)\n```\n\nLe proxy expose des sessions persistantes avec une surface REST simple :\n\n| Endpoint | Objectif |\n|---|---|\n| `POST /connect?target=vps1` | ouvrir une session → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | exécuter une commande (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | interroger la sortie |\n| Endpoints SFTP | up/download de fichiers, `ls`, `mkdir`, `rm` |\n\nL'authentification est un token Bearer — le même motif que toutes les autres API que tu utilises.\n\n## Le motif d'exécution\n\n```bash\n# 1. connecte-toi une fois, réutilise la session pour toute la tâche\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. exécute une commande\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. interroge le résultat\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Règles d'engagement\n\n1. **Réutilise les sessions.** Se connecter par commande gaspille des ressources et déclenche les limites de débit. Stocke le `session_id` (ex. `/tmp/vps1_sid`) et fais-lui un heartbeat avant réutilisation.\n2. **Nomme les targets, ne les découvre pas.** Les targets sont pré-enregistrés (`vps1`, `pi`, …). Le proxy est la source unique de vérité — n'intègre jamais d'adresses IP en dur dans le code d'agent.\n3. **Préfère SFTP à cat/heredoc de shell pour les fichiers.** Échapper des données binaires via `exec` corrompt les payloads. Utilise les endpoints dédiés d'upload/download.\n4. **Cite tes commandes.** Le corps JSON est parsé deux fois (HTTP + shell). Enveloppe les commandes distantes dans des guillemets simples et garde-les sur une ligne.\n5. **Suppose l'audit.** Chaque exec atterrit dans le journal d'audit du proxy — comporte-toi comme si l'humain lisait tout (il peut).\n\n## Quand NE PAS utiliser exec\n\n- Lire un état structuré qui a une API (Docker, bases de données, la Mind API de Synapse) — préfère toujours l'API au parsing de sortie CLI.\n- Processus interactifs de longue durée. `exec` est fire-and-forget avec timeout ; utilise `nohup` + un fichier d'état sur la cible.\n- Tout ce qui est destructif sans instruction explicite. Le proxy te donne le pouvoir — l'humain te donne la permission.\n\n## Récupération d'erreurs\n\n| Symptôme | Solution |\n|---|---|\n| 401 au connect | token expiré — relis-le depuis memory/settings, jamais depuis une transcription |\n| session 404 | session expirée (plafond 24h) — reconnecte-toi |\n| timeout d'exec | augmente `timeout` ou découpe la commande ; vérifie la charge de la cible |\n| contenu de fichier corrompu | tu as utilisé `exec` au lieu de SFTP — passe aux endpoints SFTP |\n","content_html":"<h1>Accès SSH via proxy</h1>\n<p>Les agents doivent exécuter des commandes sur des machines distantes — déployer un conteneur, lire un log, corriger une config. Remettre des clés privées SSH brutes à un LLM est une mauvaise idée : les clés finissent dans les fenêtres de contexte, les logs et les transcriptions. Un proxy SSH résout cela : il détient les identifiants, l&#39;agent ne parle qu&#39;en HTTP.</p>\n<h2>Le modèle</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► Proxy SSH ──SSH──► machines cibles\n                (détient les clés,\n                 journal d&#x27;audit,\n                 limites de débit)</code></pre><p>Le proxy expose des sessions persistantes avec une surface REST simple :</p>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>Objectif</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>ouvrir une session → <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>exécuter une commande (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>interroger la sortie</td>\n</tr>\n<tr>\n<td>Endpoints SFTP</td>\n<td>up/download de fichiers, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>L&#39;authentification est un token Bearer — le même motif que toutes les autres API que tu utilises.</p>\n<h2>Le motif d&#39;exécution</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. connecte-toi une fois, réutilise la session pour toute la tâche</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. exécute une commande</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. interroge le résultat</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>Règles d&#39;engagement</h2>\n<ol>\n<li><strong>Réutilise les sessions.</strong> Se connecter par commande gaspille des ressources et déclenche les limites de débit. Stocke le <code>session_id</code> (ex. <code>/tmp/vps1_sid</code>) et fais-lui un heartbeat avant réutilisation.</li>\n<li><strong>Nomme les targets, ne les découvre pas.</strong> Les targets sont pré-enregistrés (<code>vps1</code>, <code>pi</code>, …). Le proxy est la source unique de vérité — n&#39;intègre jamais d&#39;adresses IP en dur dans le code d&#39;agent.</li>\n<li><strong>Préfère SFTP à cat/heredoc de shell pour les fichiers.</strong> Échapper des données binaires via <code>exec</code> corrompt les payloads. Utilise les endpoints dédiés d&#39;upload/download.</li>\n<li><strong>Cite tes commandes.</strong> Le corps JSON est parsé deux fois (HTTP + shell). Enveloppe les commandes distantes dans des guillemets simples et garde-les sur une ligne.</li>\n<li><strong>Suppose l&#39;audit.</strong> Chaque exec atterrit dans le journal d&#39;audit du proxy — comporte-toi comme si l&#39;humain lisait tout (il peut).</li>\n</ol>\n<h2>Quand NE PAS utiliser exec</h2>\n<ul>\n<li>Lire un état structuré qui a une API (Docker, bases de données, la Mind API de Synapse) — préfère toujours l&#39;API au parsing de sortie CLI.</li>\n<li>Processus interactifs de longue durée. <code>exec</code> est fire-and-forget avec timeout ; utilise <code>nohup</code> + un fichier d&#39;état sur la cible.</li>\n<li>Tout ce qui est destructif sans instruction explicite. Le proxy te donne le pouvoir — l&#39;humain te donne la permission.</li>\n</ul>\n<h2>Récupération d&#39;erreurs</h2>\n<table>\n<thead>\n<tr>\n<th>Symptôme</th>\n<th>Solution</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 au connect</td>\n<td>token expiré — relis-le depuis memory/settings, jamais depuis une transcription</td>\n</tr>\n<tr>\n<td>session 404</td>\n<td>session expirée (plafond 24h) — reconnecte-toi</td>\n</tr>\n<tr>\n<td>timeout d&#39;exec</td>\n<td>augmente <code>timeout</code> ou découpe la commande ; vérifie la charge de la cible</td>\n</tr>\n<tr>\n<td>contenu de fichier corrompu</td>\n<td>tu as utilisé <code>exec</code> au lieu de SFTP — passe aux endpoints 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":418,"readMinutes":2,"llmContext":"Ne garde JAMAIS de clés privées SSH brutes en tant que LLM. Utilise une API HTTP de proxy SSH :\n1) POST /connect?target=<name> → session_id (persistante, survit des heures).\n2) POST /<session_id>/exec {command, timeout} → exec_id.\n3) GET /<session_id>/exec/<exec_id> → sortie quand c'est fini.\n4) Utilise les endpoints SFTP pour l'upload/download de fichiers au lieu de cat/heredoc.\nLes targets sont des noms pré-enregistrés (ex. \"vps1\"), pas des IPs — le proxy est\nle seul endroit où vivent les identifiants.\n","requestedLang":"fr","contentMarkdown":"\n# Accès SSH via proxy\n\nLes agents doivent exécuter des commandes sur des machines distantes — déployer un conteneur, lire un log, corriger une config. Remettre des clés privées SSH brutes à un LLM est une mauvaise idée : les clés finissent dans les fenêtres de contexte, les logs et les transcriptions. Un proxy SSH résout cela : il détient les identifiants, l'agent ne parle qu'en HTTP.\n\n## Le modèle\n\n```\nLLM ──HTTPS──► Proxy SSH ──SSH──► machines cibles\n                (détient les clés,\n                 journal d'audit,\n                 limites de débit)\n```\n\nLe proxy expose des sessions persistantes avec une surface REST simple :\n\n| Endpoint | Objectif |\n|---|---|\n| `POST /connect?target=vps1` | ouvrir une session → `session_id` |\n| `POST /<sid>/exec` `{command, timeout}` | exécuter une commande (async) → `exec_id` |\n| `GET /<sid>/exec/<exec_id>` | interroger la sortie |\n| Endpoints SFTP | up/download de fichiers, `ls`, `mkdir`, `rm` |\n\nL'authentification est un token Bearer — le même motif que toutes les autres API que tu utilises.\n\n## Le motif d'exécution\n\n```bash\n# 1. connecte-toi une fois, réutilise la session pour toute la tâche\nSID=$(curl -s -H \"Authorization: Bearer $KEY\" \\\n  \"https://ssh-proxy.example/connect?target=vps1\" | jq -r .session_id)\n\n# 2. exécute une commande\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. interroge le résultat\ncurl -s -H \"Authorization: Bearer $KEY\" \"https://ssh-proxy.example/$SID/exec/$EID\"\n```\n\n## Règles d'engagement\n\n1. **Réutilise les sessions.** Se connecter par commande gaspille des ressources et déclenche les limites de débit. Stocke le `session_id` (ex. `/tmp/vps1_sid`) et fais-lui un heartbeat avant réutilisation.\n2. **Nomme les targets, ne les découvre pas.** Les targets sont pré-enregistrés (`vps1`, `pi`, …). Le proxy est la source unique de vérité — n'intègre jamais d'adresses IP en dur dans le code d'agent.\n3. **Préfère SFTP à cat/heredoc de shell pour les fichiers.** Échapper des données binaires via `exec` corrompt les payloads. Utilise les endpoints dédiés d'upload/download.\n4. **Cite tes commandes.** Le corps JSON est parsé deux fois (HTTP + shell). Enveloppe les commandes distantes dans des guillemets simples et garde-les sur une ligne.\n5. **Suppose l'audit.** Chaque exec atterrit dans le journal d'audit du proxy — comporte-toi comme si l'humain lisait tout (il peut).\n\n## Quand NE PAS utiliser exec\n\n- Lire un état structuré qui a une API (Docker, bases de données, la Mind API de Synapse) — préfère toujours l'API au parsing de sortie CLI.\n- Processus interactifs de longue durée. `exec` est fire-and-forget avec timeout ; utilise `nohup` + un fichier d'état sur la cible.\n- Tout ce qui est destructif sans instruction explicite. Le proxy te donne le pouvoir — l'humain te donne la permission.\n\n## Récupération d'erreurs\n\n| Symptôme | Solution |\n|---|---|\n| 401 au connect | token expiré — relis-le depuis memory/settings, jamais depuis une transcription |\n| session 404 | session expirée (plafond 24h) — reconnecte-toi |\n| timeout d'exec | augmente `timeout` ou découpe la commande ; vérifie la charge de la cible |\n| contenu de fichier corrompu | tu as utilisé `exec` au lieu de SFTP — passe aux endpoints SFTP |\n","contentHtml":"<h1>Accès SSH via proxy</h1>\n<p>Les agents doivent exécuter des commandes sur des machines distantes — déployer un conteneur, lire un log, corriger une config. Remettre des clés privées SSH brutes à un LLM est une mauvaise idée : les clés finissent dans les fenêtres de contexte, les logs et les transcriptions. Un proxy SSH résout cela : il détient les identifiants, l&#39;agent ne parle qu&#39;en HTTP.</p>\n<h2>Le modèle</h2>\n<pre><code class=\"hljs language-plaintext\">LLM ──HTTPS──► Proxy SSH ──SSH──► machines cibles\n                (détient les clés,\n                 journal d&#x27;audit,\n                 limites de débit)</code></pre><p>Le proxy expose des sessions persistantes avec une surface REST simple :</p>\n<table>\n<thead>\n<tr>\n<th>Endpoint</th>\n<th>Objectif</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>POST /connect?target=vps1</code></td>\n<td>ouvrir une session → <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>exécuter une commande (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>interroger la sortie</td>\n</tr>\n<tr>\n<td>Endpoints SFTP</td>\n<td>up/download de fichiers, <code>ls</code>, <code>mkdir</code>, <code>rm</code></td>\n</tr>\n</tbody></table>\n<p>L&#39;authentification est un token Bearer — le même motif que toutes les autres API que tu utilises.</p>\n<h2>Le motif d&#39;exécution</h2>\n<pre><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># 1. connecte-toi une fois, réutilise la session pour toute la tâche</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. exécute une commande</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. interroge le résultat</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>Règles d&#39;engagement</h2>\n<ol>\n<li><strong>Réutilise les sessions.</strong> Se connecter par commande gaspille des ressources et déclenche les limites de débit. Stocke le <code>session_id</code> (ex. <code>/tmp/vps1_sid</code>) et fais-lui un heartbeat avant réutilisation.</li>\n<li><strong>Nomme les targets, ne les découvre pas.</strong> Les targets sont pré-enregistrés (<code>vps1</code>, <code>pi</code>, …). Le proxy est la source unique de vérité — n&#39;intègre jamais d&#39;adresses IP en dur dans le code d&#39;agent.</li>\n<li><strong>Préfère SFTP à cat/heredoc de shell pour les fichiers.</strong> Échapper des données binaires via <code>exec</code> corrompt les payloads. Utilise les endpoints dédiés d&#39;upload/download.</li>\n<li><strong>Cite tes commandes.</strong> Le corps JSON est parsé deux fois (HTTP + shell). Enveloppe les commandes distantes dans des guillemets simples et garde-les sur une ligne.</li>\n<li><strong>Suppose l&#39;audit.</strong> Chaque exec atterrit dans le journal d&#39;audit du proxy — comporte-toi comme si l&#39;humain lisait tout (il peut).</li>\n</ol>\n<h2>Quand NE PAS utiliser exec</h2>\n<ul>\n<li>Lire un état structuré qui a une API (Docker, bases de données, la Mind API de Synapse) — préfère toujours l&#39;API au parsing de sortie CLI.</li>\n<li>Processus interactifs de longue durée. <code>exec</code> est fire-and-forget avec timeout ; utilise <code>nohup</code> + un fichier d&#39;état sur la cible.</li>\n<li>Tout ce qui est destructif sans instruction explicite. Le proxy te donne le pouvoir — l&#39;humain te donne la permission.</li>\n</ul>\n<h2>Récupération d&#39;erreurs</h2>\n<table>\n<thead>\n<tr>\n<th>Symptôme</th>\n<th>Solution</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>401 au connect</td>\n<td>token expiré — relis-le depuis memory/settings, jamais depuis une transcription</td>\n</tr>\n<tr>\n<td>session 404</td>\n<td>session expirée (plafond 24h) — reconnecte-toi</td>\n</tr>\n<tr>\n<td>timeout d&#39;exec</td>\n<td>augmente <code>timeout</code> ou découpe la commande ; vérifie la charge de la cible</td>\n</tr>\n<tr>\n<td>contenu de fichier corrompu</td>\n<td>tu as utilisé <code>exec</code> au lieu de SFTP — passe aux endpoints 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"]}