Un jour, mon agent IA a voulu supprimer les secrets de mon infra.
Il a écrit la commande Terraform. Il l'a lancée. Rien ne s'est passé.
Pas parce qu'il s'est ravisé. Parce que mon Terraform ne s'applique que par le pipeline, jamais en local. Et parce qu'il n'a pas les accès pour le faire.
C'est le meilleur moment de tout mon setup. Un agent a fait une bêtise dangereuse, et ça n'a rien coûté. Voilà toute l'idée. Ne parie pas sur un agent sage. Construis un agent qui ne peut pas casser.
TL;DR : un agent IA, c'est un nouveau dev. Rapide, sans fatigue, sans jugement. En local, je lui donne tout, pour aller vite. En prod, presque rien : il lit les logs, et il répare par pull request. Les garde-fous ne sont pas dans le modèle. Ils sont dans les hooks, les pre-commit, les règles GitHub et le RBAC. Tu ne fais pas confiance à l'agent. Tu rends la casse impossible.
Cet article est pour les devs et les DevSecOps qui laissent un agent de code agir dans leur repo et leur infra. Claude Code, Cursor, peu importe l'outil.
Le contexte
Un agent de code, c'est un LLM qui lit ton repo et lance des commandes à ta place.
Je fais tourner ces agents tous les jours, sur mon propre SaaS. La stack est classique. Go pour le backend. Un cluster k3s pour l'infra. Un k3s est un Kubernetes léger, qui orchestre des conteneurs. Terraform pour tout provisionner. Terraform, c'est de l'IaC : tu décris ton infra dans du code. Grafana et Jaeger pour voir ce qui se passe.
Je viens de la sécurité, et je suis un peu parano. Donner les clés de la prod à un agent, sans filet, c'était non.
Un agent IA, c'est un nouveau dev
Repense à l'arrivée d'un dev junior. Tu ne lui donnes pas les accès prod et les clés SSH le premier jour. Tu lui donnes un laptop, une base de dev jetable, et une règle : tout passe par une revue de code.
Un agent IA, c'est le même onboarding. Sauf qu'il tape cent commandes par minute, sans fatigue et sans peur. C'est le moindre privilège : donner juste les droits nécessaires, rien de plus. Un agent trop permissif, c'est le vrai risque.
Ce n'est pas théorique. En 2025, l'agent IA de Replit a supprimé une base de production entière, en plein gel de code. Il a ensuite inventé de faux enregistrements et menti sur ce qu'il avait fait. Le PDG a reconnu une erreur qui « ne devrait jamais être possible ». Sa correction ? Séparer dev et prod, et brider l'agent. Exactement le sujet de cet article.
Le sujet est si sérieux que les standards bougent. Le Zero Trust est une approche de sécurité : ne fais confiance à personne par défaut, vérifie à chaque accès. L'OWASP a publié un Top 10 des risques pour les applications à base d'agents. Le NIST, dans son texte sur le Zero Trust, parle déjà de « sujets », pas seulement d'utilisateurs. Un agent est un sujet comme un autre. Et la Cloud Security Alliance a sorti un cadre Zero Trust pour les agents. Il est co-écrit avec l'inventeur du Zero Trust.
La bonne question n'est pas « combien de droits ». C'est « combien d'autonomie ». Moins de droits, oui. Mais surtout moins de pouvoir d'agir sans contrôle.
En local, donne-lui les pleins pouvoirs
En local, mon agent a tout. Tous les secrets de dev, toutes les commandes, tout l'accès. Je veux qu'il aille vite.
Dans mon cluster, il navigue librement dans l'espace de dev. Un namespace, c'est un espace isolé dans le cluster. Il déploie, casse des pods, recommence.
Le niveau d'accès vient du contexte kubeconfig. Un kubeconfig, c'est le fichier qui dit à kubectl à quel cluster parler, et avec quels droits. J'ai un contexte par environnement.
# En local, l'agent travaille sur le contexte dev : lecture-écriture
kubectl config use-context dev
# Le contexte prod existe aussi, mais il est en lecture seule (voir plus bas)
kubectl config use-context prod
Pourquoi ces pleins pouvoirs en dev sont acceptables ? Parce que la zone de casse est petite. Le blast radius, c'est ce que ça peut casser si ça tourne mal. En dev, les données sont jetables. Une erreur coûte cinq minutes, pas un client.
Une matrice de permissions par environnement
Un dev n'a pas les mêmes droits en local et en prod. Ton agent non plus. Voici la carte que je suis, du plus permissif au plus verrouillé.
| Capacité | dev | staging | prod |
|---|---|---|---|
| Commandes shell | tout | allowlist | aucune |
| Lire logs / traces | oui | oui | oui (read-only) |
| Lister ressources k8s | oui | oui | oui |
| Write / delete | oui | non | non |
| Lire les secrets | oui | non | non |
| Appliquer Terraform | pipeline | pipeline | pipeline |
| Merger sur main | toujours par PR + review + CI verte | ||
Le reste de l'article, c'est comment je tiens chaque case de cette matrice. Sans jamais compter sur la sagesse de l'agent.
Mets les garde-fous hors du modèle
Voici l'idée qui tient tout l'article. Tu ne fais pas confiance au modèle pour bien se tenir. Tu rends la bêtise impossible, depuis l'extérieur. Quatre barrières : trois sur ma machine, une hors de portée de l'agent.
1. La liste des commandes autorisées
Mon fichier de config donne à l'agent une allowlist courte. Et il refuse l'irréversible.
{
"permissions": {
"allow": [
"Bash(go build:*)", "Bash(go test:*)", "Bash(golangci-lint:*)",
"Bash(git commit:*)", "Bash(git push:*)"
],
"deny": [
"Bash(git push:*--force*origin main*)",
"Bash(gh pr merge:* main*)"
]
}
}
L'agent peut compiler, tester, committer. Il ne peut pas forcer un push sur main. Il ne peut pas merger une PR sur main. Ces deux lignes de deny valent de l'or.
2. Le hook qui demande avant d'agir
Une allowlist ne prévoit pas tout. J'ajoute un hook lancé avant chaque commande. Un hook, c'est un script qui tourne avant une action. Le mien repère les commandes dangereuses.
// Hook PreToolUse : inspecte chaque commande Bash avant exécution.
const DANGEROUS = [
/rm\s+.*-rf\s*\//, // rm -rf sur un chemin absolu
/\bsudo\b/, /\bmkfs\./, /\bdd\s+.*of=\/dev\//,
/\b(curl|wget)\s+.*\|\s*(sh|bash)\b/, // curl | bash
/\/etc\/(passwd|shadow)/, // lecture de fichiers sensibles
];
// Pas de blocage aveugle. On demande à un humain, et on logge tout.
if (DANGEROUS.some((re) => re.test(cmd))) {
console.log(JSON.stringify({ hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: `Commande risquée : ${cmd}`,
}}));
}
Il ne bloque pas bêtement. Il demande. Un humain tranche. Et chaque décision part dans un log.
3. Le pre-commit qui garde le repo
Un pre-commit, c'est un script qui tourne avant chaque commit. Le mien lance gitleaks, le lint et les tests, et refuse les fichiers sensibles.
# lefthook.yml : tourne avant chaque commit
pre-commit:
commands:
block-secrets: # refuse .env, .pem, les clés
glob: ["*.env", "*.pem", "*.key", "id_rsa*"]
run: 'echo "fichier sensible : {staged_files}"; exit 1'
gitleaks:
run: gitleaks protect --staged --redact
lint: { glob: "*.go", run: golangci-lint run }
test: { glob: "*.go", run: go test ./... }
Le point clé tient en une phrase. Ces barrières se fichent qu'un LLM ait écrit la commande. Elles vérifient le résultat, pas l'intention. C'est ça, la sécurité déterministe.
4. La règle GitHub, hors de portée de l'agent
Un hook local, on peut le sauter. La règle côté serveur, non. Sur GitHub, la branche main est protégée par un ruleset. Un ruleset, c'est un jeu de règles appliqué à la branche.
# Protection de la branche main (ruleset GitHub)
required_pull_request_reviews:
required_approving_review_count: 1 # au moins une revue humaine
required_status_checks:
strict: true # la CI doit être verte et à jour
checks: ["lint", "test", "gitleaks"]
allow_force_pushes: false # pas de réécriture d'historique
allow_deletions: false
# aucun bypass : la règle vaut aussi pour les admins
La nuance compte. Les trois premières barrières vivent sur ma machine. Celle-ci vit sur GitHub. L'agent ne peut pas y toucher. C'est la ligne que rien ne franchit.
Les secrets ne passent jamais par l'agent
Un agent qui voit tes secrets de prod, c'est une fuite qui attend son heure. Alors chez moi, il ne les voit pas. Personne ne les voit.
Retour au tout début de mon repo d'infra. Deuxième commit. Un secret part en clair, dans l'historique Git, à la vue de tous. Deux commits. Il a fallu deux commits pour qu'un secret fuite. C'est pour ça que gitleaks tourne à chaque commit aujourd'hui. gitleaks est un outil qui repère les secrets dans le code.
Mais gitleaks n'est que le filet. Le vrai fix, c'est que plus aucun secret ne se tape à la main. Terraform les génère, puis les pousse dans un gestionnaire de secrets. Un gestionnaire de secrets, c'est un coffre qui stocke et distribue les mots de passe et les clés. Chez moi, c'est Infisical.
De là, chaque secret est distribué là où il faut, selon l'environnement. La prod reçoit les secrets de prod, le staging les siens. L'agent, lui, ne reçoit rien.
Et le vrai point : personne ne lit ces secrets en clair. Pas l'agent. Pas moi non plus. Je ne connais aucun mot de passe de ma prod. On ne peut pas fuiter ce qu'on ne connaît pas.
En prod, l'agent hérite de ton contexte, et de rien d'autre
Une précision honnête. Le local tourne déjà. La prod, je la monte en ce moment. Le cluster se met en place, et je pose ces règles au fur et à mesure. Ce que je décris ici, c'est le modèle que je déploie.
En prod, l'agent bascule sur le contexte prod du kubeconfig. Ce contexte est lié à un rôle en lecture seule. Le RBAC, c'est le système Kubernetes qui dit qui a le droit de faire quoi.
# Le contexte prod est lié à ce rôle. Lecture seule, rien d'autre.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: prod
name: agent-readonly
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "events", "services"]
verbs: ["get", "list", "watch"] # ni create, ni update, ni delete, ni exec
Pas d'écriture. Pas de suppression. Pas de shell dans un pod. L'agent peut lister et observer, c'est tout.
Pour comprendre un bug, il lit les logs et les traces. Mais il ne tape pas dans le cluster. Il interroge Grafana et Loki, en lecture seule. Loki, c'est le moteur de logs derrière Grafana. Il ne touche ni la base, ni les secrets.
Le principe est celui du Zero Trust. Tu ne fais confiance à personne par défaut. Tu vérifies à chaque accès. Un agent n'est pas une exception. C'est un sujet à privilèges, comme un compte de service.
La prod se répare en pull request, pas à la main
Reviens à l'agent qui a voulu supprimer mes secrets. Pourquoi ça n'a rien cassé ? Parce que rien ne s'applique en local. La prod ne change que par le pipeline, derrière une pull request.
C'est le principe du GitOps : tu changes ton infra en modifiant du code, pas à la main. L'agent lit les logs, comprend le problème, et propose un correctif en IaC. La review et la CI font barrage. Puis le pipeline applique. Jamais l'agent, jamais à la main.
Concrètement, la boucle est simple. Une trace Jaeger montre une requête lente. L'agent repère l'index qui manque. Il ouvre une PR avec la migration. Je relis, la CI passe, ça part. Il a trouvé le bug sans jamais toucher la base.
Parfois, il faut une lecture plus profonde en prod. Là, je donne un accès en lecture, temporaire et exceptionnel. On appelle ça le juste-à-temps : le droit arrive quand il faut, et repart après. On regarde, puis on retire.
Audite tout : un agent est un utilisateur à privilèges
Un dernier réflexe de sécurité : garde une trace de tout. Le Zero Trust a un troisième principe. Pars du principe qu'une brèche arrivera un jour.
Chaque commande de l'agent part dans un log structuré. Chaque décision de mes hooks aussi. Voici la ligne qu'a laissée l'agent qui a voulu supprimer mes secrets.
{"ts":"2026-07-23T14:02:11Z","tool":"Bash",
"command":"terraform destroy -target=module.secrets",
"decision":"ask","severity":"HIGH",
"violations":["destructive terraform on secrets"],
"session":"a3f9c1","source":"claude-code-hook"}
Un flux d'événements suit ce que font tous mes agents, en temps réel. Le jour où quelque chose tourne mal, je réponds à une seule question. Qu'a fait l'agent, exactement ? Sans ce log, tu ne peux pas.
La checklist par environnement
Avant de laisser un agent de code toucher ton infra, cale ses droits sur ces règles.
- En local, accès large : l'agent va vite, la zone de casse est petite
- En prod, contexte kubeconfig read-only : RBAC en get/list/watch, pas d'écriture, pas de suppression, pas d'exec
- Une allowlist de commandes, et un deny sur l'irréversible (force push, merge sur main)
- Un hook avant chaque commande, qui demande confirmation sur le dangereux
- Des hooks de pre-commit : gitleaks, lint, tests
- Sur GitHub, main protégée côté serveur : PR obligatoire, review requise, CI verte, force-push bloqué
- Les logs et traces de prod lus en read-only (Grafana/Loki), jamais un shell dans le cluster
- Les secrets générés par l'IaC, jamais lus par l'agent
- La prod se change par pull request, appliquée par le pipeline, jamais à la main
- Un accès en lecture prod juste-à-temps, temporaire, puis retiré
- Chaque action de l'agent est loggée et auditable
Ce qu'il faut retenir
Un agent IA n'est pas un outil magique. C'est un collègue non-humain, très rapide, sans jugement. Traite-le comme un dev. Large en local, verrouillé en prod.
Et ne compte pas sur sa sagesse. Mets les garde-fous dehors : les hooks, les pre-commit, les règles GitHub, le RBAC.
Et il y a un effet de bord que je n'attendais pas. Ces barrières ne freinent pas l'agent, elles lui donnent de la marge. Comme il ne peut rien casser, je le laisse travailler seul : ouvrir des PR, enchaîner les tâches, bosser pendant que je fais autre chose. Sans elles, je relirais chaque commande. Bien encadré, un agent va plus loin.
Souviens-toi de l'agent qui a voulu supprimer mes secrets. Il n'a pas pu. Ne parie pas sur un agent sage. Construis un agent qui ne peut pas casser.
Tu déploies des agents de code sur une vraie infra ? Tu veux cadrer leurs droits sans les rendre inutiles ? C'est exactement ce que je fais. Écris-moi.