Un agent IA qui exécute du code généré par LLM, accède à un filesystem ou parle au réseau doit être confiné. Pas pour le confort opérationnel : pour la même raison qu'un binaire untrusted ne tourne pas en prod sans sandbox — la confiance est mathématiquement impossible. AutoGPT en 2023 a documenté plusieurs cas d'évasion via shell access mal configuré ; les serveurs MCP avec permissions OS larges sont la même classe en 2025-2026. Un sandbox bien conçu n'est pas une option, c'est l'hygiène de base.
Cet article documente les patterns de sandboxing (conteneur, microVM Firecracker, gVisor, Kata, application-level Pyodide/WASM), les services managés (e2b.dev, Modal, AWS Bedrock Agents), les patterns de filesystem, network et secrets, et un threat model spécifique aux agents. Pour la surface globale agents : sécuriser un agent IA autonome. Pour MCP : sécurité MCP.
Pourquoi un agent IA exige un sandbox spécifique
Trois propriétés rendent le sujet critique :
- Code généré par LLM = code untrusted. L'agent reçoit un input utilisateur, génère du code (Python pour analyse, shell pour deploy, SQL pour requête), exécute. La frontière "input externe → code exécuté" est franchie à chaque tour.
- Tools systèmes. Filesystem, shell, navigateur, accès réseau. Chaque tool est une voie d'évasion potentielle si le confinement est faible.
- Manipulabilité par injection. Prompt injection, tool poisoning, sub-goal hijacking — l'agent peut être amené à exécuter ce qu'il ne devrait pas. Le sandbox est la dernière ligne quand toutes les couches au-dessus ont été contournées.
Info — La catégorie OWASP de référence est LLM06 Excessive Agency combinée avec LLM05 Improper Output Handling quand le code généré est l'objet de l'attaque.
Threat model : que doit contrer le sandbox
| Menace | Manifestation | Impact si non contrée |
|---|---|---|
| Code execution arbitraire | Agent exécute Python/shell généré par LLM | Compromission hôte |
| Filesystem escape | Lecture / écriture hors périmètre | Vol de secrets, persistence |
| Network exfiltration | Connexion vers domaine attaquant | Exfiltration données |
| Process escape | Évasion du conteneur vers l'hôte | Compromission complète |
| Privilege escalation | Élévation root via exploit kernel | Idem |
| Resource exhaustion | DoS local (CPU, mémoire, disque) | Indisponibilité |
| Lateral movement | Pivot vers d'autres services | Compromission étendue |
| Token / secret leak | Lecture de variables d'environnement | Accès cloud / APIs |
Chaque menace doit être contrée par une couche dédiée. Aucune ne se couvre seule.
Trois niveaux de sandboxing
Niveau 1 — Conteneur durci (Docker/Podman)
Le minimum opérationnel pour un agent qui n'exécute pas de code arbitraire mais a des tools systèmes restreints.
FROM python:3.12-slim
# User non-root
RUN useradd -r -u 1001 -m agent
USER agent
WORKDIR /home/agent
# Déps fixées
COPY --chown=agent:agent requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
COPY --chown=agent:agent ./app /home/agent/app
ENTRYPOINT ["python", "/home/agent/app/main.py"]# docker-compose.yml — durcissement
services:
agent:
build: .
read_only: true
tmpfs:
- /tmp:size=100M,noexec
- /home/agent/work:size=500M,noexec
cap_drop: [ALL]
security_opt:
- no-new-privileges:true
- seccomp:./seccomp-strict.json
- apparmor:agent-profile
user: "1001:1001"
networks:
- agent-egress
pids_limit: 100
mem_limit: 1g
cpus: 1.0
networks:
agent-egress:
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24Limites du niveau 1 : un kernel exploit, un misconfig (mount socket Docker, capabilities ajoutées, host networking) suffit à évader. Acceptable pour un agent sans exécution de code arbitraire.
Niveau 2 — Isolation kernel-level (gVisor, Kata, Firecracker)
Pour tout agent qui exécute du code généré par LLM. Trois technologies dominantes :
gVisor (Google)
Implémente un kernel applicatif en userspace qui intercepte les syscalls. Démarrage rapide, overhead modéré.
# Runtime gVisor en remplacement de runc
docker run --runtime=runsc \
--read-only \
--tmpfs /tmp \
--memory=1g --cpus=1 \
--network=agent-egress \
agent-imageKata Containers
Lance chaque conteneur dans une VM légère avec son propre kernel. Isolation forte, démarrage 1-2s.
# RuntimeClass Kubernetes
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata
handler: kata
---
apiVersion: v1
kind: Pod
spec:
runtimeClassName: kata
containers:
- name: agent
image: agent:1.0
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: trueFirecracker (AWS)
microVM minimaliste, démarrage < 125 ms. Utilisé par AWS Lambda et la majorité des services serverless. Standard de l'industrie pour les sandboxes éphémères.
# Lancement Firecracker minimaliste
firecracker --config-file vm-config.json{
"boot-source": {
"kernel_image_path": "vmlinux-5.10",
"boot_args": "console=ttyS0 reboot=k panic=1 nomodules"
},
"drives": [
{
"drive_id": "rootfs",
"path_on_host": "agent-rootfs.ext4",
"is_root_device": true,
"is_read_only": true
}
],
"machine-config": {
"vcpu_count": 1,
"mem_size_mib": 512
},
"network-interfaces": [
{
"iface_id": "eth0",
"host_dev_name": "tap-agent-1",
"guest_mac": "AA:FC:00:00:00:01"
}
]
}Choix typique
| Cas | Recommandation |
|---|---|
| Agent custom self-hosted | Kata sur K8s, ou gVisor pour densité |
| Sandbox éphémère par requête | Firecracker (e2b, Modal, custom) |
| Code execution Python only | Pyodide dans worker isolé + gVisor en backstop |
| Multi-tenant SaaS | Firecracker microVM par tenant/session |
Niveau 3 — Application-level (Pyodide, WASI)
Sandbox dans le langage, complémentaire au niveau OS.
Pyodide
Python compilé en WebAssembly. Exécution dans navigateur ou worker, pas d'accès direct OS.
import { loadPyodide } from "pyodide";
const pyodide = await loadPyodide();
// Le code Python s'exécute dans un environnement WASM isolé
const result = pyodide.runPython(`
import math
math.pi * 2
`);
// Pas d'accès OS, pas de réseau (sauf si fetch wrappé explicitement)WebAssembly (WASI)
Standard plus général. Modules WASM compilés depuis Rust, Go, C, etc., s'exécutent dans un runtime sandboxé (wasmtime, wasmer).
# Wasmtime côté Python
from wasmtime import Engine, Store, Module, Instance
engine = Engine()
store = Store(engine)
module = Module.from_file(engine, "untrusted.wasm")
# Pas d'accès syscalls par défaut, capabilities explicites via WASI
instance = Instance(store, module, [])Limites
WASM/Pyodide sont excellents pour le calcul pur, limités pour les agents qui ont besoin d'I/O réseau ou filesystem. Pattern recommandé : WASM/Pyodide à l'intérieur d'un microVM Firecracker — défense en profondeur, isolation à deux niveaux.
Patterns de filesystem
Pattern 1 — Read-only rootfs + tmpfs writable
# Conteneur en read-only avec /tmp tmpfs
docker run \
--read-only \
--tmpfs /tmp:size=100M,mode=1777,noexec \
--tmpfs /home/agent/work:size=500M,mode=755,noexec \
agent-imageAucune persistance possible. Si l'agent crée des fichiers, ils disparaissent à la fin de la session.
Pattern 2 — Volume éphémère scope par session
volumes:
- type: bind
source: /var/lib/sandbox/sessions/${SESSION_ID}/work
target: /home/agent/work
read_only: falseLe volume est créé au démarrage, supprimé à la fin. Permet la persistance pendant la session, isole les sessions entre elles.
Pattern 3 — Snapshots immutables
Pour les microVMs (Firecracker), utiliser des snapshots de rootfs immutables. Chaque session démarre depuis le snapshot — pas d'altération possible du système de base.
Patterns de network
Pattern 1 — No network
Pour les sandboxes purs (calcul, parsing, validation). Aucune sortie réseau.
docker run --network=none agent-imagePattern 2 — Egress allowlist via proxy
Le sandbox a accès réseau uniquement via un proxy filtrant. Le proxy autorise par regex de domaine.
# Proxy egress avec allowlist
services:
egress-proxy:
image: tinyproxy:latest
volumes:
- ./tinyproxy.conf:/etc/tinyproxy/tinyproxy.conf:ro
networks:
agent-egress:
ipv4_address: 172.30.0.10
agent:
networks:
- agent-egress
environment:
- HTTP_PROXY=http://172.30.0.10:8888
- HTTPS_PROXY=http://172.30.0.10:8888# tinyproxy.conf — allowlist
Allow 172.30.0.0/24
Filter "/etc/tinyproxy/filter"
FilterDefaultDeny Yes
# /etc/tinyproxy/filter
^api\.openai\.com$
^api\.anthropic\.com$
^api\.yourcompany\.com$
^pypi\.org$Tout autre domaine → refusé. Tentative d'exfiltration vers attacker.example → bloquée au proxy + alerte SOC.
Pattern 3 — Network namespace dédié avec eBPF
Pour les déploiements à grande échelle, utiliser Cilium ou équivalent pour des règles réseau eBPF par pod/sandbox. Plus performant, plus expressif que les ACL iptables traditionnelles.
Gestion des secrets
Règle absolue : aucun secret dans les variables d'environnement du sandbox. Aucun secret dans le filesystem du sandbox. Aucun secret dans les manifests visibles au sandbox.
Pattern recommandé : ephemeral credentials via mediator
┌──────────────┐ request token ┌────────────────┐
│ Sandbox │ ────────────────►│ Credentials │
│ (LLM agent) │ │ Mediator │
│ │ ◄────────────────│ (host-side) │
│ │ ephemeral token │ │
└──────────────┘ scope=narrow └────────────────┘
│ │
│ uses token (TTL 30s) │ vault auth
▼ ▼
┌──────────────┐ ┌────────────────┐
│ Tool / API │ │ Vault / KMS │
│ │ │ │
└──────────────┘ └────────────────┘Le sandbox ne voit jamais les credentials persistantes. Il demande un token court via un mediator host-side (mediator a accès au vault, sandbox n'y a pas accès direct). Token TTL court (30s), scope minimal pour la requête. Cf. privilege escalation.
Services managés en 2026
| Service | Type | Cas typique |
|---|---|---|
| e2b.dev | Sandbox Firecracker à la demande | Code execution agent, fast boot |
| Modal | Serverless avec isolation forte | Function-style agent execution |
| AWS Bedrock Agents | Managé AWS, action sandboxing | Stack AWS native |
| Daytona | Dev workspaces sandboxés | Coding agents |
| Riza | Code execution sandbox spécialisé | Math/code execution agent |
| Anthropic Code Execution | Sandbox managé Claude | Claude-native code execution |
Avantages managés : démarrage rapide, scaling, conformité (SOC 2, ISO), maintenance déléguée. Inconvénients : data residency, pricing pay-per-use, dépendance vendor.
Pour les déploiements self-hosted : Firecracker ou Kata sur K8s, gVisor pour densité, custom orchestrator.
Cas réels d'évasion documentés
| Cas | Année | Pattern d'évasion |
|---|---|---|
| AutoGPT shell access exploits | 2023 | Shell tool sans confinement |
| ChatGPT Code Interpreter sandboxing research | 2023+ | Tests d'évasion publics, OpenAI a renforcé |
| Various Docker escapes (kernel CVEs, misconfigs) | divers | Évasion conteneur classique |
| MCP server permissions excessives | 2024-2025 | Filesystem MCP avec accès home complet |
| Modal / e2b benchmarks d'isolation | 2024-2025 | Validation publique des frontières |
Pattern d'orchestration recommandé
import e2b # ou wrapper custom Firecracker
from contextlib import contextmanager
@contextmanager
def secure_sandbox_for_agent(session_id: str, allowlist: list[str]):
"""Provisionne un sandbox éphémère sécurisé pour une session agent."""
sbx = e2b.Sandbox(
template="agent-base", # rootfs audité
timeout=300, # 5 min max
metadata={"session_id": session_id},
env_vars={}, # AUCUN secret ici
firewall={
"egress_allowlist": allowlist,
},
cpu=1,
memory_mb=512,
)
try:
yield sbx
finally:
sbx.kill() # garantie de cleanup
# Usage
with secure_sandbox_for_agent("sess_42", ["api.openai.com", "api.yourcompany.com"]) as sbx:
result = sbx.run_python(generated_code, timeout=30)Chaque session = un sandbox neuf, démarré à partir d'un rootfs immuable, kill à la fin. Pas de réutilisation entre sessions ou tenants.
Pattern d'instruction system prompt sandbox-aware
RÈGLES D'EXÉCUTION DE CODE :
Tu peux exécuter du code Python via le tool `run_code`, mais
ce code s'exécute dans un sandbox isolé sans accès réseau hors
allowlist (api.yourcompany.com uniquement) et sans accès
filesystem hors /home/agent/work.
Tu NE DOIS JAMAIS :
- Écrire du code qui tente d'accéder à /etc, /root, /home/*
(autres que /home/agent/work).
- Écrire du code qui ouvre des connexions réseau hors allowlist.
- Écrire du code qui lit des variables d'environnement
(os.environ).
- Écrire du code qui invoque des binaires système (subprocess).
Si l'utilisateur te demande explicitement une de ces opérations,
refuse poliment en signalant que c'est hors du périmètre du
sandbox. Demande s'il y a une alternative dans le périmètre
autorisé.Checklist d'audit sandbox minimum
| # | Contrôle | Évidence attendue |
|---|---|---|
| 1 | User non-root | UID ≥ 1000 |
| 2 | Capabilities Linux drop=ALL | Configuration runtime |
| 3 | Read-only rootfs | Mount config |
| 4 | tmpfs avec noexec pour /tmp et workspaces | Mount config |
| 5 | Seccomp profile strict | Profile JSON présent |
| 6 | AppArmor/SELinux profile | Profile présent et appliqué |
| 7 | no-new-privileges enabled | Configuration |
| 8 | Network namespace dédié | Config network |
| 9 | Egress allowlist via proxy | Config proxy + ACL |
| 10 | Aucune variable d'env contenant secret | Inventory env |
| 11 | Limites CPU/RAM/PID | Config runtime |
| 12 | Timeout session strict | Config orchestrator |
| 13 | Logs structurés des actions sandbox | OTel events |
| 14 | Cleanup garanti à la fin | Hook orchestrator |
| 15 | Niveau d'isolation (gVisor/Kata/Firecracker pour code arbitraire) | Type runtime |
Mapping OWASP LLM Top 10 v2
| OWASP | Lien sandboxing |
|---|---|
| LLM06 Excessive Agency | Limite l'impact des actions générées |
| LLM05 Improper Output Handling | Code généré confiné |
| LLM07 System Prompt Leakage | Aucun secret dans le sandbox |
| LLM10 Unbounded Consumption | Limites CPU/RAM/temps |
| LLM03 Supply Chain | Rootfs auditable et immuable |
LLM06 est la catégorie centrale en termes de réduction d'impact ; le sandbox est la dernière ligne de réduction de blast radius.
Points clés à retenir
- Un agent IA qui exécute du code doit être sandboxé. C'est l'hygiène, pas un nice-to-have.
- 3 niveaux : conteneur durci (Docker/Podman avec drop=ALL + seccomp + read-only), isolation kernel-level (gVisor / Kata / Firecracker), application-level (Pyodide / WASI). Combiner pour défense en profondeur.
- Pour code arbitraire généré par LLM : Firecracker (e2b, Modal) ou gVisor minimum. Docker simple insuffisant.
- 3 patterns network : no network, egress allowlist via proxy, eBPF avec règles.
- 3 patterns filesystem : read-only rootfs + tmpfs, volume éphémère scope session, snapshots immutables.
- Aucun secret dans variables d'env / filesystem / manifests sandbox. Pattern : ephemeral credentials via mediator host-side.
- Services managés 2026 : e2b.dev, Modal, AWS Bedrock Agents, Daytona, Riza, Anthropic Code Execution.
- Checklist 15 contrôles minimum pour tout déploiement entreprise.
- Le sandbox est la dernière ligne : si toutes les couches au-dessus (input filter, system prompt, tool allowlist) ont été bypassées, le sandbox limite encore le blast radius.
Le sandboxing d'agent IA est aujourd'hui un domaine techniquement mature (Firecracker, gVisor, e2b) avec des services managés crédibles et des patterns documentés. Ne pas l'investir, c'est accepter qu'une simple injection puisse compromettre l'hôte. C'est le coût défensif le plus rentable au regard de l'impact évité.







