# Accès sudo pour les agents d’IA : contrôlez chaque élévation

Donner sudo à un agent d’IA parce qu’il sait déjà ouvrir un shell distant est une erreur de catégorie. L’exécution distante répond à la question « peut-il atteindre cette machine ? » L’élévation répond à une autre : « cette demande peut-elle modifier un état protégé ? » Ces décisions nécessitent des éléments de preuve, des contrôles et des enregistrements différents.

J’ai vu des équipes les réduire à une seule fonctionnalité pratique : un agent se connecte en SSH, exécute `sudo` et laisse derrière lui un transcript de conversation. Tout semble bien organisé jusqu’à ce qu’une mauvaise commande redémarre le mauvais service, remplace un fichier de configuration ou suive une instruction cachée dans un dépôt. Ensuite, personne ne peut dire quel processus détenait l’autorité, qui l’a approuvé ni pourquoi l’accès root semblait nécessaire.

**L’accès sudo pour les agents d’IA** devrait désigner une exception limitée et attribuable, associée à une opération précise. Il ne devrait jamais signifier que l’agent a acquis les pouvoirs permanents d’un administrateur pour le reste de sa tâche.

## Un shell distant et sudo répondent à des questions différentes

Une session SSH prouve qu’un client s’est authentifié auprès d’un compte distant. Elle n’établit pas que chaque commande saisie ou générée dans cette session mérite l’autorité root. Gardez le compte ordinaire suffisamment utile pour la découverte, les builds, les tests, la lecture des journaux et la préparation des déploiements. Placez le petit ensemble de modifications protégées derrière un chemin d’élévation distinct.

Cette distinction devient floue parce que le shell fait passer l’élévation de privilèges pour un détail de syntaxe :

```sh
ssh deploy@api-02 'sudo systemctl restart payment-worker'
```

Cette seule ligne dissimule au moins cinq décisions : quel processus d’agent l’a émise, quel hôte il a ciblé, si le nom du service est correct, pourquoi un redémarrage est nécessaire et si une personne a accepté les conséquences. SSH peut authentifier la connexion. `sudo` peut changer l’utilisateur effectif. Aucun de ces outils ne capture à lui seul la raison ni ne fournit un point de décision adapté au travail autonome.

Les commandes distantes ordinaires doivent rester ordinaires. Un agent peut exécuter `systemctl status`, consulter un journal de service qu’il a déjà le droit de lire, comparer une configuration générée ou lancer un contrôle de santé sans demander root. Cette séparation apporte un avantage concret : l’agent peut recueillir des éléments avant de demander à quelqu’un d’approuver une modification.

Ne réagissez pas en plaçant chaque commande shell derrière une validation humaine. Cela crée une fatigue de l’approbation et les utilisateurs finissent par accepter des demandes qu’ils ne lisent plus. Placez la friction lorsqu’une action franchit une limite protégée : gestion de services, modification de paquets système, écriture de fichiers privilégiés, changements de comptes, règles réseau, secrets, paramètres de démarrage et opérations sur les données de production.

La forme d’une commande ne détermine pas son risque. `sudo cat /var/log/...` peut exposer des informations sensibles. `sudo systemctl restart ...` peut interrompre un service utilisé par les clients. Un `sudo install` apparemment inoffensif peut remplacer un exécutable. Classez l’effet, la cible et la possibilité que les arguments permettent de s’échapper vers un comportement root arbitraire.

## Les règles sudoers sont des listes d’autorisation, pas un argument de sécurité

Le manuel `sudoers` indique que sudo détermine si un utilisateur peut exécuter une commande en fonction du chemin de la commande et, lorsque cela est configuré, de ses arguments. C’est un mécanisme utile, mais il ne transforme pas une liste d’autorisation large en délégation sûre.

Une règle comme celle-ci délègue bien plus que beaucoup ne l’imaginent :

```sudoers
autobot ALL=(root) NOPASSWD: /usr/bin/systemctl *
```

Elle permet au compte de démarrer, arrêter, redémarrer, activer, désactiver, masquer et inspecter n’importe quelle unité prise en charge par le `systemctl` local. Un agent capable d’influencer un fichier d’unité, un fichier d’environnement ou l’exécutable d’un service peut transformer un redémarrage autorisé en exécution de code avec les droits root. La règle ne prévoit pas non plus de raison de modification ni d’expiration.

Les restrictions d’arguments ne sont utiles que lorsque le programme autorisé possède une interface réduite et stable, et qu’il ne peut pas interpréter une entrée contrôlée par un attaquant comme un chemin, une expression shell, un plug-in, un éditeur, un pager ou une source de configuration. Les administrateurs l’oublient souvent parce que la commande leur est familière. Une commande ne devient pas limitée simplement parce que le chemin de son binaire est absolu.

Préférez un wrapper conçu pour un objectif précis, avec une validation explicite. Par exemple, un wrapper de redémarrage peut accepter un nom de service prédéfini plutôt que des arguments `systemctl` arbitraires :

```sh
#!/bin/sh
set -eu

case "${1:-}" in
  payment-worker|report-worker) ;;
  *) echo "unsupported service" >&2; exit 64 ;;
esac

exec /usr/bin/systemctl restart "$1"
```

Limitez ensuite sudo à ce wrapper et à des arguments exacts lorsque la plateforme le permet :

```sudoers
autobot ALL=(root) /usr/local/sbin/restart-approved-service payment-worker, \
                    /usr/local/sbin/restart-approved-service report-worker
```

Cela empêche la multiplication des sous-commandes `systemctl`. Cela ne dit pas si le redémarrage est pertinent à cet instant. Le wrapper doit également appartenir à root, se trouver dans des répertoires parents non modifiables et être protégé contre les variables d’environnement contrôlées par l’agent. Si un agent peut modifier le wrapper ou remplacer quelque chose qu’il exécute, la règle a échoué.

Évitez la recommandation populaire « utilisez simplement NOPASSWD pour l’automatisation ». Elle plaît parce que les tâches sans surveillance cessent d’échouer sur les demandes de mot de passe. Pour un agent autonome, la demande de mot de passe n’a jamais été un contrôle significatif. La remplacer par une élévation sans restriction ne fait que supprimer la dernière pause visible. Remplacez-la par une autorisation précise et une trace attribuable, pas par une commodité vide.

## Une demande d’élévation doit donner une raison qu’un réviseur peut évaluer

La raison fait partie de la décision d’autorisation. Ce n’est pas une phrase décorative à recopier dans un ticket après coup. L’agent devrait créer la demande avant l’élévation, et l’interface d’approbation devrait afficher l’opération proposée avec cette raison.

Demandez que la requête réunisse les champs suivants :

- La cible exacte, par exemple `api-02` et `payment-worker`.
- L’opération privilégiée demandée, avec ses arguments fixes.
- La raison, liée à un incident, un déploiement, une tâche de maintenance ou une condition observée.
- L’effet attendu et une action de retour arrière.
- Une expiration suffisamment courte pour qu’une demande abandonnée ne devienne pas une autorité permanente.

La raison doit contenir des éléments qu’un humain peut évaluer. « Besoin de sudo pour corriger le build » signifie que l’agent n’a pas encore effectué assez de diagnostic. « Remplacer `/etc/acme/client.conf` par la configuration de version examinée après que le renouvellement du certificat actuel a signalé une erreur d’analyse ; restaurer la version précédente si la validation échoue » identifie un fichier, une condition et un moyen de récupération.

Gardez une demande structurée, même si vous conservez aussi une explication en texte libre. Les champs structurés empêchent l’agent de déplacer discrètement la cible entre la planification et l’exécution. Ils rendent également l’examen ultérieur possible sans demander à quelqu’un de reconstituer une décision à partir de plusieurs fenêtres de conversation.

Utilisez un identifiant de demande immuable. L’approbation doit autoriser cet identifiant, la cible indiquée et l’opération indiquée. N’approuvez pas une instruction en langage naturel comme « gérer la panne » en laissant l’agent décider plus tard quelles commandes root elle couvre. Cela transforme l’approbation humaine en chèque en blanc illimité.

Une demande utile peut ressembler à ceci :

```json
{
  "request_id": "elev-7f4c2",
  "agent_session": "run-91b0",
  "host": "api-02",
  "operation": "/usr/local/sbin/restart-approved-service payment-worker",
  "reason": "Deployment 482 left payment-worker unhealthy; status shows repeated configuration parse errors.",
  "expected_effect": "Service restarts with the reviewed configuration.",
  "rollback": "Restore the previous configuration revision and restart the service.",
  "expires_at": "2025-03-08T14:25:00Z"
}
```

L’enregistrement d’exécution doit reprendre `request_id`. Sans ce lien, un approbateur peut avoir accepté une opération alors que l’agent en a effectué une autre.

## Le processus de l’agent a besoin de sa propre identité

Un compte `deploy` partagé fait paraître tous les acteurs identiques une fois les dégâts causés. Donnez à l’exécution de l’agent une identité qui enregistre quel exécutable l’a démarrée, quel humain ou service l’a lancée, quel dépôt et quelle tâche lui ont été transmis, et quand son autorité prend fin.

L’identité humaine et l’identité du processus sont deux faits distincts. Un développeur peut lancer un agent, mais le processus de l’agent émet la commande plusieurs heures plus tard, après avoir lu des fichiers, des sorties d’outils et des réponses réseau. La piste d’audit doit conserver ces deux faits. « Jamie a lancé l’exécution 91b0 » et « le processus d’agent signé 91b0 a demandé une élévation » permettent à un enquêteur de distinguer le parrainage de l’exécution.

La signature du code peut aider à identifier l’exécutable local qui a demandé l’accès. Elle ne prouve pas que l’instruction du modèle était sûre et ne rend pas fiable un dépôt compromis. Traitez l’identité du processus comme une limite de l’autorité de demande, pas comme la preuve que la demande est judicieuse.

Ne donnez pas à l’agent un mot de passe root réutilisable, un identifiant SSH privé qui arrive directement sur un compte administrateur ou un horodatage sudo de longue durée qu’il peut renouveler indéfiniment. Chacun transforme une demande limitée en capacité portable. Dès que l’agent peut copier cette capacité dans un espace de travail, un journal, un artefact de build ou un processus enfant, vous avez perdu le contrôle de sa propagation.

Les identifiants de courte durée réduisent l’exposition, mais ne rendent pas une mauvaise commande acceptable. Utilisez l’expiration avec une opération définie, un processus lié et une raison. Si l’un de ces éléments manque, vous avez un jeton d’accès portant simplement une étiquette plus élégante.

## Un shell root laisse à l’agent trop de latitude pour réinterpréter les instructions

N’approuvez jamais `sudo -i`, `sudo su`, `sudo sh` ou un `sudo bash` sans restriction pour une session d’agent. Un shell root autorise toutes les commandes suivantes, y compris celles assemblées à partir de sorties d’outils qui n’existaient pas lorsque la première commande a été approuvée.

Le même avertissement s’applique aux formes de commandes qui dissimulent un interpréteur arbitraire :

```sh
sudo python3 -c "$AGENT_TEXT"
sudo env CONFIG="$AGENT_TEXT" /usr/local/sbin/apply-config
sudo tee /etc/some-file.conf
```

Chaque exemple semble limité jusqu’à ce que l’on examine son canal d’entrée. Python exécute du code arbitraire. `env` peut modifier le comportement d’un programme d’une manière imprévue par l’appelant. `tee` donne à l’écrivain le pouvoir sur un chemin appartenant à root si ce chemin reste sans contrainte. Une politique de commande qui ignore les arguments et l’environnement n’est qu’une politique de nom de fichier.

Une meilleure conception sépare le diagnostic de l’exécution. L’agent peut examiner les faits accessibles sans privilèges, générer une modification proposée et la soumettre à validation. Un helper privilégié limité reçoit ensuite uniquement les entrées approuvées. Il doit les valider à nouveau, car la validation au moment de l’examen et celle au moment de l’exécution protègent contre des défaillances différentes.

Imaginez un agent de déploiement qui constate la panne d’un service. Avec un sudo large, il pourrait modifier une surcharge d’unité, recharger le gestionnaire et redémarrer le service. Une chaîne malveillante dans une configuration contrôlée par le dépôt peut devenir une ligne `ExecStart`, puis être exécutée avec les droits root lors du redémarrage. Dans une conception contrainte, l’agent peut lire l’état et préparer la configuration examinée, mais un helper de déploiement privilégié n’accepte qu’une empreinte de contenu provenant d’un emplacement d’artefacts approuvé. Il rejette les fichiers d’unité, les chemins arbitraires et les surcharges non gérées.

Cela demande plus de travail que de remettre un shell. C’est aussi la différence entre une opération connue et un interpréteur capable d’inventer de nouvelles opérations après l’approbation.

## L’approbation doit suivre le risque de l’opération

Une approbation par session peut convenir à des actions distantes courantes et limitées. Elle ne convient pas à chaque utilisation d’une capacité d’administration. Traitez l’autorisation de session et l’autorisation par appel comme deux contrôles distincts.

L’autorisation de session répond à la question : « Ce processus d’agent identifié peut-il utiliser les canaux distants ordinaires pendant la durée de cette exécution ? » Elle empêche un processus local inconnu d’usurper discrètement l’identité d’un agent connu. Elle doit prendre fin lorsque le processus se termine, et un opérateur doit pouvoir la révoquer immédiatement.

L’autorisation par appel répond à la question : « Cette action précise peut-elle être exécutée maintenant ? » Appliquez-la aux opérations ayant un rayon d’impact élevé, des identifiants rares, un effet sur la production ou des conséquences irréversibles. La carte d’approbation doit commencer par l’identité du processus, puis afficher la cible, l’opération, la raison, l’expiration et l’effet attendu. Cacher l’identité sous un long texte va à l’encontre du but recherché.

Ne demandez pas à l’opérateur de décortiquer cent lignes de shell généré. Donnez à l’agent un vocabulaire d’opérations nommées avec des détails rendus lisibles. « Redémarrer le worker de paiement sur api-02 » peut être examiné. Un bloc shell avec des guillemets imbriqués pousse l’humain à approuver machinalement ce qu’il ne comprend pas.

Le parcours d’approbation doit aussi prévoir un refus qui aide l’agent à récupérer. Renvoyez un refus clair et, si nécessaire, une instruction comme « nécessite un enregistrement de modification du déploiement » ou « cette opération n’est pas disponible en production ». Ne laissez pas l’agent réessayer avec de légères variations de formulation jusqu’à ce qu’une personne fatiguée accepte. Un refus doit fermer la demande, sauf si un humain en crée une nouvelle avec des informations sensiblement différentes.

En cas d’urgence, gardez la même discipline. Un ingénieur d’astreinte peut approuver une demande limitée avec une expiration courte et une référence d’incident. L’urgence peut justifier un examen plus rapide, pas la suppression de la trace ni l’octroi d’un shell root interactif.

## Les traces d’audit doivent survivre à l’agent qui a créé la demande

Un transcript de terminal aide au débogage, mais ne peut pas tenir lieu de trace d’audit. L’agent peut l’omettre, modifier des fichiers locaux ou exécuter des commandes par un chemin que le transcript n’a pas enregistré. Capturez l’autorisation et l’exécution dans un stockage que l’agent ne peut pas réécrire.

Enregistrez la décision séparément de l’action. Une trace d’approbation doit indiquer qui a approuvé, quand, l’identité du processus, le contenu exact de la demande et l’expiration. Une trace d’exécution doit indiquer si le helper a été lancé, quel hôte il a atteint, ce qu’il a renvoyé, quand il s’est terminé et l’identifiant de demande qui l’a autorisé.

Incluez une empreinte du résultat ou un résumé de sortie limité plutôt que de déverser des sorties de commande sensibles dans un journal largement visible. La personne chargée de l’enquête doit pouvoir constater que `payment-worker` a redémarré et est redevenu sain. Elle n’a pas besoin d’un mot de passe de base de données copié parce qu’il est apparu par hasard dans stderr.

La preuve d’altération compte parce que les journaux d’audit deviennent précieux après un incident. Une chaîne de hachage relie chaque enregistrement au précédent. Si quelqu’un modifie ou supprime une entrée, la vérification détecte la rupture. Gardez la vérification indépendante de l’agent et, si possible, des identifiants utilisés pour exécuter les actions. Un journal qui nécessite l’identifiant administrateur compromis pour être vérifié est moins utile au moment où vous en avez le plus besoin.

Par exemple, un vérificateur hors ligne devrait signaler une séquence de ce type :

```text
$ sp audit verify --file agent-audit.enc
verified records: 184
chain start: 8c1a...e72d
chain end: 4bf0...193a
status: valid
```

La propriété importante n’est pas le nom de la commande. C’est le fait que le vérificateur puisse détecter un enregistrement chiffré modifié sans demander à l’agent de s’expliquer. Conservez des copies en dehors de la machine où l’agent travaille, car un attaquant qui contrôle cette machine peut supprimer tout le journal.

Sallyport applique cette séparation en gardant les identifiants hors de portée de l’agent et en produisant les journaux de session et d’action individuelle à partir d’un journal d’audit chiffré, chaîné par hachage et aveugle à l’écriture. Ce modèle est utile lorsque les agents doivent effectuer des actions HTTP ou SSH sans jamais posséder les identifiants qui les autorisent.

## Les helpers privilégiés ont besoin d’interfaces étroites et de tests avec entrées hostiles

Un helper est du code sensible pour la sécurité même s’il ne comporte que vingt lignes. Testez-le comme si chaque argument, variable d’environnement, répertoire courant et fichier référencé provenait d’un attaquant, car un agent d’IA peut être amené à transmettre du contenu hostile sans intention malveillante.

Commencez par dresser l’inventaire des opérations. Notez chaque effet privilégié dont l’agent a réellement besoin, comme redémarrer un service précis ou installer un artefact de version signé. Si vous ne pouvez pas décrire un effet sans dire « exécuter une commande arbitraire », l’opération n’est pas encore conçue.

Pour chaque helper, répondez aux questions suivantes avant de l’ajouter à sudoers :

1. Quelles entrées exactes l’appelant peut-il fournir, et comment le helper valide-t-il chacune d’elles ?
2. Quels chemins du système de fichiers, exécutables, fichiers de configuration et variables d’environnement influencent son comportement ?
3. Une entrée acceptée peut-elle déclencher un shell, un interpréteur, un pager, un éditeur, un chargeur de plug-ins ou une récupération réseau ?
4. Le helper vérifie-t-il la propriété, les permissions et l’identité du contenu avant d’agir ?
5. Quelle trace émet-il lorsque la validation échoue, et lorsqu’une exécution réussit ?

Exécutez des tests négatifs, pas seulement le parcours nominal. Transmettez un parcours `../`, des métacaractères shell, un nom de service vide, une entrée trop longue, des caractères Unicode inattendus et un nom valide qui pointe vers un état dangereux. Essayez de remplacer un fichier référencé entre la validation et son utilisation. Vérifiez si un répertoire de journaux, un répertoire temporaire ou un répertoire parent accessible en écriture permet à l’agent de rediriger une sortie root.

Examinez également les dépendances après les mises à jour. Un helper sûr avec une version donnée d’une commande peut devenir dangereux lorsqu’une nouvelle option accepte un chemin de configuration ou charge une extension. Des interfaces étroites réduisent cette charge de maintenance, mais ne la suppriment pas.

## Déployez l’élévation comme une exception contrôlée

Commencez par supprimer les formes les plus dangereuses de privilège permanent : comptes administrateur partagés, `NOPASSWD` sans restriction, secrets root réutilisables dans la configuration de l’agent et shells root. Vous n’avez pas besoin d’arrêter toute l’automatisation pour effectuer cette modification. Conservez d’abord le diagnostic en lecture seule, puis déplacez une opération protégée à la fois vers un helper explicite et un parcours d’approbation.

Choisissez une opération assez fréquente pour tester le processus, mais dont le retour arrière est limité, par exemple le redémarrage d’un worker nommé après un déploiement examiné. Demandez à l’agent de fournir l’hôte, la raison, l’effet, le retour arrière et l’expiration. Faites rejeter les demandes vagues par l’approbateur. Cette friction apprend au flux de travail de l’agent les éléments qu’il doit recueillir avant de demander des privilèges.

Examinez les demandes refusées avec autant d’attention que les demandes acceptées. Une série de demandes d’écriture arbitraire de fichiers peut indiquer que l’interface de déploiement ne propose pas une opération nécessaire. Elle peut aussi montrer que l’agent tente sans cesse de contourner une limite. Ce sont deux problèmes différents, et une piste d’audit permet de les distinguer.

Entraînez-vous ensuite à la révocation. Interrompez une session pendant que l’agent travaille, refusez une demande déjà émise après son expiration et vérifiez qu’un identifiant de demande copié ne peut pas autoriser une seconde action. Les équipes testent souvent les écrans d’approbation et négligent cette étape. La révocation est le contrôle dont vous avez besoin lorsque l’agent commence à se comporter différemment au milieu d’une exécution.

Ne mesurez pas la maturité au nombre de commandes qu’un agent peut exécuter avec les droits root. Mesurez-la à la capacité de garder chaque élévation limitée, attribuable, temporaire, vérifiable et récupérable lorsque l’agent ou ses entrées se comportent mal.
