Comment une liste de contrôle de préparation SSH améliore la sécurité des agents
Utilisez cette liste de contrôle SSH pour tester l’analyse des résultats, l’identité d’hôte, l’idempotence, les approbations et les états distants inconnus avant d’accorder un accès shell aux agents.

HTTP en lecture seule est un premier canal indulgent pour un agent. La requête a une méthode, une cible délimitée, un code d’état, des en-têtes et, généralement, un corps au format documenté. Un GET qui échoue ne modifie normalement pas le service. Des identifiants peuvent tout de même être mal gérés ou des données sensibles exposées, mais le modèle d’exécution donne aux relecteurs un élément compact à évaluer.
SSH change l’unité de risque. L’agent n’appelle pas une opération définie. Il envoie du texte à un shell distant dont le comportement dépend du compte de connexion, du shell, du répertoire de travail, de l’environnement, du système d’exploitation, des outils installés, des règles de citation et de l’état actuel de la machine. Une commande peut réussir en partie, se déconnecter avant de communiquer son résultat et laisser la prochaine tentative refaire le travail.
Cela ne rend pas SSH inadapté aux agents. Cela signifie que « le canal HTTP a fonctionné » ne suffit pas pour ajouter SSH. Le test d’admission doit couvrir cinq propriétés distinctes : une analyse fiable des résultats, une identité d’hôte vérifiée, des commandes qui peuvent être répétées sans risque, des approbations qui décrivent les conséquences et une gestion explicite de l’état distant inconnu. Si l’une d’elles manque, gardez le second canal désactivé.
Considérez SSH comme un modèle d’exécution différent
La préparation à SSH commence par reconnaître qu’un shell distant n’est pas un transport HTTP avec une URL différente. Les API HTTP exposent des opérations choisies par le serveur. SSH expose un interpréteur choisi par le compte distant, puis demande à votre client d’y transporter une chaîne de commande non structurée.
Avec un appel HTTP en lecture seule, le relecteur peut souvent déduire l’effet de GET, de l’hôte et du chemin. Une réponse comme l’état 404 a aussi une place conventionnelle dans le protocole, même si l’application lui donne un sens propre à son domaine. Avec SSH, test -f /srv/app/release && cat /srv/app/release peut renvoyer 1 parce que le fichier est absent, alors que cat /srv/app/release peut renvoyer 1 parce que l’autorisation est refusée. Si votre analyseur réduit les deux à « la commande a échoué », l’agent ne peut pas décider quoi faire en sécurité.
L’environnement distant change également le sens. sed -i se comporte différemment selon les systèmes d’exploitation courants. Un shell de connexion peut charger des fichiers de démarrage qui impriment des bannières sur stdout. PATH peut trouver un programme intermédiaire à la place du binaire attendu. Les paramètres régionaux peuvent modifier les diagnostics lisibles par une personne. Un pseudo-terminal peut mêler un comportement prévu pour une personne à une commande prévue pour un analyseur. Aucune de ces variables n’apparaît dans un schéma de requête HTTP classique.
Rédigez un contrat de canal avant d’autoriser les commandes. Il doit préciser l’utilisateur distant, les hôtes acceptés, le shell, le répertoire initial, la politique d’environnement, l’interdiction ou non d’un pseudo-terminal, la durée maximale, les limites de sortie et l’enveloppe de résultat exacte. Épinglez les chemins des exécutables pour les actions sensibles. Définissez des paramètres régionaux connus si vous devez traiter du texte. Préférez une sortie exploitable par machine fournie par le programme distant, sans jamais supposer qu’une option JSON rend le shell environnant structuré.
Utilisez une sonde non interactive comme première porte :
/usr/bin/ssh \
-o BatchMode=yes \
-o StrictHostKeyChecking=yes \
-o ConnectTimeout=10 \
-T [email protected] \
'umask 077; printf "%s\n" "{\"probe\":\"ssh-ready\",\"version\":1}"'
OpenSSH indique que BatchMode=yes désactive les invites, y compris celles de mot de passe et de confirmation de clé d’hôte. -T désactive l’allocation de pseudo-terminal. StrictHostKeyChecking=yes refuse les clés inconnues ou modifiées. La sortie stdout attendue est un seul objet JSON, stderr est vide et le statut de sortie distant est zéro. Testez chaque écart séparément. Un système incapable de distinguer un hôte inconnu d’un JSON malformé n’a pas franchi cette porte.
Rendez l’enveloppe de résultat sans ambiguïté
L’analyse des résultats n’est prête que lorsque les erreurs de transport, la fin du processus distant, stdout et stderr restent séparés. Les réduire à un seul champ de texte crée une confiance trompeuse et dangereuse.
La RFC 4254 définit stdout comme des données de canal et stderr comme des données étendues de canal. Elle définit aussi les messages exit-status et exit-signal, mais le choix des mots compte : envoyer un statut de sortie est recommandé, pas obligatoire, et le client peut l’ignorer. OpenSSH ajoute une convention locale utile au-dessus de ce protocole. Sa commande ssh se termine avec le statut de la commande distante, ou 255 lorsqu’une erreur survient.
Ne transformez pas cette convention en vérité universelle. Un programme distant peut lui-même quitter avec le code 255, qui entre en collision avec la valeur d’erreur client d’OpenSSH à la frontière du processus. Un serveur ou une bibliothèque peut omettre le message exit-status. Un signal n’est pas équivalent à une sortie non nulle ordinaire. Le canal peut se fermer après avoir produit une sortie, avant que votre client reçoive un statut terminal.
Votre résultat interne doit ressembler à un enregistrement typé, et non à une transcription :
{
"phase": "completed",
"transport": "ok",
"host": "host.example",
"host_key_fingerprint": "SHA256:verified-value",
"exit_status": 0,
"exit_signal": null,
"stdout": "{\"state\":\"present\",\"release\":\"2026.07\"}\n",
"stderr": "",
"truncated": false,
"started_at": "request timestamp",
"finished_at": "result timestamp"
}
Gardez exit_status nullable. Faites en sorte que phase distingue au minimum rejected, not-started, started, completed et unknown. Enregistrez la troncature comme une donnée. Ne tronquez jamais silencieusement une sortie avant de remettre le reste à l’agent comme s’il était complet. Joignez l’empreinte d’hôte vérifiée utilisée pour cette connexion, et pas seulement le nom d’hôte demandé par l’agent.
Testez ensuite une matrice de résultats. Exécutez une commande qui réussit sans sortie, une autre qui quitte avec 7 après avoir écrit sur les deux flux, une commande tuée par un signal, une qui dépasse l’échéance, une qui émet de l’UTF-8 invalide si votre pile accepte les octets, et une qui dépasse chaque limite de sortie. Coupez la connexion cliente pendant qu’une commande distante est en veille, puis inspectez le résultat. Votre adaptateur ne doit pas inventer exit_status: 0, déduire la réussite depuis stdout ou appeler un délai d’attente « échec » lorsqu’il ne peut pas prouver que la commande a été exécutée.
La composition dans le shell mérite ses propres tests. POSIX indique qu’un pipeline renvoie normalement le statut de sa dernière commande, sauf si pipefail est activé. Ainsi, generate | upload peut signaler une réussite parce que upload a accepté une entrée vide après l’échec de generate. Ne dépendez pas des réglages par défaut d’un shell interactif. Placez les opérations à plusieurs commandes dans des scripts revus, avec une gestion explicite des erreurs et une version, puis appelez un seul point d’entrée de script.
Vérifiez les hôtes avant qu’un agent puisse les atteindre
La vérification des hôtes relève de l’inventaire, pas d’une invite à laquelle un agent devrait répondre. Un identifiant utilisateur valide prouve qui est le client auprès du serveur. La clé d’hôte du serveur prouve quel serveur a répondu au client. Vous avez besoin des deux.
N’utilisez jamais StrictHostKeyChecking=no comme solution d’automatisation. La documentation actuelle d’OpenSSH indique que ce réglage peut ajouter automatiquement de nouvelles clés et autoriser la poursuite d’une connexion lorsqu’une clé d’hôte a changé, sous certaines restrictions. accept-new est préférable, car il refuse les clés modifiées, mais fait encore confiance à la première connexion. Pour un canal d’agent, préparez la confiance avant l’exécution et utilisez StrictHostKeyChecking=yes.
ssh-keyscan aide à collecter des clés d’hôte publiques, mais ne les authentifie pas. Son manuel avertit lui-même qu’un attaquant réseau peut substituer une clé et indique qu’il faut vérifier la sortie hors bande ou ne l’utiliser que sur un réseau de confiance. Copier directement sa sortie en direct dans known_hosts transforme une étape de vérification en enregistrement de la première réponse reçue.
Obtenez les empreintes via un plan de contrôle indépendant : console d’instance cloud, enregistrement de création d’image, dépôt de configuration revu par le propriétaire de l’hôte ou remise directe par un administrateur. Stockez le nom d’hôte, le port, les algorithmes de clés d’hôte autorisés, les empreintes, le propriétaire, l’environnement et la procédure de rotation. Vérifiez aussi les alias et les hôtes rebonds. La destination finale peut être parfaitement épinglée, mais un hôte rebond non épinglé rompt le chemin de confiance.
Un contrôle d’admission utile compare les clés observées et approuvées sans modifier la confiance :
ssh-keyscan -T 5 -t ed25519 host.example > observed.keys
ssh-keygen -lf observed.keys
La sortie de l’empreinte contient la longueur en bits, l’empreinte, l’étiquette d’hôte et le type de clé. Une personne ou un service d’inventaire de confiance compare l’empreinte à la valeur fournie indépendamment. L’automatisation ne doit installer l’entrée known-hosts qu’après cette correspondance. L’analyse est une preuve à comparer, pas une preuve à laquelle faire confiance.
Planifiez la rotation avant d’appliquer l’épinglage. UpdateHostKeys d’OpenSSH peut apprendre des clés supplémentaires après l’authentification du serveur avec une clé déjà approuvée, ce qui facilite une rotation progressive. Que vous utilisiez cette extension ou distribuiez un nouveau jeu de known-hosts, définissez une période de chevauchement et une procédure d’urgence. Une clé modifiée doit arrêter l’exécution et produire une erreur d’identité distincte. Elle ne doit jamais déclencher une nouvelle tentative générique, une suppression automatique de l’ancienne entrée ni une carte d’approbation demandant à un relecteur pressé d’accepter une empreinte inexpliquée.
Exigez l’idempotence à la frontière de l’effet
Une commande ne peut être réessayée sans risque que si sa répétition, après toute exécution partielle, produit le même état voulu sans dupliquer l’effet. Une syntaxe en lecture seule ne lui confère pas cette propriété, et un statut de sortie nul ne la prouve pas.
Certaines commandes se répètent naturellement : lire un fichier fixe, vérifier l’état d’un service ou créer un répertoire avec mkdir -p sous des permissions contrôlées. D’autres nécessitent des garde-fous. Ajouter une ligne avec echo ... >> file, envoyer une notification, créer un utilisateur avec un identifiant généré, facturer un compte via un outil local ou redémarrer un service ne sont pas sûrs simplement parce que la commande shell est courte.
Le conseil habituel de « réessayer les erreurs SSH transitoires » est erroné à ce niveau. Il est répandu parce que se reconnecter résout de nombreux problèmes réseau et que les bibliothèques clientes HTTP normalisent les nouvelles tentatives. SSH peut perdre la connexion après que le processus distant a validé sa modification, mais avant que le client reçoive le statut de sortie. Une nouvelle tentative automatique répète alors une action terminée.
Déplacez la répétabilité dans l’opération distante. Donnez à chaque requête qui modifie l’état un identifiant d’opération stable, généré avant l’approbation. Stockez cet identifiant à côté de l’effet dans la même transaction lorsque c’est possible. Si l’opération s’exécute de nouveau, renvoyez le résultat enregistré au lieu d’appliquer encore la mutation. Lorsqu’aucune transaction ne couvre à la fois le marqueur et l’effet, ajoutez une requête de réconciliation capable de déterminer quel côté a abouti.
Un petit script de déploiement peut rendre le contrat visible :
#!/bin/sh
set -eu
op_id=$1
release=$2
state_dir=/var/lib/agent-ops
record="$state_dir/$op_id"
test -d "$state_dir" || exit 70
if test -f "$record"; then
cat "$record"
exit 0
fi
current=$(/usr/bin/readlink /srv/app/current || true)
if test "$current" = "/srv/app/releases/$release"; then
/usr/bin/printf '{"operation":"%s","state":"already-current"}\n' "$op_id"
exit 0
fi
test -d "/srv/app/releases/$release" || exit 66
/usr/bin/ln -sfn "/srv/app/releases/$release" /srv/app/current.new
/usr/bin/mv -f /srv/app/current.new /srv/app/current
/usr/bin/printf '{"operation":"%s","state":"changed","release":"%s"}\n' \
"$op_id" "$release" > "$record.tmp"
/usr/bin/mv -f "$record.tmp" "$record"
cat "$record"
Cet exemple n’est pas atomique dans tous les cas. Le remplacement du lien symbolique et l’enregistrement de l’opération sont deux modifications du système de fichiers, donc un plantage entre les deux laisse un vide. La vérification explicite de current réconcilie ce vide précis. Votre opération a besoin d’un garde-fou lié à son propre effet, et non d’un marqueur générique copié depuis ce script.
Classez chaque commande autorisée comme lecture seule, convergente, dédupliquée ou non répétable. Convergente signifie qu’une exécution répétée progresse vers un état déclaré, comme définir une valeur de configuration. Dédupliquée signifie que le côté distant reconnaît l’identifiant d’opération. Les actions non répétables exigent une requête d’état distincte et une décision humaine après une incertitude. Si le propriétaire ne peut pas classer une commande, ne l’autorisez pas.
Montrez l’effet à la personne qui approuve, pas le texte shell
Une approbation n’est utile que si un relecteur peut identifier la cible, l’autorité, l’effet voulu et la pire conséquence plausible avant d’agir. Le texte shell brut est une preuve nécessaire, mais un mauvais résumé.
Comparez systemctl restart api à une approbation qui indique : hôte de production api-03, utilisateur distant deploy, redémarrage du service api, les connexions actives peuvent être interrompues, identifiant d’opération rel-2026-07-24-04, version de commande restart-service/v2. La seconde description fournit au relecteur des faits qu’il peut confronter à une modification. Elle révèle aussi le contexte manquant. Si l’agent ne peut pas dire quel hôte ou quel service il affectera, il ne doit pas recevoir d’approbation.
La charge utile d’approbation doit être liée à la requête d’exécution exacte. Incluez l’hôte et le port canoniques, l’empreinte vérifiée, le compte distant, le condensat de la commande ou du script revu, les arguments normalisés, le répertoire de travail, les ajouts d’environnement, le délai d’attente, le changement de privilèges demandé, l’identifiant d’opération et le caractère répétable ou non de l’action. Hachez cette charge utile et n’exécutez que le hachage approuvé. Sinon, un agent peut obtenir l’approbation pour une commande et modifier un argument avant l’envoi.
Affichez la citation shell exactement, sans obliger un relecteur à l’exécuter mentalement. N’analysez que les formes de commande que vous contrôlez. Si du texte shell arbitraire reste dans le périmètre, étiquetez-le comme arbitraire et présentez toute la chaîne sans omission. Signalez les redirections, substitutions de commande, pipes, exécution en arrière-plan, sudo, suppression de fichiers, changements d’autorisations, opérations sur des paquets, contrôle de services et téléchargements réseau. Un signalement n’est pas un verdict. Il indique au relecteur où les conséquences peuvent se cacher.
La portée d’approbation doit se réduire à mesure que les effets augmentent. Une approbation de session peut convenir à des sondes fixes en lecture seule sur un inventaire approuvé. Une approbation par appel convient aux modifications d’état, à l’utilisation d’un compte distant privilégié ou aux commandes dont les arguments choisissent la cible. Ne laissez pas une approbation inoffensive pour uname autoriser silencieusement un déploiement ultérieur parce que les deux utilisent la même clé SSH.
Les contrôles fixes de Sallyport correspondent bien à cette séparation : son autorisation de session identifie un nouveau processus d’agent, tandis qu’une clé par appel peut exiger une approbation à chaque utilisation. Le travail de conception essentiel reste dans la requête : la carte doit révéler l’effet distant, car posséder un canal approuvé n’explique pas ce qu’une commande fera.
Testez l’intégrité de l’approbation, pas seulement son apparence. Modifiez un octet d’un argument approuvé et vérifiez que l’exécution s’arrête. Mettez en concurrence deux requêtes qui réutilisent un identifiant d’opération. Révoquez la session entre l’approbation et l’envoi. Verrouillez le magasin d’identifiants après l’apparition de la carte. Chaque test doit se terminer par un refus enregistré ou une requête qui doit être approuvée de nouveau, jamais par une poursuite au mieux.
Traitez l’état distant inconnu comme un résultat à part entière
Inconnu est un résultat valide chaque fois que le client ne peut pas prouver que l’effet distant a été réalisé. L’appeler un échec incite aux nouvelles tentatives. L’appeler une réussite masque le travail inachevé.
Prenons une séquence courante. Un agent se connecte, lance un script et le script remplace un fichier de configuration. Le rechargement du service commence. À ce moment, le chemin réseau tombe. Le client ne reçoit ni statut de sortie ni stdout final. Un délai local expire et marque l’appel comme échoué. L’agent réessaie. La seconde exécution voit le nouveau fichier, déclenche un autre rechargement et peut écraser l’enregistrement de diagnostic de la première. L’appel initial a réellement modifié quelque chose, même si le client n’a jamais observé sa fin.
La RFC 4254 rend cette ambiguïté peu surprenante. Le protocole transporte séparément la sortie de commande, le statut de sortie, le signal de sortie, EOF et la fermeture de canal. Il recommande de renvoyer le statut de sortie, sans le garantir. Même une fermeture propre du canal n’informe le client que sur le canal, pas sur l’atteinte par un système externe de l’état métier demandé.
Définissez la machine à états avant la mise en production :
not_started: la connexion, l’identité, l’authentification ou l’approbation a échoué avant l’envoi.started: le côté distant a accepté la commande, mais aucun résultat terminal n’existe encore.completed: un statut terminal et toute la sortie bornée sont arrivés.unknown: l’envoi a pu avoir lieu, mais le client a perdu la preuve de la fin.reconciled: une requête indépendante a ensuite établi l’état obtenu.
Seul not_started peut généralement être réessayé automatiquement, et même cette étiquette doit venir d’une frontière fiable. Si les octets contenant la commande ont pu atteindre le serveur, utilisez unknown. Une échéance n’annule pas un processus distant sans protocole d’annulation confirmé. Fermer le socket client n’est pas ce protocole.
Toute commande qui modifie l’état a besoin d’un plan de réconciliation nommé avant l’approbation. Ce plan peut interroger l’enregistrement de l’opération, comparer un identifiant de version déployée, lire l’état du gestionnaire de services ou demander au système en aval l’identifiant d’opération stable. Exécutez la réconciliation avec un identifiant en lecture seule lorsque c’est possible. Conservez la requête initiale, sa sortie partielle, les horodatages, l’empreinte d’hôte et l’identifiant d’opération afin que le suivi réponde à la bonne question.
Définissez un budget d’état inconnu. Décidez combien de temps le système attend, qui reçoit l’alerte, quelles actions sont bloquées derrière l’opération non résolue et à quel moment une personne prend le relais. Ne laissez jamais deux opérations incertaines se concurrencer sur la même ressource. Sérialisez-les par ressource ou utilisez un verrou distant avec un propriétaire et une politique d’expiration qui survit à la déconnexion du client.
Limitez le compte distant avant d’étendre les commandes
La préparation à SSH dépend davantage de l’autorité distante que de l’intention côté client. Un écran d’approbation parfait ne compense pas un compte de connexion capable de réécrire l’hôte.
Créez un compte dédié au canal de l’agent. Donnez-lui le minimum d’accès au système de fichiers et de permissions de service nécessaires aux opérations autorisées. Évitez un compte administrateur général. Si une élévation est indispensable, autorisez des commandes nommées avec des chemins fixes et des arguments contrôlés. Considérez sudo sans restriction, les échappements shell dans les programmes autorisés, les répertoires de scripts modifiables et les exécutables modifiables comme des voies équivalentes vers un accès plus large.
Les restrictions authorized_keys d’OpenSSH peuvent réduire l’exposition d’un identifiant. Selon votre conception, une commande forcée peut faire passer chaque connexion par un répartiteur, tandis que des options peuvent désactiver les pseudo-terminaux, le transfert d’agent, le transfert X11 et le transfert de ports. La configuration serveur peut aussi restreindre les transferts. Consultez le manuel réel du serveur et testez la configuration effective, car un include ou un bloc match permissif peut annuler votre hypothèse.
Un répartiteur doit accepter un petit nom d’opération et des données, valider les deux, puis appeler un exécutable par chemin absolu sans reconstruire de texte shell arbitraire. Par exemple, read-release peut ne prendre aucun argument, tandis que activate-release accepte un identifiant de version respectant un format strict. Le canal SSH reste le transport, mais la surface distante commence à ressembler à une API définie.
Ne transférez pas l’agent d’authentification du développeur dans une session autonome. Le transfert d’agent permet au côté distant de demander des signatures via le socket transféré pendant toute la connexion. Un hôte distant compromis ne peut peut-être pas extraire la clé privée, mais il peut utiliser la capacité de signature. Donnez au canal un identifiant dédié dont l’autorisation côté serveur est déjà limitée.
Vérifiez la propriété du système de fichiers jusqu’à chaque exécutable et fichier de configuration. Si le compte restreint peut modifier un répertoire parent, remplacer le répartiteur, influencer un fichier de démarrage chargé ou ajouter un binaire via PATH, la liste d’autorisation est décorative. Vérifiez aussi les interpréteurs. L’autorisation d’exécuter un interpréteur large signifie souvent l’autorisation de faire tout ce que le compte peut faire.
Gardez le premier ensemble de commandes sans surprise : lectures d’inventaire fixes, requêtes de santé à sortie bornée et une mutation convergente avec un chemin de réconciliation testé. Transfert de ports, shells interactifs, téléversements arbitraires, gestion de paquets et commandes root libres relèvent d’examens ultérieurs, s’ils doivent en relever.
Prouvez l’observabilité malgré les troncatures et les déconnexions
Les preuves d’audit sont prêtes lorsqu’elles peuvent reconstruire l’autorisation, l’envoi, l’identité distante et le résultat observé sans dépendre du résumé de l’agent. Les journaux doivent conserver l’incertitude au lieu de la faire disparaître.
Enregistrez un identifiant de requête et un identifiant d’opération stables, l’identité du processus ou de la session de l’agent, la décision d’approbation, la méthode de l’approbateur, le hachage de la charge utile approuvée, la cible canonique, l’empreinte de clé d’hôte, le compte distant, l’heure de début, l’heure d’envoi, l’heure terminale, le statut ou le signal de sortie, le nombre d’octets de chaque flux, les indicateurs de troncature et la classification finale de l’état. Gardez stdout et stderr séparés. Si la politique interdit de conserver la sortie complète, stockez la portion permise avec un condensat et des métadonnées de rétention claires.
Les limites de sortie nécessitent deux comportements : arrêter la collecte locale et décider ce qui arrive à distance. Fermer simplement le canal après un mégaoctet peut laisser le processus en cours. Un enveloppeur distant peut limiter la sortie, l’envoyer dans un fichier contrôlé et signaler un condensat, mais cet enveloppeur a aussi besoin de quotas de disque et d’un nettoyage. Testez une commande qui ne ferme jamais stdout, un enfant qui survit à son parent et un processus qui écrit sans fin sur stderr.
Les journaux doivent aussi montrer ce qui ne s’est pas produit. Une discordance de clé d’hôte, un coffre verrouillé, une approbation rejetée, une session expirée, une requête malformée ou une commande interdite doit créer un enregistrement de refus avant le retour. Sinon, les opérateurs voient un vide et ne peuvent pas distinguer un système silencieux d’un contournement.
Sallyport enregistre les exécutions d’agents et les appels individuels dans un journal d’audit unique, chiffré et chaîné par hachage, et sp audit verify vérifie la chaîne hors ligne sur le texte chiffré sans clé. Le canal dispose ainsi de preuves locales infalsifiables, mais les identifiants d’opération distants et les résultats de réconciliation doivent toujours figurer dans la requête et le résultat pour qu’un opérateur puisse relier l’appel à l’état de la machine.
Injectez des défaillances tout en recueillant les preuves qu’obtiendrait une personne chargée d’un incident. Arrêtez le client avant l’envoi, juste après, au milieu de stdout et après la fin du processus distant mais avant la finalisation locale. Faites tourner la clé d’hôte sans mettre à jour l’inventaire. Remplissez le système de fichiers distant avant l’écriture d’un marqueur. Renvoyez une sortie réussie avec une sortie structurée malformée. Pour chaque cas, posez une question : un relecteur peut-il savoir quelle autorité a été utilisée, ce qui a pu changer et ce qui doit se passer ensuite ?
N’autorisez SSH qu’après validation des portes
Le second canal est prêt lorsque l’équipe peut démontrer son comportement en cas d’échec, et non lorsqu’une commande sur le chemin nominal atteint un hôte de test. Utilisez une porte écrite avec des responsables et des preuves conservées.
Le dossier d’admission doit contenir :
- Un contrat de canal qui nomme le shell, le compte, le répertoire, l’environnement, les délais d’attente, les limites de sortie et le schéma de résultat.
- Un inventaire d’hôtes vérifié, avec une source indépendante pour les empreintes, la couverture des hôtes rebonds et un processus de rotation testé.
- Un catalogue de commandes qui classe le comportement de répétition et nomme la requête de réconciliation de chaque mutation.
- Une spécification d’approbation liée à l’hôte, au compte, à la version de commande, aux arguments, au privilège, au délai d’attente et à l’identifiant d’opération exacts.
- Des résultats d’injection de défaillances prouvant les états inconnus, les refus, les troncatures, les révocations et la reconstruction d’audit.
Faites d’abord passer les sondes en lecture seule. Autorisez ensuite une écriture convergente dans un environnement jetable. Déconnectez-la à chaque frontière et réconciliez le résultat. Répétez l’exercice sur un hôte semblable à la production avec une ressource inoffensive. Examinez les preuves avec la personne responsable de cet hôte, pas seulement avec l’équipe qui a construit la passerelle de l’agent.
Distinguez le retour arrière de la nouvelle tentative. Un retour arrière est une nouvelle mutation explicite, avec sa propre approbation, son identifiant d’opération, ses préconditions et son possible état inconnu. Exécuter automatiquement une commande inverse après un délai d’attente peut endommager une modification qui s’est en fait terminée correctement. Le système doit établir l’état actuel avant de le modifier de nouveau.
Définissez des critères de retrait en même temps que les critères d’admission. Désactivez une opération si le condensat de son script change sans revue, si son hôte quitte l’inventaire, si la réconciliation cesse de fonctionner, si la sortie devient non bornée ou si les opérateurs ne peuvent pas expliquer un résultat inconnu. L’accès au canal n’est pas un badge permanent.
SSH mérite son admission opération par opération. Si vous ne pouvez pas épingler le serveur, décrire l’effet, répéter l’action en sécurité, distinguer tous les états de résultat et reconstruire l’appel ensuite, le résultat correct de la liste de contrôle est « pas prêt ». Gardez le canal HTTP en lecture seule et corrigez la frontière manquante avant qu’un shell distant transforme une nouvelle tentative ambiguë en deuxième modification de production.
FAQ
Quand un agent d’IA est-il prêt à obtenir un accès SSH ?
Un agent est prêt lorsque l’identité de l’hôte est épinglée, que les commandes renvoient des résultats typés, que les mutations peuvent être répétées sans danger, que les approbations sont liées à l’effet exact et que les déconnexions produisent un état inconnu explicite. Une connexion de test réussie prouve seulement la connectivité.
Un accès SSH en lecture seule est-il suffisamment sûr pour commencer ?
C’est la bonne première étape, mais le compte distant doit toujours disposer d’autorisations limitées et d’une sortie bornée. Une commande qui semble en lecture seule peut exécuter des fichiers de démarrage, appeler un binaire inattendu via PATH ou exposer des secrets dans sa sortie.
Faut-il utiliser StrictHostKeyChecking no pour automatiser SSH ?
Non. Préparez les clés d’hôte vérifiées avant l’exécution et utilisez StrictHostKeyChecking=yes. Désactiver cette vérification remplace une invite opérationnelle par une défaillance d’identité que l’automatisation risque de ne pas détecter.
ssh-keyscan peut-il créer known_hosts en toute sécurité ?
ssh-keyscan peut collecter une clé, mais ne peut pas authentifier la clé reçue. Comparez son empreinte à une valeur obtenue via un canal de confiance indépendant avant de l’installer.
Un code de sortie SSH égal à 0 prouve-t-il que la modification a réussi ?
Cela prouve seulement que le statut de la commande distante rapporté était zéro. La commande peut mal définir la réussite, un pipeline peut masquer un échec antérieur ou l’état externe attendu peut rester incorrect. Vérifiez donc une sortie structurée ou réconciliez l’état.
Que signifie le code de sortie SSH 255 ?
Le client OpenSSH utilise 255 lorsqu’il rencontre une erreur, et renvoie sinon le statut de la commande distante. Comme un programme distant peut aussi choisir 255, conservez séparément l’état du transport et le statut de sortie distant dans votre adaptateur.
Quand une commande SSH est-elle idempotente ?
Une commande est idempotente lorsque la répéter après une exécution partielle produit le même état voulu sans dupliquer l’effet. Testez la frontière de l’effet, pas la formulation de la commande, et utilisez des identifiants d’opération stables ou des garde-fous basés sur l’état.
Un agent doit-il réessayer automatiquement après un délai d’attente SSH ?
Seulement si le système peut prouver que la commande n’a jamais démarré. Si l’envoi a pu avoir lieu, marquez le résultat comme inconnu et lancez une requête de réconciliation en lecture seule avant d’envisager une autre mutation.
Que doit afficher une carte d’approbation SSH ?
Affichez l’hôte canonique, l’empreinte vérifiée, le compte distant, l’effet attendu, la version exacte de la commande ou du script, les arguments normalisés, le changement de privilèges, le délai d’attente et l’identifiant d’opération. Liez l’approbation à cette charge utile pour que rien ne puisse changer après le clic.
Comment les équipes doivent-elles tester l’accès SSH des agents ?
Injectez des défaillances avant, pendant et après l’exécution distante, notamment des changements de clé d’hôte, des dépassements de sortie, des signaux, des délais d’attente et des déconnexions. Les preuves doivent indiquer à un opérateur ce qui a pu changer et quelle action de réconciliation est sûre.