# Vérifier les hôtes SSH pour les agents qui interviennent en production

Un agent de programmation autonome ne doit jamais décider seul qu'un nouveau serveur SSH est digne de confiance. Il peut inspecter un dépôt, préparer un déploiement et demander un accès, mais il doit s'arrêter lorsque l'identité distante ne correspond pas à un enregistrement établi par une personne ou par un processus d'approvisionnement fiable.

SSH pose deux questions de sécurité distinctes : « Ai-je atteint le serveur prévu ? » et « Que peut faire ce compte sur ce serveur ? » Les équipes les confondent souvent parce que les deux mécanismes utilisent des clés publiques. Cette erreur transforme une connexion mal dirigée en exposition d'identifiants, puis un compte trop permissif en incident de production.

La conception pratique est simple : épinglez l'identité de l'hôte avant la connexion de l'agent, donnez à chaque tâche d'agent un compte distant aux droits limités et faites examiner par une personne les commandes dont l'effet ne peut pas être déduit sans risque. Chaque couche limite un type d'échec différent. Aucune ne remplace les autres.

## L'identité de l'hôte doit être vérifiée avant l'authentification de l'utilisateur

La vérification de l'hôte indique si le client SSH a atteint le serveur qu'il devait joindre. L'authentification de l'utilisateur indique si ce serveur accepte l'identifiant proposé par le client. L'ordre compte, car SSH négocie et vérifie la clé d'hôte du serveur avant que le client n'envoie les éléments d'authentification de l'utilisateur.

Une clé d'hôte appartient au serveur, pas à l'administrateur et pas à l'agent. L'empreinte est une représentation courte de cette clé publique d'hôte, généralement affichée au format SHA256. Si `deploy.example.internal` présente normalement une empreinte et en présente soudainement une autre, le client sait que quelque chose a changé. Il peut s'agir d'une reconstruction légitime. Cela peut aussi être une erreur DNS, une adresse réutilisée, une erreur de bastion ou une tentative active d'interception.

Prenons le cas d'un agent de programmation auquel on demande d'exécuter une migration sur `db-prod.internal`. Sa configuration SSH traduit ce nom en adresse. Un attaquant capable d'influencer le DNS, le chemin d'un proxy ou une entrée d'inventaire obsolète peut rediriger la connexion vers un serveur qu'il contrôle. Si le client accepte la clé d'hôte inconnue, ce serveur peut demander l'authentification de l'utilisateur. Une clé privée SSH côté client ne quitte peut-être pas le client, mais l'accès de l'agent comprend souvent des mots de passe, des processus de signature de certificats, de la redirection ou des commandes qui révèlent des informations utiles après la connexion. Surtout, l'agent peut maintenant exécuter sa commande prévue sur la mauvaise machine.

Une empreinte d'hôte épinglée fait échouer la connexion avant que l'agent ne puisse considérer la machine comme sa cible. C'est pourquoi un hôte inconnu ou modifié constitue une limite d'autorisation, et non un simple avertissement à désactiver.

La vérification de l'hôte ne confirme pas qu'une machine est saine, correctement configurée ou sûre à modifier. Elle confirme uniquement la continuité de son identité cryptographique. Cette limite est utile. Ne demandez pas à une empreinte d'hôte de décider si `rm -rf`, une migration de schéma ou une modification du pare-feu sont pertinentes.

## La confiance à la première utilisation convient mal au travail sans surveillance

La confiance à la première utilisation peut se tolérer lorsqu'un développeur se connecte manuellement à une machine personnelle jetable. C'est un mauvais choix par défaut pour un processus autonome, car la première connexion est précisément le moment où quelqu'un doit décider si le nom d'hôte, le chemin réseau et l'empreinte correspondent bien.

OpenSSH documente ce choix dans le manuel `ssh_config`, à la section `StrictHostKeyChecking`. Avec `yes`, le client n'ajoute jamais automatiquement les clés d'hôte inconnues et refuse une clé modifiée. Avec `accept-new`, il enregistre automatiquement une clé inconnue, mais refuse toujours une clé modifiée. Avec `no` ou `off`, il accepte davantage de situations qui devraient attirer l'attention d'un opérateur.

`accept-new` est souvent présenté comme un compromis raisonnable. Il réduit effectivement les interruptions lorsque les hôtes sont créés fréquemment. Il donne aussi à un chemin réseau le droit d'établir l'enregistrement d'identité initial. Pour un agent capable de modifier l'infrastructure, c'est la mauvaise partie qui prend la décision.

Utilisez `StrictHostKeyChecking=yes` pour les points d'accès de production et de préproduction gérés par un agent. Lorsqu'une connexion échoue parce que l'hôte est inconnu, transmettez la demande de l'agent à une personne capable de comparer l'empreinte signalée avec une source extérieure à cette connexion SSH. Une console cloud, un enregistrement d'inventaire signé, une console physique ou un canal de gestion existant peuvent servir à cette comparaison.

Ne résolvez pas l'interruption en plaçant `StrictHostKeyChecking=no` dans un fichier de configuration global. Cette ligne survit souvent à l'incident temporaire qui l'a justifiée, puis s'applique discrètement à des hôtes dont personne ne voulait assouplir les règles.

Il existe une exception plus limitée : une infrastructure de test éphémère dont les identités d'hôte proviennent d'un système d'approvisionnement capable de publier une liste d'hôtes authentifiée avant le début du test. L'agent ne doit toujours pas apprendre l'identité d'un hôte lors de son premier contact réseau. La source de confiance a changé, elle n'a pas disparu.

## Une empreinte n'est utile que si sa source est indépendante

Une empreinte copiée depuis le point d'accès que vous essayez de vérifier ne prouve presque rien. `ssh-keyscan` est pratique pour collecter les clés publiques d'hôte, et cette facilité crée un piège courant : un opérateur l'exécute sur un nom d'hôte, colle le résultat dans `known_hosts` et considère l'hôte comme vérifié. Si le DNS ou le routage pointe déjà vers un attaquant, c'est sa clé qui a été épinglée.

Utilisez `ssh-keyscan` comme commande de collecte après avoir obtenu une empreinte indépendante, et non comme source de confiance. Par exemple, un administrateur peut récupérer l'empreinte d'une clé d'hôte depuis une console de fournisseur ou un enregistrement de compilation signé, puis la comparer avec la clé collectée.

```sh
ssh-keyscan -t ed25519 app-prod.internal > /tmp/app-prod.hostkey
ssh-keygen -lf /tmp/app-prod.hostkey -E sha256
```

La deuxième commande affiche une sortie de cette forme :

```text
256 SHA256:exampleFingerprintMaterial app-prod.internal (ED25519)
```

Comparez caractère par caractère la valeur `SHA256:` avec la valeur obtenue indépendamment. Vérifiez aussi l'algorithme. Si l'enregistrement indique ED25519 et que le résultat collecté indique RSA, arrêtez-vous et examinez la situation au lieu de considérer les deux résultats comme interchangeables.

Épinglez ensuite la clé publique, et pas seulement une note contenant son empreinte. Un fichier dédié sépare les cibles de l'agent de l'accumulation personnelle d'anciennes machines d'un développeur :

```text
app-prod.internal ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExamplePublicHostKeyMaterial
[192.0.2.44]:22 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExamplePublicHostKeyMaterial
```

Incluez le nom d'hôte canonique et toute forme d'adresse que l'agent est autorisé à utiliser. Sinon, un flux qui utilise une adresse un jour et un nom le lendemain peut retomber sur une nouvelle décision de confiance. Gardez le fichier dans une gestion de configuration contrôlée, avec une revue du changement qui identifie l'ancienne empreinte et celle qui la remplace.

Les enregistrements DNS SSHFP peuvent être utiles lorsque la validation DNSSEC est correctement déployée de bout en bout. Ils ne corrigent pas une configuration d'agent qui accepte des réponses DNS ordinaires et non signées comme preuve. Considérez SSHFP comme un canal de publication vérifié supplémentaire, et non comme un enregistrement décoratif qui rend sûre une première utilisation aveugle.

## Épinglez le chemin et l'enregistrement de l'hôte dans la configuration SSH

Un agent a besoin d'une configuration SSH qui supprime les ambiguïtés, et non d'une configuration qui hérite des habitudes d'un poste de travail. Épinglez explicitement le nom de destination, le fichier de clés d'hôte attendu, le compte et le comportement de la connexion.

```sshconfig
Host app-production
    HostName app-prod.internal
    User agent_release
    Port 22
    UserKnownHostsFile ~/.ssh/agent_known_hosts
    GlobalKnownHostsFile /dev/null
    StrictHostKeyChecking yes
    UpdateHostKeys no
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    ForwardAgent no
    PermitLocalCommand no
```

Cet extrait évite plusieurs erreurs courantes. `UserKnownHostsFile` empêche une dépendance imprévue aux entrées d'hôtes accumulées par un utilisateur. `GlobalKnownHostsFile /dev/null` empêche un fichier géré à l'échelle de la machine d'élargir silencieusement la confiance. `UpdateHostKeys no` empêche les mises à jour automatiques des clés d'hôte de modifier l'ensemble épinglé pendant le travail de l'agent. `ForwardAgent no` empêche la machine distante d'utiliser un agent d'authentification transféré pour atteindre un autre système.

Le manuel OpenSSH `ssh_config` explique que `UpdateHostKeys` permet la rotation des clés d'hôte lorsqu'un hôte prouve qu'il possède une clé de confiance. Ce comportement peut être utile pour des parcs interactifs gérés avec rigueur. Pour un agent autonome, une modification automatique complique l'examen des incidents. Une personne doit approuver un changement d'identité en production et mettre délibérément à jour le fichier d'hôtes contrôlé.

Utilisez un alias stable comme `app-production` dans la demande de l'agent et réservez les adresses brutes aux procédures d'urgence. L'alias rend la cible approuvée visible dans les journaux et évite que les commandes alternent entre noms, adresses temporaires et extraits de shell copiés.

Testez la configuration effective avant d'accorder à l'agent un chemin vers les identifiants :

```sh
ssh -G app-production | grep -E '^(hostname|user|stricthostkeychecking|userknownhostsfile|forwardagent|updatehostkeys) '
```

Attendez-vous à des valeurs de ce type :

```text
hostname app-prod.internal
user agent_release
stricthostkeychecking yes
userknownhostsfile /Users/operator/.ssh/agent_known_hosts
forwardagent no
updatehostkeys no
```

Cette vérification repère les erreurs de priorité dues aux fichiers `Include`, aux valeurs par défaut de l'utilisateur et à la gestion de configuration. J'ai vu des entrées d'hôtes soigneusement définies être neutralisées par une section générique ultérieure qui modifiait l'utilisateur, activait la redirection ou choisissait un autre fichier d'hôtes connus. La configuration réellement exécutée est celle que vous devez inspecter.

## La limitation du compte réduit les dégâts après une connexion correcte

Un hôte vérifié peut tout de même recevoir une mauvaise commande. Donnez donc à l'agent un compte associé à une tâche précise et limitée. Ne donnez pas à un agent de programmation autonome le même accès SSH que celui utilisé par un opérateur pour toutes les urgences.

La portée d'un compte comporte quatre aspects : l'hôte auquel il peut accéder, les fichiers et services qu'il peut modifier, les chemins d'élévation de privilèges dont il dispose et la durée pendant laquelle il reste utilisable. Un compte séparé rend ces réponses vérifiables. Un compte partagé `deploy` transforme chaque exécution automatisée en problème d'attribution et finit généralement par obtenir des droits étendus, car chaque nouveau flux nécessite une exception supplémentaire.

Un agent de publication sur un serveur applicatif peut avoir besoin de lire un répertoire de versions, d'écrire un nouvel artefact, d'appeler un seul programme de déploiement et de redémarrer un seul service. Il n'a pas besoin d'un shell avec un `sudo` illimité, d'un accès en lecture à tous les répertoires personnels ou de la possibilité de modifier la configuration SSH.

Lorsque la tâche est étroite, limitez la clé publique SSH autorisée avec une commande forcée. Dans `authorized_keys`, une entrée côté serveur peut associer cet identifiant à un programme intermédiaire :

```text
command="/usr/local/sbin/agent-release-wrapper",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleAgentPublicKey agent-release
```

Le programme intermédiaire doit analyser un petit ensemble d'arguments, rejeter les métacaractères du shell, journaliser l'opération demandée et appeler des binaires fixes avec des chemins fixes. N'écrivez pas un programme qui accepte une chaîne arbitraire dans `SSH_ORIGINAL_COMMAND` et la transmet à `sh -c`. Cela ne fait que cacher un accès shell distant sans restriction derrière le nom d'une fonction.

Les commandes forcées ne conviennent pas à toutes les tâches de maintenance. Lorsqu'un agent a réellement besoin d'un shell pour enquêter, utilisez un compte d'investigation distinct, avec des droits de lecture et sans élévation de privilèges. Prévoyez un chemin séparé et explicitement vérifié pour les modifications. Mélanger diagnostic et mutation dans un seul compte étendu laisse à l'agent trop de latitude pour transformer une déduction imparfaite en action irréversible.

Les certificats SSH de courte durée peuvent réduire le travail de nettoyage si vous exploitez déjà une autorité de certification avec des contrôles d'émission clairs. Ils ne suppriment pas le besoin de vérifier l'hôte. Un certificat renseigne sur le compte client, tandis que l'empreinte d'hôte indique quel serveur l'a reçue.

## L'examen d'une commande doit montrer son effet, pas une intention vague

L'approbation d'une commande n'est utile que si la personne dispose de suffisamment de contexte pour en juger l'effet. « Déployer la version » est une intention. `sudo systemctl restart payments-api` sur `app-production` avec le compte `agent_release` est une action qu'un réviseur peut évaluer.

Examinez l'alias de destination exact, le compte distant, la commande littérale, les arguments et le répertoire de travail. Vérifiez aussi si la commande appelle un shell, développe des variables, lit un script distant, télécharge du contenu ou utilise `sudo`. Ces détails déterminent si une demande apparemment inoffensive peut aller bien au-delà de sa description.

Un enregistrement d'approbation raisonnable pour une action distante ressemble à ceci :

```text
Target: app-production (app-prod.internal)
Account: agent_release
Command: /usr/local/sbin/agent-release-wrapper activate 2025.04.17-rc2
Reason: activate the approved release after smoke tests
```

Comparez-le à cette demande :

```text
ssh app-production "curl $URL | sudo sh"
```

La deuxième commande regroupe la récupération de contenu distant, l'exécution d'un shell, l'élévation de privilèges et une valeur qui peut être développée autrement que prévu par le réviseur. Aucun réglage de vérification d'hôte ne la rend sûre. Refusez-la et exigez un artefact dont le condensat a été vérifié auparavant, un programme de déploiement fixe et des arguments qui désignent une version approuvée.

L'examen des commandes a aussi son propre mode d'échec : la lassitude face aux approbations. Si un agent demande une confirmation pour chaque `cat`, `git status` et vérification d'état de service sans danger, les utilisateurs finissent par approuver sans lire. Placez l'investigation en lecture seule derrière un compte limité ou un programme intermédiaire aux commandes strictement définies, puis réservez les approbations interactives aux actions qui écrivent, redémarrent, font tourner des secrets, modifient les permissions ou franchissent une limite de confiance.

Sallyport peut conserver les identifiants SSH dans son coffre chiffré et exiger une approbation pour chaque utilisation d'un identifiant sélectionné, tandis que son canal SSH s'exécute via `sp-ssh` sans exposer l'identifiant à l'agent. Il vous revient toutefois de toujours épingler l'hôte et de décider si la commande visible mérite une approbation.

## Le chemin d'échec dangereux est généralement une chaîne de raccourcis ordinaires

La plupart des incidents SSH liés à l'automatisation ne commencent pas par une rupture cryptographique sophistiquée. Ils commencent par un raccourci qui semblait inoffensif lors de la configuration.

Imaginez un agent de publication configuré avec `StrictHostKeyChecking=accept-new`, un compte de déploiement partagé et une demande d'approbation qui dit seulement « lancer le déploiement ». Un enregistrement DNS pointe brièvement vers une machine de remplacement avant la fin de la mise à jour de l'inventaire. L'agent voit un hôte inconnu, enregistre sa clé, se connecte à la machine de remplacement parce qu'il accepte le compte partagé et exécute le programme de déploiement. Ce programme dispose de droits d'écriture étendus, car le compte partagé sert aussi aux réparations d'urgence.

Rien dans cette séquence ne nécessite un attaquant. Une simple erreur de nommage peut déployer des artefacts dans le mauvais environnement, exposer la sortie du déploiement à une machine imprévue ou modifier un hôte qui n'est pas prêt. Ajoutez un attaquant capable d'influencer la résolution de noms ou le routage, et les mêmes raccourcis offrent une ouverture bien plus grave.

Chaque contrôle interrompt un point différent de cette chaîne :

1. Un enregistrement d'hôte préalablement épinglé rejette la machine de remplacement jusqu'à ce qu'un opérateur la vérifie.
2. Un compte dédié limite les dégâts si la cible approuvée reste incorrecte d'une manière que l'identité de l'hôte ne peut pas détecter.
3. Un enregistrement d'approbation contenant la commande complète donne au réviseur la possibilité de repérer une cible inattendue ou une action élevée.
4. Une trace de session et d'appel permet à l'équipe de reconstituer la demande, l'approbation, la cible et le résultat après un échec.

Ne remplacez pas le premier contrôle par les suivants. Un réviseur peut manquer une incohérence de cible. Une restriction de compte peut contenir un privilège oublié. Un journal explique les dégâts après coup. L'épinglage de l'identité de l'hôte empêche une catégorie d'erreurs de connexion avant même que le compte distant n'entre en jeu.

## La rotation des clés d'hôte exige une procédure de changement, pas un bouton d'exception

Les clés d'hôte changent pour des raisons légitimes : une machine est reconstruite, une image est remplacée, un algorithme est abandonné ou un opérateur fait tourner les identifiants après une exposition présumée. Traitez ces événements comme des changements d'identité planifiés et documentés, pas comme des boîtes de dialogue d'avertissement à ignorer.

La personne responsable de l'hôte doit obtenir la nouvelle clé publique d'hôte depuis la console de la nouvelle machine ou un autre chemin de gestion authentifié. Elle doit publier la nouvelle entrée dans le fichier d'hôtes connus contrôlé, noter la raison du changement et ne retirer l'ancienne entrée qu'une fois la bascule réussie. Si la machine doit rester en service pendant la transition, OpenSSH peut conserver plusieurs clés d'hôte acceptées pour un même nom. Les clients peuvent ainsi accepter les anciennes et nouvelles identités pendant une période définie.

Gardez l'enregistrement de rotation court, mais précis : nom d'hôte, adresse si nécessaire, ancienne empreinte, nouvelle empreinte, raison, personne ayant vérifié le changement et date d'expiration de toute période à deux clés. Un ticket indiquant seulement « SSH a changé » ne permet pas de distinguer un travail planifié d'une tentative d'apprendre aux clients à faire confiance à un imposteur.

Ne demandez jamais à l'agent d'exécuter `ssh-keygen -R hostname` puis de se reconnecter automatiquement. La suppression de l'ancien enregistrement efface le signal de divergence avant que quiconque n'ait établi sa cause. Un opérateur peut utiliser cette commande après vérification, dans le cadre d'une mise à jour examinée, mais elle ne doit pas faire partie de la logique de récupération d'un flux d'agent.

## Les enregistrements d'audit doivent permettre de reconstituer la décision

Après l'échec ou la surprise d'une action SSH, l'enregistrement utile répond à quatre questions : quel processus d'agent l'a demandée, quelle identité l'a approuvée, quelle connexion et quelle commande exactes ont été utilisées, et quel résultat a été renvoyé. « L'agent a déployé le service » ne répond à aucune de ces questions.

Conservez les enregistrements de session séparément de ceux des appels individuels. Une session identifie l'exécution de l'agent et permet à un opérateur de révoquer son autorisation lorsqu'il commence à se comporter de façon anormale. Un enregistrement d'appel individuel contient l'alias de l'hôte, le compte, l'heure, le résultat de l'approbation, la commande et la sortie ou l'erreur. Cette distinction compte lorsqu'une exécution d'agent réalise cent lectures sans danger, puis une écriture risquée.

Protégez les journaux contre l'agent qui a généré la demande. Si un agent peut modifier l'enregistrement après une commande distante, la piste d'audit est un journal intime, pas une preuve. Un stockage en ajout seulement avec vérification de l'intégrité constitue un minimum raisonnable. Évitez aussi de placer des secrets dans les commandes ou les arguments, car un bon journal d'audit conservera exactement ce que vous lui demandez de conserver.

Sallyport sépare les journaux de session et d'activité dans un journal d'audit chiffré, chaîné par hachage et protégé contre l'écriture ; `sp audit verify` peut vérifier la chaîne hors ligne sans clé du coffre. Utilisez ce type de preuve pour examiner une exception, mais concevez l'épinglage des hôtes, les restrictions de comptes et le texte des approbations avant qu'un incident ne vous donne une raison de le consulter.

Le premier changement opérationnel à effectuer est simple : trouvez chaque configuration SSH d'agent qui accepte un hôte inconnu, remplacez ce comportement par un fichier d'hôtes dédié et épinglé, puis testez les paramètres effectifs avec `ssh -G`. Vous découvrirez des alias obsolètes, des règles génériques accidentelles et des comptes qui disposent de bien plus d'autorité que leur rôle ne l'exige. Ce sont ces connexions qu'il faut corriger avant que l'agent ne gagne en rapidité.
