Détecter le comportement anormal d'un agent IA au runtime avec Falco et Tetragon
Falco et Tetragon détectent l'agent IA en conteneur qui sort de son enveloppe : appel d'outil anormal, spawn de process, escalade par prompt malveillant.
Un agent IA en conteneur, ou un serveur MCP exposant des outils à ce même agent, n'est pas un service applicatif comme les autres. Il reçoit des instructions en langage naturel, décide seul des actions à exécuter, et dispose souvent d'un accès réseau sortant et d'un droit d'exécution shell pour accomplir ses tâches. Le monitoring de conteneur classique (CPU, mémoire, logs applicatifs) ne voit rien de ce qui compte vraiment : quel outil l'agent a appelé, quel process il a fait naître, vers quelle destination il s'est connecté. Cet article détaille comment combler ce trou de visibilité avec Falco et Tetragon, au niveau syscall, sans dépendre de ce que l'agent choisit de logger lui-même.
Le modèle de menace propre à l'agent IA
Un agent IA en conteneur introduit trois familles de risques que le monitoring réseau ou applicatif ne couvre pas :
- L'appel d'outil anormal : l'agent invoque une fonction hors de son périmètre déclaré (accès à un fichier de secrets, appel à une API de facturation alors que sa mission est du support client).
- Le spawn de process inattendu : l'agent ou le serveur MCP qui l'outille lance un shell, un interpréteur, ou un binaire réseau (
nc,curlvers une IP externe,wget) qui n'a aucune raison d'apparaître dans son cycle de vie normal. - L'escalade déclenchée par prompt : une injection de prompt (via un document ingéré, une réponse d'outil empoisonnée, ou une entrée utilisateur malveillante) pousse l'agent à exécuter une action hors mandat, par exemple lire
/etc/passwd, ouvrir une connexion sortante non prévue, ou modifier sa propre configuration.
Ce dernier point est le plus spécifique aux agents IA : le vecteur d'attaque n'est pas une vulnérabilité logicielle classique, c'est le contenu même que l'agent traite. La détection ne peut donc pas reposer sur le code de l'agent (qui a exactement fait ce qu'on lui a demandé, du point de vue du processus) mais sur son comportement système observé de l'extérieur.
Établir la baseline du conteneur agent ou MCP
Avant d'écrire la moindre règle, il faut caractériser ce que fait le conteneur en fonctionnement normal, sur une fenêtre représentative (idéalement plusieurs jours, incluant les pics d'usage) :
- Syscalls attendus : ouverture de fichiers de configuration, lecture du modèle ou du cache, écriture de logs applicatifs dans un chemin précis.
- Processus enfants légitimes : un agent qui appelle un outil de recherche web via un binaire dédié, ou un serveur MCP qui lance un sous-processus d'indexation, ont un arbre de processus stable et répétitif.
- Connexions sortantes attendues : l'API du fournisseur de modèle, la base vectorielle interne, les API métier autorisées. Cette liste doit être une allowlist explicite, pas une déduction a posteriori.
Cette baseline se construit avec Falco en mode observation pure (règles en priority: INFO, sans alerte bruyante) pendant la phase de qualification, puis se traduit en règles de détection strictes une fois le périmètre stabilisé.
Règle Falco : process inattendu et accès fichier hors scope
Falco observe les événements au niveau noyau via son driver (module kernel ou eBPF) et les confronte à des règles déclaratives. Voici une règle qui alerte sur un spawn de process inattendu dans le conteneur agent :
- rule: Unexpected process spawned in AI agent container
desc: >
An AI agent or MCP server container spawned a process outside its
declared tool baseline (shell, interpreter, or network utility).
condition: >
spawned_process
and container
and container.image.repository = "registry.internal/ai-agent"
and not proc.name in (agent_baseline_binaries)
and (proc.name in (shell_binaries) or proc.name in (net_binaries))
output: >
Unexpected process in AI agent container
(container=%container.name image=%container.image.repository
process=%proc.cmdline parent=%proc.pname user=%user.name)
priority: WARNING
tags: [ai-agent, process, mitre_execution]
- rule: AI agent container reading file outside declared scope
desc: >
File access outside the agent's declared working directory or
tool-output paths, a strong signal of prompt-driven exfiltration attempt.
condition: >
open_read
and container.image.repository = "registry.internal/ai-agent"
and not fd.name startswith /app/workdir
and not fd.name startswith /app/tool-cache
and (fd.name contains "/etc/" or fd.name contains ".ssh" or fd.name contains "secrets")
output: >
AI agent container reading out-of-scope file
(container=%container.name file=%fd.name process=%proc.cmdline)
priority: WARNING
tags: [ai-agent, filesystem, mitre_collection]Les listes agent_baseline_binaries, shell_binaries et net_binaries sont des macros Falco (list:) maintenues séparément, alimentées par la phase de baseline. C'est ce découpage qui rend la règle maintenable : le corps de la règle ne change pas quand la liste d'exceptions évolue.
Tetragon eBPF : TracingPolicy avec enforcement
Falco alerte, il n'empêche pas. Pour un agent IA qui peut potentiellement exécuter des actions destructrices en quelques secondes, la détection seule ne suffit pas toujours : il faut parfois pouvoir bloquer l'action avant qu'elle ne se termine. C'est le rôle de Tetragon, qui observe les mêmes catégories d'événements via eBPF mais peut aussi les intercepter en kernel-space, avant retour à l'espace utilisateur.
Une TracingPolicy Tetragon sur execve et connect, avec enforcement (kill) sur violation :
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: ai-agent-runtime-enforcement
spec:
podSelector:
matchLabels:
app: ai-agent
kprobes:
- call: "sys_execve"
syscall: true
args:
- index: 0
type: "string"
selectors:
- matchArgs:
- index: 0
operator: "NotPrefix"
values:
- "/app/bin/"
- "/usr/bin/python3"
matchActions:
- action: Sigkill
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
selectors:
- matchArgs:
- index: 0
operator: "NotDAddr"
values:
- "10.0.4.0/24" # API modèle interne
- "10.0.5.10/32" # base vectorielle
matchActions:
- action: SigkillCe TracingPolicy termine immédiatement (Sigkill) tout processus qui exécute un binaire hors de la liste autorisée, et toute connexion TCP sortante vers une destination hors des plages déclarées. Le mécanisme intervient au niveau du kprobe, avant que la connexion ne soit établie ou que l'exécution ne se poursuive : c'est un enforcement réel, pas une alerte a posteriori.
Distinguer l'outillage légitime de l'abus
Le piège classique est de confondre deux comportements qui se ressemblent en surface. Un agent IA légitime qui utilise un outil de recherche web fait des connexions sortantes HTTP vers des domaines variables, ce qui est normal si le prompt le demande. Un agent compromis qui exfiltre des données via une connexion sortante, ou qui ouvre un reverse shell suite à une injection de prompt, produit un pattern différent :
| Signal | Outillage légitime | Abus (exfiltration / reverse shell) |
|---|---|---|
| Processus parent | Toujours le runtime de l'agent | Shell inattendu, interpréteur non déclaré |
| Destination réseau | Domaines dans la liste d'outils déclarée | IP brute, port inhabituel, domaine jamais vu |
| Séquence | Appel outil → réponse → traitement | Exécution shell → connexion sortante immédiate |
| Fichiers touchés | Répertoire de travail, cache d'outils | /etc/, clés SSH, répertoires de secrets |
La règle qui fonctionne en pratique croise toujours au moins deux signaux (processus et destination, ou fichier et processus parent), jamais un seul en isolation. Une connexion sortante isolée déclenche trop de faux positifs sur un agent qui fait légitimement de la recherche web ; un shell isolé peut être un outil de diagnostic autorisé. C'est la combinaison qui distingue l'abus de l'usage normal.
Tuning anti-faux-positifs par image et par conteneur
Un exemple avant / après illustre le gain du tuning par exception ciblée. Avant tuning, la règle Falco générique déclenche sur chaque healthcheck HTTP interne du conteneur MCP, plusieurs centaines de fois par jour :
# Avant : condition trop large, alerte sur le healthcheck légitime
condition: >
spawned_process
and container.image.repository = "registry.internal/mcp-server"
and proc.name = "curl"Après ajout d'un bloc d'exceptions scopé à l'image et au conteneur précis, la règle ne cible plus que les destinations hors allowlist :
# Après : exception nominative par image + conteneur, healthcheck exclu
- list: mcp_healthcheck_targets
items: ["http://localhost:9090/health", "http://127.0.0.1:9090/ready"]
condition: >
spawned_process
and container.image.repository = "registry.internal/mcp-server"
and proc.name = "curl"
and not proc.cmdline in (mcp_healthcheck_targets)Ce bloc d'exceptions doit rester scopé à l'image et au conteneur exacts, jamais à une règle globale qui s'appliquerait aussi aux conteneurs applicatifs classiques. Un agent IA de production justifie un profil de détection dédié, distinct du profil générique du reste du cluster, précisément parce que son enveloppe comportementale (appels d'outils, exécution dynamique) est plus large qu'un microservice classique.
Ce que cela change pour le RSSI
La visibilité runtime sur un agent IA ou un serveur MCP ne s'obtient pas en ajoutant des logs applicatifs, elle s'obtient en observant le système depuis le kernel, indépendamment de ce que l'agent choisit de rapporter sur lui-même. C'est la différence entre faire confiance à l'agent et vérifier son comportement.
Ce travail de baseline, de règles Falco et de TracingPolicy Tetragon, validé sur des conteneurs agent réels et débruité par un corpus de faux positifs mesuré, est exactement ce que le pack cloud-native de ThreatClaw livre prêt à l'emploi. Découvrez le flux de détection cloud-native.
Articles liés
Détection cryptojacking Kubernetes Falco : la règle qui repère un binaire lancé depuis /tmp, les connexions vers un pool de minage, et ce qui reste en HITL.
Le scan d'image ne voit rien de ce qui se passe à l'exécution. Guide sur la sécurité runtime conteneurs eBPF avec Falco et Tetragon : règles, exemples et pièges à éviter.
Google observe des clusters attaqués en 18 minutes et des évasions via pods privilégiés. Voici la détection Falco au runtime et les garde-fous OPA à l'admission.
Détection ransomware agent IA autonome : JADEPUFFER, premier chiffreur piloté sans opérateur, et la méthode de corrélation pour le repérer sans faux positif.