El contratista con la llave maestra: aislar agentes de IA del workstation al cluster
Primer post de una pareja sobre aislamiento de agentes de IA. Este fija el qué y el dónde: el mapa completo de primitivas de aislamiento y a qué dominio pertenece cada una. El runbook hermano fija el cómo, con comandos:
ai-jaily bubblewrap en el cliente,TracingPolicyde Tetragon yRuntimeClassen el cluster. Si solo vas a leer uno, este te da el modelo mental; el otro, los ficheros que se copian y pegan.
TL;DR
Un agente de IA que ejecuta código necesita acceso a tu filesystem y a tus herramientas: compilador, linter, grep, make, cargo, npm. Ese es el mínimo para ser útil. El problema es que junto a ese acceso viaja la capacidad de leer ~/.aws/credentials, exfiltrar tus claves SSH o lanzar un rm -rf fuera del directorio del proyecto. Y no hace falta un modelo malicioso: basta una dependencia comprometida en un npm install, porque el agente bienintencionado y el post-install script envenenado corren con los mismos permisos. La respuesta no es confiar en las buenas intenciones del LLM; es aislar para acotar el radio de explosión. Este post recorre las cinco familias de aislamiento de 2026 —del sandbox de proceso a la VM completa— y las reparte en dos columnas: lo que aplica en el cliente (el workstation del desarrollador: bubblewrap, ai-jail, sandbox-exec, Landlock, los sandboxes nativos de Claude Code y Codex) y lo que aplica en el cluster (donde el agente o la inferencia corren en Kubernetes: namespaces+seccomp, gVisor, microVMs Firecracker/Kata y eBPF/Tetragon como capa de observación y enforcement en caliente). La tesis: el modelo de amenaza es el mismo en los dos sitios; las herramientas, no. La política se extrapola; la primitiva se reescribe.
La analogía: el contratista con la llave maestra
Contratas a un operario para una reforma. Es competente y va de buena fe. Pero pasan dos cosas que no controlas. La primera: puede malinterpretar la orden y tirar el tabique equivocado. La segunda, peor: su caja de herramientas pudo manipularse antes de que entrara por tu puerta —alguien metió algo dentro—, y cuando la abre en tu salón, ese algo se activa.
Nadie sensato le da la llave maestra del edificio entero. Le abres la habitación donde trabaja, le dejas las herramientas que necesita, y mantienes cerrados el despacho con la caja fuerte y el cuarto de los servidores. Si la reforma sale mal —por error o por sabotaje—, el daño se queda en esa habitación.
Un agente de IA es ese contratista. El sandbox es la política de llaves: le das la habitación del proyecto y las herramientas, no la llave maestra del sistema. Y aquí está el giro que justifica dos posts: el operario trabaja en dos edificios distintos. Uno es tu piso —el workstation del desarrollador, con tus credenciales, tu ~/.ssh, el baúl de contraseñas del navegador—. El otro es el centro de datos —el cluster donde la inferencia y los agentes autónomos sirven a clientes, con datos de varios inquilinos a la vez—. La política de llaves es idéntica en los dos: principio de mínimo privilegio, acota el radio. Pero la cerradura de la puerta de tu piso no es la misma que la del centro de datos. En el piso pones un bombín (bubblewrap). En el centro de datos pones un guardia que vigila cada puerta y un ala separada del edificio (Tetragon + microVM). Mismo principio, distinta ferretería. Eso es extrapolar la tecnología, no copiarla.
El modelo de amenaza: qué puede hacer un agente desbocado
Antes de elegir cerradura conviene enumerar al ladrón. La superficie de ataque de un agente que ejecuta bash arbitrario se descompone en cinco amenazas concretas. No todas se defienden con la misma capa, y —dato incómodo que la propia documentación de seguridad reconoce— ninguna capa las cubre todas.
| Amenaza | Qué hace el agente | Capa mínima que la corta |
|---|---|---|
| Filesystem fuera de alcance | Lee .env, ~/.ssh/id_rsa, secretos del sistema; modifica fuentes fuera del proyecto | Sandbox de proceso (allowlist de rutas) |
| Egress de red arbitrario | Exfiltra datos, recibe instrucciones de un C2 remoto, llama APIs sin autorizar | Bloqueo de red / NetworkPolicy / microVM |
| Superficie de syscalls del kernel | Un exploit del kernel desde el contenedor escala al host (kernel compartido) | gVisor o microVM (kernel dedicado) |
| Fuga entre inquilinos | El workload de un cliente lee datos de otro en una plataforma multi-tenant | microVM (estándar de facto) |
| Exfiltración de secretos | Saca tokens y variables de entorno vía /proc o el environment | --clearenv / tmpfs de $HOME / secretos fuera del pod |
Hay una sexta amenaza que ningún sandbox resuelve: el prompt injection. Si un atacante consigue colar instrucciones en el contexto del agente —un comentario envenenado en el código, un fichero malicioso que el agente lee, una respuesta adversaria de una herramienta—, el agente ejecutará esas instrucciones con los permisos que el sandbox le conceda. El aislamiento encoge el radio de impacto de una inyección exitosa; no impide la inyección. Por eso el sandbox es una capa, no la solución: encima van validación de entrada, allowlists de tool-calls y auditoría de salida. La frase a interiorizar: el aislamiento no hace al agente confiable; acota lo que un agente no confiable puede romper.
Dos dominios, una política
El operario trabaja en dos edificios. El reparto de herramientas se ve mejor a dos columnas:
El cliente: aislar al agente en el workstation
Aquí el agente es un asistente de coding —Claude Code, Codex, OpenCode, Cursor— que un desarrollador lanza en su máquina. Un proceso, un usuario, y al lado los activos más jugosos que existen: ~/.aws/credentials, ~/.ssh, ~/.gnupg, el almacén de contraseñas del navegador. El tier que corresponde es el más ligero: el sandbox de proceso.
bubblewrap (Linux) y sandbox-exec (macOS). bubblewrap (bwrap) es el mismo sandbox que usa Flatpak para aislar cada app de escritorio: ~50 KB de binario, ~4.000 líneas de C, mantenido por el equipo de GNOME, y —la propiedad clave— corre sin root vía CLONE_NEWUSER, creando namespaces sin privilegios elevados. Monta $HOME como un tmpfs efímero y solo expone, con escritura, el directorio del proyecto; el resto del sistema se vuelve invisible. En macOS el equivalente es sandbox-exec con perfiles SBPL: API legacy de Apple, oficialmente deprecada y sin reemplazo público, pero funciona hoy. La paridad entre las dos no es exacta —en macOS la GPU (Metal) y el display (Cocoa) son de sistema y sandbox-exec no los restringe—, pero ambas protegen lo que importa: el acceso a las zonas sensibles del filesystem.
Landlock como segunda barrera. bubblewrap aísla por namespaces y montajes; Landlock —un Linux Security Module disponible desde el kernel 5.13— restringe el acceso a nivel VFS, independiente de los namespaces. No reemplaza a bwrap: lo complementa. Cierra vectores que el aislamiento por montaje no cubre por sí solo (rutas de escape vía /proc, trucos con symlinks dentro de montajes permitidos) y actúa de red de seguridad si la maquinaria de namespaces tuviera un bug. Es defensa en profundidad dentro del propio cliente, y degrada limpiamente a no-op en kernels que no lo soportan.
Dev containers, cuando hace falta reproducibilidad. Un dev container (devcontainer.json, lo que usan Codespaces y Cursor) es un contenedor Docker con una capa de configuración encima. Da aislamiento de filesystem razonable y reset fácil (destruir y recrear), pero comparte el kernel del host —misma limitación que cualquier Docker— y tiende a ser de larga vida, acumulando estado. Para un agente que ejecuta código de tu propio equipo, en tu máquina, es un buen relato de repetibilidad; no es la capa de aislamiento para código no confiable por sí solo.
Lo que envuelve todo esto. El script de bash a mano funciona, pero no escala a un equipo. Las herramientas que lo empaquetan:
ai-jail(Rust, GPL-3.0): envuelvebwrap/sandbox-execcon config por proyecto en un fichero.ai-jail(TOML, commiteable al repo, de modo que todo el equipo hereda la misma política), auto-detección de GPU/Docker/display, modo--lockdown(proyecto en read-only, red cortada con--unshare-net,--clearenv),--dry-runpara auditar, y--bootstrappara generar las allowlists de permisos de cada agente. Es agnóstico de la herramienta: el mismo binario sirve para Claude, Codex, OpenCode o Crush. Aplica además Landlock automáticamente en kernels 5.13+ como defensa en profundidad.- El
/sandboxde Claude Code: desde octubre de 2025, Claude Code trae sandbox propio que usa —exactamente—bubblewrapen Linux ysandbox-execen macOS. Su Sandboxed Bash aísla los comandos de shell, pero no las herramientas de fichero, los servidores MCP ni los hooks, que corren con los permisos completos del proceso salvo que actives el paquete betasandbox-runtime, que envuelve el proceso entero. Hay un matiz que conviene conocer: si un comando falla por una restricción, el agente puede reintentar condangerouslyDisableSandbox—es opt-out, no opt-in—. - Codex CLI: tres modos vía
--sandbox(read-only,workspace-write,danger-full-access); el recomendado por defecto esworkspace-write. La filosofía es deliberada: Codex no provee el aislamiento, lo delega al entorno que lo envuelve.danger-full-accesssolo tiene sentido dentro de una microVM. - Cursor: sus cloud agents corren en VMs aisladas;
/worktreecrea un worktree aislado de un solo uso por tarea, y/best-of-nlanza varios intentos en paralelo en worktrees separados.
El cluster: aislar al agente en producción
El segundo edificio es el centro de datos. Aquí el “agente” puede ser un agente autónomo que corre sin un humano delante, o el propio servicio de inferencia ejecutando código generado, o un workload multi-tenant donde el pod de un cliente no debe tocar los datos de otro. El proceso ya no es uno: son pods en un cluster Kubernetes (RKE2/RKE3 en una plataforma soberana típica). Las primitivas cambian de naturaleza.
El baseline del pod. Antes de nada, lo de serie: namespaces de Linux, seccomp (RuntimeDefault) para recortar la superficie de syscalls, cgroups para los límites de recursos, securityContext sin privilegios (runAsNonRoot, readOnlyRootFilesystem, drop de todas las capabilities) y NetworkPolicy para cortar el egress. Es el equivalente cluster de la allowlist del sandbox de proceso. Necesario, pero comparte kernel con el host: insuficiente para código realmente no confiable.
gVisor (runsc). El kernel en espacio de usuario de Google: intercepta las syscalls del workload antes de que lleguen al kernel del host y las atiende dentro de un kernel Linux reimplementado en Go (el Sentry). La superficie expuesta a vulnerabilidades del kernel del host se reduce drásticamente, manteniendo arranque rápido y footprint bajo. Es el término medio cuando el riesgo de escape de kernel es real pero el overhead de una microVM no es asumible.
microVMs Firecracker / Kata. El estándar de facto para código no confiable en 2026. Firecracker (VMM de AWS en Rust, sobre KVM) da a cada sandbox un kernel Linux dedicado: un exploit de kernel dentro de la microVM no alcanza al host por construcción. Es lo que hay debajo de Vercel Sandbox (GA enero 2026) y E2B. En Kubernetes, Kata Containers trae ese modelo a un RuntimeClass: marcas el pod del agente no confiable con runtimeClassName: kata y se ejecuta en su propia microVM en lugar de compartir el kernel del nodo. Para multi-tenant con código generado, esto es el baseline, no el lujo.
eBPF / Tetragon: la capa que ya tenemos. Aquí está la pieza que distingue una plataforma con observabilidad de runtime de una que solo confía en la configuración. Las capas anteriores son estáticas: definen lo que el pod puede hacer antes de arrancar. Tetragon —el componente de seguridad runtime de Cilium, basado en eBPF— es dinámico: observa, en el kernel y con coste mínimo, cada ejecución de proceso, cada conexión de red y cada apertura de fichero de cada pod, y puede actuar en línea. No reemplaza al sandbox; lo vigila desde dentro del kernel. Donde bubblewrap en el cliente bloquea curl con una blocklist de comandos, Tetragon en el cluster engancha tcp_connect en el kernel y, si el destino no está permitido, mata el proceso con Sigkill antes de que el paquete salga. Donde el cliente esconde ~/.ssh tras un tmpfs, Tetragon engancha security_file_open y reporta —o mata— cualquier intento de leer una ruta sensible montada. Es el guardia que recorre los pasillos mientras las microVMs son las paredes. Y es, exactamente, el tipo de control que materializa las medidas de monitorización y trazabilidad del ENS (op.mon, op.exp) sin instrumentar la aplicación: la visibilidad vive en el kernel, no en el código del agente.
La tabla del panorama
Las cinco familias, su fortaleza relativa de aislamiento, su coste de arranque y el dominio donde viven:
| Tier | Primitiva | Aislamiento | Arranque | Dominio natural |
|---|---|---|---|---|
| Sandbox de proceso | Seatbelt · bubblewrap | Baseline | ~0 ms | Cliente (defecto de Claude Code) |
| Dev container | Docker + seccomp | Moderado | segundos | Cliente / cluster (repetibilidad) |
| Kernel user-space | gVisor (runsc) | Fuerte | ms | Cluster (multi-tenant medio) |
| microVM | Firecracker · Kata | El más fuerte (práctico) | <1 s | Cluster (código no confiable) |
| VM completa | KVM · EC2 | Máximo | 30 s+ | Cluster (frontera externa, compliance) |
| Runtime enforcement | eBPF · Tetragon | Transversal | siempre activo | Cluster (observa+mata sobre cualquier tier) |
Tetragon ocupa una fila aparte a propósito: no es un tier en la escalera, es una capa transversal que opera sobre cualquiera de los otros. Se apila con todos.
Un apunte numérico sobre por qué la columna “arranque” decide tanto como la columna “aislamiento”. Una VM completa gana en aislamiento bruto pero tarda decenas de segundos en provisionarse; para un agente que necesita un entorno fresco por petición o por sesión, ese coste es prohibitivo. Una microVM Firecracker arranca en menos de 1 segundo y un sandbox de proceso en ~0 ms. Por eso el patrón dominante en 2026 no es “la VM más aislada”, sino VM completa como frontera externa + microVM como unidad de ejecución por petición dentro —la arquitectura de Vercel, AWS Lambda y E2B—. En el cliente el cálculo es el opuesto: el desarrollador lanza el agente decenas de veces al día de forma interactiva, y un arranque de segundos rompería el flujo; de ahí que el sandbox de proceso, con su overhead de microsegundos, sea el defecto correcto.
Extrapolar, no copiar
La tesis de la pareja de posts cabe en una frase: el modelo de amenaza es invariante entre dominios; la primitiva que lo implementa, no. El cliente y el cluster defienden exactamente los mismos cinco vectores —filesystem, red, kernel, multi-tenant, secretos—, pero con cajas de herramientas que no se solapan. Cada control tiene su gemelo en el otro lado:
$HOMEcomo tmpfs efímero (cliente) ↔readOnlyRootFilesystem+emptyDir(cluster).- Blocklist de
curl/wgetenbwrap(cliente) ↔TracingPolicysobretcp_connecten Tetragon + NetworkPolicy (cluster). --unshare-neten lockdown (cliente) ↔ NetworkPolicy default-deny (cluster).- Sin escape hatch, el proceso vive dentro de
bwrap(cliente) ↔ sinprivileged, sinhostPath,RuntimeClasskata (cluster). ~/.sshy~/.awsnunca montados (cliente) ↔ secretos fuera del pod + Tetragon vigilandosecurity_file_open(cluster).
El runbook hermano convierte cada una de estas equivalencias en ficheros concretos. Lo que importa retener aquí es el método: cuando alguien te enseña un sandbox de agente —sea el /sandbox de Claude Code en un portátil o un microVM en un PaaS—, la pregunta útil no es “¿qué herramienta usa?”, sino “¿cuál de los cinco vectores cierra, y cuál deja abierto?”. La herramienta se sustituye; el mapa de amenazas se queda.
Lo que ningún sandbox resuelve
Tres límites que la propia documentación de Anthropic enuncia, y que conviene tener delante para no vender humo:
- El egress sigue siendo un riesgo en cualquier sandbox que permita conexiones salientes. Si el agente puede abrir una conexión, puede exfiltrar. Por eso el lockdown del cliente corta la red y el cluster usa NetworkPolicy default-deny + Tetragon: no se confía en “filtrar bien”, se confía en “no dejar salir”.
- La modificación de código sigue siendo posible en cualquier sandbox con el directorio del proyecto montado en escritura. El remedio no es técnico-de-sandbox, es git: con el remoto intacto y sin permiso de
push, el peor caso es corromper el working copy local —git checkout .y a empezar—. El daño no llega al remoto. - Ningún sandbox impide que un prompt comprometido llegue a la API. El aislamiento acota el impacto de una inyección; no la previene. Las defensas complementarias —validación de entrada, allowlists de tool-calls, auditoría de salida— son obligatorias junto al aislamiento, no en su lugar.
La conclusión operativa: el aislamiento encoge el radio de explosión; la defensa en profundidad es lo que cierra el círculo. Un sandbox de proceso para código de confianza en una máquina conocida es apropiado y prácticamente gratis. Para un agente que actúa sobre prompts de usuario, ejecuta código generado o corre en multi-tenant, el mínimo aceptable en 2026 es una microVM, con Tetragon observando por encima. Elige el tier que case con tu amenaza real, verifica qué vector deja abierto, y apila controles complementarios encima.
Ver también
- Runbook: enjaular al agente de IA — bubblewrap en el cliente, Tetragon en el cluster — el compañero operativo de este post: los ficheros
.ai-jail, el--bootstrapde permisos y lasTracingPolicyde Tetragon que se copian y pegan. Con comandos. - La puerta de la cocina que el maître no miró: NUMA de red, Cilium eBPF y DRANET — el datapath eBPF de Cilium sobre el que se apoya Tetragon en el cluster; la misma capa de kernel, otro uso.
- Guardrails y safety en LLM — la mitigación en el plano del contenido (qué dice y qué se le dice al modelo); este post es la mitigación en el plano de la ejecución (qué puede hacer el proceso del agente).
- Controles técnicos: ENS × ISO 42001 × EU AI Act — el marco de cumplimiento que el aislamiento de runtime materializa: Tetragon como evidencia técnica de
op.mon/op.exp. - Catálogo de herramientas OSS para LLMOps — dónde encaja la capa de seguridad runtime en el stack abierto completo.
Referencias
- bubblewrap: https://github.com/containers/bubblewrap
- Landlock LSM: https://landlock.io · https://docs.rs/landlock
- ai-jail (Fabio Akita): https://github.com/akitaonrails/ai-jail
- gVisor: https://gvisor.dev
- Firecracker: https://firecracker-microvm.github.io
- Kata Containers: https://katacontainers.io
- Tetragon (Cilium): https://tetragon.io
- Vercel Sandbox — concepts: https://vercel.com/docs/vercel-sandbox/concepts
- E2B: https://github.com/e2b-dev/E2B
- Claude Code sandboxing: https://docs.claude.com/en/docs/claude-code
- Codex CLI: https://github.com/openai/codex