<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Firecracker on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/firecracker/</link><description>Recent content in Firecracker on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Tue, 09 Jun 2026 16:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/firecracker/index.xml" rel="self" type="application/rss+xml"/><item><title>El contratista con la llave maestra: aislar agentes de IA del workstation al cluster</title><link>https://blog.lo0.es/posts/aislar-agentes-ia-cliente-cluster/</link><pubDate>Tue, 09 Jun 2026 16:00:00 +0200</pubDate><guid>https://blog.lo0.es/posts/aislar-agentes-ia-cliente-cluster/</guid><description>&lt;blockquote>
&lt;p>Primer post de una pareja sobre &lt;strong>aislamiento de agentes de IA&lt;/strong>. Este fija el &lt;em>qué&lt;/em> y el &lt;em>dónde&lt;/em>: el mapa completo de primitivas de aislamiento y a qué dominio pertenece cada una. El &lt;a href="https://blog.lo0.es/posts/runbook-aislar-agentes-ia-bubblewrap-tetragon/">runbook hermano&lt;/a> fija el &lt;em>cómo&lt;/em>, con comandos: &lt;code>ai-jail&lt;/code> y bubblewrap en el cliente, &lt;code>TracingPolicy&lt;/code> de Tetragon y &lt;code>RuntimeClass&lt;/code> en el cluster. Si solo vas a leer uno, este te da el modelo mental; el otro, los ficheros que se copian y pegan.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Un agente de IA que ejecuta código necesita acceso a tu filesystem y a tus herramientas: compilador, linter, &lt;code>grep&lt;/code>, &lt;code>make&lt;/code>, &lt;code>cargo&lt;/code>, &lt;code>npm&lt;/code>. Ese es el mínimo para ser útil. El problema es que junto a ese acceso viaja la capacidad de leer &lt;code>~/.aws/credentials&lt;/code>, exfiltrar tus claves SSH o lanzar un &lt;code>rm -rf&lt;/code> fuera del directorio del proyecto. Y no hace falta un modelo malicioso: basta una dependencia comprometida en un &lt;code>npm install&lt;/code>, porque el agente bienintencionado y el &lt;em>post-install script&lt;/em> envenenado &lt;strong>corren con los mismos permisos&lt;/strong>. La respuesta no es confiar en las buenas intenciones del LLM; es &lt;strong>aislar para acotar el radio de explosión&lt;/strong>. 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 &lt;strong>cliente&lt;/strong> (el workstation del desarrollador: bubblewrap, &lt;code>ai-jail&lt;/code>, sandbox-exec, Landlock, los sandboxes nativos de Claude Code y Codex) y lo que aplica en el &lt;strong>cluster&lt;/strong> (donde el agente o la inferencia corren en Kubernetes: namespaces+seccomp, gVisor, microVMs Firecracker/Kata y &lt;strong>eBPF/Tetragon&lt;/strong> 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.&lt;/p>
&lt;h2 id="la-analogía-el-contratista-con-la-llave-maestra">La analogía: el contratista con la llave maestra&lt;/h2>
&lt;p>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.&lt;/p>
&lt;p>Nadie sensato le da la &lt;strong>llave maestra&lt;/strong> 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.&lt;/p>
&lt;p>Un agente de IA es ese contratista. El sandbox es la política de llaves: &lt;strong>le das la habitación del proyecto y las herramientas, no la llave maestra del sistema.&lt;/strong> Y aquí está el giro que justifica dos posts: el operario trabaja en dos edificios distintos. Uno es &lt;strong>tu piso&lt;/strong> —el workstation del desarrollador, con tus credenciales, tu &lt;code>~/.ssh&lt;/code>, el baúl de contraseñas del navegador—. El otro es &lt;strong>el centro de datos&lt;/strong> —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 (&lt;code>bubblewrap&lt;/code>). 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 &lt;strong>extrapolar la tecnología&lt;/strong>, no copiarla.&lt;/p>
&lt;h2 id="el-modelo-de-amenaza-qué-puede-hacer-un-agente-desbocado">El modelo de amenaza: qué puede hacer un agente desbocado&lt;/h2>
&lt;p>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— &lt;strong>ninguna capa las cubre todas&lt;/strong>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Amenaza&lt;/th>
&lt;th>Qué hace el agente&lt;/th>
&lt;th>Capa mínima que la corta&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>Filesystem fuera de alcance&lt;/strong>&lt;/td>
&lt;td>Lee &lt;code>.env&lt;/code>, &lt;code>~/.ssh/id_rsa&lt;/code>, secretos del sistema; modifica fuentes fuera del proyecto&lt;/td>
&lt;td>Sandbox de proceso (allowlist de rutas)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Egress de red arbitrario&lt;/strong>&lt;/td>
&lt;td>Exfiltra datos, recibe instrucciones de un C2 remoto, llama APIs sin autorizar&lt;/td>
&lt;td>Bloqueo de red / NetworkPolicy / microVM&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Superficie de syscalls del kernel&lt;/strong>&lt;/td>
&lt;td>Un exploit del kernel desde el contenedor escala al host (kernel compartido)&lt;/td>
&lt;td>gVisor o microVM (kernel dedicado)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Fuga entre inquilinos&lt;/strong>&lt;/td>
&lt;td>El workload de un cliente lee datos de otro en una plataforma multi-tenant&lt;/td>
&lt;td>microVM (estándar de facto)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Exfiltración de secretos&lt;/strong>&lt;/td>
&lt;td>Saca tokens y variables de entorno vía &lt;code>/proc&lt;/code> o el environment&lt;/td>
&lt;td>&lt;code>--clearenv&lt;/code> / tmpfs de &lt;code>$HOME&lt;/code> / secretos fuera del pod&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Hay una sexta amenaza que &lt;strong>ningún sandbox resuelve&lt;/strong>: el &lt;em>prompt injection&lt;/em>. 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 &lt;strong>encoge el radio de impacto&lt;/strong> 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: &lt;em>el aislamiento no hace al agente confiable; acota lo que un agente no confiable puede romper.&lt;/em>&lt;/p>
&lt;h2 id="dos-dominios-una-política">Dos dominios, una política&lt;/h2>
&lt;p>El operario trabaja en dos edificios. El reparto de herramientas se ve mejor a dos columnas:&lt;/p>
&lt;div class="diagram" style="max-width:820px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 820 430" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Mapa a dos columnas: dominio cliente (workstation) y dominio cluster, con sus capas de aislamiento y el puente de política común">
&lt;defs>&lt;marker id="am" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">&lt;path d="M0,0 L10,5 L0,10 z" fill="#666"/>&lt;/marker>&lt;/defs>
&lt;text x="410" y="26" text-anchor="middle" font-family="sans-serif" font-size="14" font-weight="700" fill="currentColor">Misma política de mínimo privilegio · distinta primitiva&lt;/text>
&lt;!-- columna cliente -->
&lt;rect x="24" y="46" width="360" height="350" rx="10" fill="#eef4fb" stroke="#1f5fa8" stroke-width="1.6"/>
&lt;text x="204" y="72" text-anchor="middle" font-family="sans-serif" font-size="14" font-weight="700" fill="#0d3a66">CLIENTE · workstation del dev&lt;/text>
&lt;text x="204" y="90" text-anchor="middle" font-family="sans-serif" font-size="11" fill="#0d3a66">un proceso, un usuario, datos personales&lt;/text>
&lt;rect x="48" y="106" width="312" height="50" rx="6" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.2"/>
&lt;text x="204" y="126" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="600" fill="#0d3a66">Sandbox de proceso&lt;/text>
&lt;text x="204" y="143" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#0d3a66">bubblewrap (Linux) · sandbox-exec (macOS)&lt;/text>
&lt;rect x="48" y="166" width="312" height="42" rx="6" fill="#dff0ff" stroke="#1f5fa8" stroke-width="1.1"/>
&lt;text x="204" y="183" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="600" fill="#0d3a66">Landlock (LSM, 2ª barrera VFS)&lt;/text>
&lt;text x="204" y="199" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#0d3a66">defensa en profundidad, kernel ≥5.13&lt;/text>
&lt;rect x="48" y="218" width="312" height="42" rx="6" fill="#eaf4ff" stroke="#1f5fa8" stroke-width="1.1"/>
&lt;text x="204" y="235" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="600" fill="#0d3a66">Dev container (opcional)&lt;/text>
&lt;text x="204" y="251" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#0d3a66">reproducibilidad + reset, kernel compartido&lt;/text>
&lt;text x="204" y="284" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#0d3a66">Lo que lo envuelve:&lt;/text>
&lt;text x="204" y="304" text-anchor="middle" font-family="sans-serif" font-size="10.5" fill="#0d3a66">ai-jail · Claude Code /sandbox&lt;/text>
&lt;text x="204" y="320" text-anchor="middle" font-family="sans-serif" font-size="10.5" fill="#0d3a66">Codex --sandbox · Cursor /worktree&lt;/text>
&lt;text x="204" y="352" text-anchor="middle" font-family="sans-serif" font-size="10.5" fill="#0d3a66">Red de seguridad: git remoto sin push&lt;/text>
&lt;text x="204" y="376" text-anchor="middle" font-family="sans-serif" font-size="10.5" font-style="italic" fill="#555">arranque ~0 ms · sin daemon&lt;/text>
&lt;!-- columna cluster -->
&lt;rect x="436" y="46" width="360" height="350" rx="10" fill="#f3eefb" stroke="#5a2db0" stroke-width="1.6"/>
&lt;text x="616" y="72" text-anchor="middle" font-family="sans-serif" font-size="14" font-weight="700" fill="#42208a">CLUSTER · inferencia / agentes en prod&lt;/text>
&lt;text x="616" y="90" text-anchor="middle" font-family="sans-serif" font-size="11" fill="#42208a">multi-pod, multi-tenant, RKE2&lt;/text>
&lt;rect x="460" y="106" width="312" height="50" rx="6" fill="#e6d9f2" stroke="#5a2db0" stroke-width="1.2"/>
&lt;text x="616" y="126" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="600" fill="#42208a">Baseline de pod&lt;/text>
&lt;text x="616" y="143" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#42208a">namespaces + seccomp + cgroups + NetworkPolicy&lt;/text>
&lt;rect x="460" y="166" width="312" height="42" rx="6" fill="#ede1f7" stroke="#5a2db0" stroke-width="1.1"/>
&lt;text x="616" y="183" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="600" fill="#42208a">gVisor (runsc) — kernel en user-space&lt;/text>
&lt;text x="616" y="199" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#42208a">reduce superficie de syscalls al host&lt;/text>
&lt;rect x="460" y="218" width="312" height="42" rx="6" fill="#f0e6fb" stroke="#5a2db0" stroke-width="1.1"/>
&lt;text x="616" y="235" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="600" fill="#42208a">microVM Firecracker / Kata — kernel propio&lt;/text>
&lt;text x="616" y="251" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#42208a">aislamiento por construcción para no confiable&lt;/text>
&lt;rect x="460" y="270" width="312" height="62" rx="6" fill="#fde9d6" stroke="#a85a00" stroke-width="1.6"/>
&lt;text x="616" y="290" text-anchor="middle" font-family="sans-serif" font-size="12" font-weight="700" fill="#8a4a00">eBPF / Tetragon (lo que ya tenemos)&lt;/text>
&lt;text x="616" y="307" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#8a4a00">observa cada exec · connect · open en kernel&lt;/text>
&lt;text x="616" y="322" text-anchor="middle" font-family="sans-serif" font-size="10" fill="#8a4a00">y mata (Sigkill) lo que se salga del guion&lt;/text>
&lt;text x="616" y="356" text-anchor="middle" font-family="sans-serif" font-size="10.5" fill="#42208a">Red de seguridad: GitOps + revisión de PR&lt;/text>
&lt;text x="616" y="376" text-anchor="middle" font-family="sans-serif" font-size="10.5" font-style="italic" fill="#555">arranque &amp;lt;1 s (microVM) · capa runtime siempre&lt;/text>
&lt;!-- puente -->
&lt;path d="M384,221 L436,221" stroke="#666" stroke-width="1.6" fill="none" marker-end="url(#am)"/>
&lt;path d="M436,241 L384,241" stroke="#666" stroke-width="1.6" fill="none" marker-end="url(#am)"/>
&lt;text x="410" y="415" text-anchor="middle" font-family="sans-serif" font-size="11" font-style="italic" fill="#555">extrapolar: el control del cliente tiene su análogo en el cluster (tabla de equivalencias en el runbook)&lt;/text>
&lt;/svg>
&lt;/div>
&lt;h2 id="el-cliente-aislar-al-agente-en-el-workstation">El cliente: aislar al agente en el workstation&lt;/h2>
&lt;p>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: &lt;code>~/.aws/credentials&lt;/code>, &lt;code>~/.ssh&lt;/code>, &lt;code>~/.gnupg&lt;/code>, el almacén de contraseñas del navegador. El tier que corresponde es el más ligero: el &lt;strong>sandbox de proceso&lt;/strong>.&lt;/p>
&lt;p>&lt;strong>bubblewrap (Linux) y sandbox-exec (macOS).&lt;/strong> &lt;code>bubblewrap&lt;/code> (&lt;code>bwrap&lt;/code>) 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 &lt;strong>sin root&lt;/strong> vía &lt;code>CLONE_NEWUSER&lt;/code>, creando namespaces sin privilegios elevados. Monta &lt;code>$HOME&lt;/code> 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 &lt;code>sandbox-exec&lt;/code> 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 &lt;code>sandbox-exec&lt;/code> no los restringe—, pero ambas protegen lo que importa: el acceso a las zonas sensibles del filesystem.&lt;/p>
&lt;p>&lt;strong>Landlock como segunda barrera.&lt;/strong> &lt;code>bubblewrap&lt;/code> aísla por &lt;em>namespaces y montajes&lt;/em>; Landlock —un Linux Security Module disponible desde el kernel 5.13— restringe el acceso a nivel &lt;strong>VFS&lt;/strong>, independiente de los namespaces. No reemplaza a &lt;code>bwrap&lt;/code>: lo complementa. Cierra vectores que el aislamiento por montaje no cubre por sí solo (rutas de escape vía &lt;code>/proc&lt;/code>, 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.&lt;/p>
&lt;p>&lt;strong>Dev containers, cuando hace falta reproducibilidad.&lt;/strong> Un dev container (&lt;code>devcontainer.json&lt;/code>, lo que usan Codespaces y Cursor) es un contenedor Docker con una capa de configuración encima. Da aislamiento de filesystem razonable y &lt;em>reset&lt;/em> fácil (destruir y recrear), pero &lt;strong>comparte el kernel del host&lt;/strong> —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.&lt;/p>
&lt;p>&lt;strong>Lo que envuelve todo esto.&lt;/strong> El script de bash a mano funciona, pero no escala a un equipo. Las herramientas que lo empaquetan:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>ai-jail&lt;/code>&lt;/strong> (Rust, GPL-3.0): envuelve &lt;code>bwrap&lt;/code>/&lt;code>sandbox-exec&lt;/code> con config por proyecto en un fichero &lt;code>.ai-jail&lt;/code> (TOML, &lt;em>commiteable&lt;/em> al repo, de modo que todo el equipo hereda la misma política), auto-detección de GPU/Docker/display, modo &lt;code>--lockdown&lt;/code> (proyecto en read-only, red cortada con &lt;code>--unshare-net&lt;/code>, &lt;code>--clearenv&lt;/code>), &lt;code>--dry-run&lt;/code> para auditar, y &lt;code>--bootstrap&lt;/code> para 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.&lt;/li>
&lt;li>&lt;strong>El &lt;code>/sandbox&lt;/code> de Claude Code&lt;/strong>: desde octubre de 2025, Claude Code trae sandbox propio que usa —exactamente— &lt;code>bubblewrap&lt;/code> en Linux y &lt;code>sandbox-exec&lt;/code> en macOS. Su &lt;em>Sandboxed Bash&lt;/em> aísla los comandos de shell, pero &lt;strong>no&lt;/strong> las herramientas de fichero, los servidores MCP ni los hooks, que corren con los permisos completos del proceso salvo que actives el paquete beta &lt;code>sandbox-runtime&lt;/code>, que envuelve el proceso entero. Hay un matiz que conviene conocer: si un comando falla por una restricción, el agente puede reintentar con &lt;code>dangerouslyDisableSandbox&lt;/code> —es &lt;em>opt-out&lt;/em>, no &lt;em>opt-in&lt;/em>—.&lt;/li>
&lt;li>&lt;strong>Codex CLI&lt;/strong>: tres modos vía &lt;code>--sandbox&lt;/code> (&lt;code>read-only&lt;/code>, &lt;code>workspace-write&lt;/code>, &lt;code>danger-full-access&lt;/code>); el recomendado por defecto es &lt;code>workspace-write&lt;/code>. La filosofía es deliberada: Codex no provee el aislamiento, lo delega al entorno que lo envuelve. &lt;code>danger-full-access&lt;/code> solo tiene sentido &lt;strong>dentro&lt;/strong> de una microVM.&lt;/li>
&lt;li>&lt;strong>Cursor&lt;/strong>: sus cloud agents corren en VMs aisladas; &lt;code>/worktree&lt;/code> crea un worktree aislado de un solo uso por tarea, y &lt;code>/best-of-n&lt;/code> lanza varios intentos en paralelo en worktrees separados.&lt;/li>
&lt;/ul>
&lt;h2 id="el-cluster-aislar-al-agente-en-producción">El cluster: aislar al agente en producción&lt;/h2>
&lt;p>El segundo edificio es el centro de datos. Aquí el &amp;ldquo;agente&amp;rdquo; 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.&lt;/p>
&lt;p>&lt;strong>El baseline del pod.&lt;/strong> Antes de nada, lo de serie: namespaces de Linux, &lt;code>seccomp&lt;/code> (&lt;code>RuntimeDefault&lt;/code>) para recortar la superficie de syscalls, cgroups para los límites de recursos, &lt;code>securityContext&lt;/code> sin privilegios (&lt;code>runAsNonRoot&lt;/code>, &lt;code>readOnlyRootFilesystem&lt;/code>, &lt;em>drop&lt;/em> de todas las capabilities) y &lt;strong>NetworkPolicy&lt;/strong> 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.&lt;/p>
&lt;p>&lt;strong>gVisor (&lt;code>runsc&lt;/code>).&lt;/strong> 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 &lt;em>Sentry&lt;/em>). 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.&lt;/p>
&lt;p>&lt;strong>microVMs Firecracker / Kata.&lt;/strong> 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 &lt;strong>kernel Linux dedicado&lt;/strong>: un exploit de kernel dentro de la microVM no alcanza al host &lt;em>por construcción&lt;/em>. Es lo que hay debajo de Vercel Sandbox (GA enero 2026) y E2B. En Kubernetes, &lt;strong>Kata Containers&lt;/strong> trae ese modelo a un &lt;code>RuntimeClass&lt;/code>: marcas el pod del agente no confiable con &lt;code>runtimeClassName: kata&lt;/code> 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.&lt;/p>
&lt;p>&lt;strong>eBPF / Tetragon: la capa que ya tenemos.&lt;/strong> 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 &lt;em>estáticas&lt;/em>: definen lo que el pod puede hacer antes de arrancar. &lt;strong>Tetragon&lt;/strong> —el componente de seguridad runtime de Cilium, basado en eBPF— es &lt;em>dinámico&lt;/em>: observa, en el kernel y con coste mínimo, &lt;strong>cada&lt;/strong> ejecución de proceso, &lt;strong>cada&lt;/strong> conexión de red y &lt;strong>cada&lt;/strong> apertura de fichero de cada pod, y puede actuar en línea. No reemplaza al sandbox; lo vigila desde dentro del kernel. Donde &lt;code>bubblewrap&lt;/code> en el cliente bloquea &lt;code>curl&lt;/code> con una blocklist de comandos, Tetragon en el cluster engancha &lt;code>tcp_connect&lt;/code> en el kernel y, si el destino no está permitido, &lt;strong>mata el proceso con &lt;code>Sigkill&lt;/code>&lt;/strong> antes de que el paquete salga. Donde el cliente esconde &lt;code>~/.ssh&lt;/code> tras un tmpfs, Tetragon engancha &lt;code>security_file_open&lt;/code> 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 (&lt;code>op.mon&lt;/code>, &lt;code>op.exp&lt;/code>) sin instrumentar la aplicación: la visibilidad vive en el kernel, no en el código del agente.&lt;/p>
&lt;h2 id="la-tabla-del-panorama">La tabla del panorama&lt;/h2>
&lt;p>Las cinco familias, su fortaleza relativa de aislamiento, su coste de arranque y el dominio donde viven:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Tier&lt;/th>
&lt;th>Primitiva&lt;/th>
&lt;th style="text-align:center">Aislamiento&lt;/th>
&lt;th style="text-align:center">Arranque&lt;/th>
&lt;th>Dominio natural&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Sandbox de proceso&lt;/td>
&lt;td>Seatbelt · bubblewrap&lt;/td>
&lt;td style="text-align:center">Baseline&lt;/td>
&lt;td style="text-align:center">~0 ms&lt;/td>
&lt;td>&lt;strong>Cliente&lt;/strong> (defecto de Claude Code)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Dev container&lt;/td>
&lt;td>Docker + seccomp&lt;/td>
&lt;td style="text-align:center">Moderado&lt;/td>
&lt;td style="text-align:center">segundos&lt;/td>
&lt;td>Cliente / cluster (repetibilidad)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Kernel user-space&lt;/td>
&lt;td>gVisor (&lt;code>runsc&lt;/code>)&lt;/td>
&lt;td style="text-align:center">Fuerte&lt;/td>
&lt;td style="text-align:center">ms&lt;/td>
&lt;td>&lt;strong>Cluster&lt;/strong> (multi-tenant medio)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>microVM&lt;/td>
&lt;td>Firecracker · Kata&lt;/td>
&lt;td style="text-align:center">El más fuerte (práctico)&lt;/td>
&lt;td style="text-align:center">&amp;lt;1 s&lt;/td>
&lt;td>&lt;strong>Cluster&lt;/strong> (código no confiable)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VM completa&lt;/td>
&lt;td>KVM · EC2&lt;/td>
&lt;td style="text-align:center">Máximo&lt;/td>
&lt;td style="text-align:center">30 s+&lt;/td>
&lt;td>Cluster (frontera externa, compliance)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;em>Runtime enforcement&lt;/em>&lt;/td>
&lt;td>&lt;strong>eBPF · Tetragon&lt;/strong>&lt;/td>
&lt;td style="text-align:center">&lt;em>Transversal&lt;/em>&lt;/td>
&lt;td style="text-align:center">&lt;em>siempre activo&lt;/em>&lt;/td>
&lt;td>&lt;strong>Cluster&lt;/strong> (observa+mata sobre cualquier tier)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Tetragon ocupa una fila aparte a propósito: no es un tier en la escalera, es una &lt;strong>capa transversal&lt;/strong> que opera &lt;em>sobre&lt;/em> cualquiera de los otros. Se apila con todos.&lt;/p>
&lt;p>Un apunte numérico sobre por qué la columna &amp;ldquo;arranque&amp;rdquo; decide tanto como la columna &amp;ldquo;aislamiento&amp;rdquo;. Una VM completa gana en aislamiento bruto pero tarda decenas de segundos en provisionarse; para un agente que necesita un entorno fresco &lt;em>por petición o por sesión&lt;/em>, ese coste es prohibitivo. Una microVM Firecracker arranca en &lt;strong>menos de 1 segundo&lt;/strong> y un sandbox de proceso en &lt;strong>~0 ms&lt;/strong>. Por eso el patrón dominante en 2026 no es &amp;ldquo;la VM más aislada&amp;rdquo;, sino &lt;strong>VM completa como frontera externa + microVM como unidad de ejecución por petición dentro&lt;/strong> —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.&lt;/p>
&lt;h2 id="extrapolar-no-copiar">Extrapolar, no copiar&lt;/h2>
&lt;p>La tesis de la pareja de posts cabe en una frase: &lt;strong>el modelo de amenaza es invariante entre dominios; la primitiva que lo implementa, no.&lt;/strong> 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:&lt;/p>
&lt;ul>
&lt;li>&lt;code>$HOME&lt;/code> como tmpfs efímero (cliente) ↔ &lt;code>readOnlyRootFilesystem&lt;/code> + &lt;code>emptyDir&lt;/code> (cluster).&lt;/li>
&lt;li>Blocklist de &lt;code>curl&lt;/code>/&lt;code>wget&lt;/code> en &lt;code>bwrap&lt;/code> (cliente) ↔ &lt;code>TracingPolicy&lt;/code> sobre &lt;code>tcp_connect&lt;/code> en Tetragon + NetworkPolicy (cluster).&lt;/li>
&lt;li>&lt;code>--unshare-net&lt;/code> en lockdown (cliente) ↔ NetworkPolicy default-deny (cluster).&lt;/li>
&lt;li>Sin &lt;em>escape hatch&lt;/em>, el proceso vive dentro de &lt;code>bwrap&lt;/code> (cliente) ↔ sin &lt;code>privileged&lt;/code>, sin &lt;code>hostPath&lt;/code>, &lt;code>RuntimeClass&lt;/code> kata (cluster).&lt;/li>
&lt;li>&lt;code>~/.ssh&lt;/code> y &lt;code>~/.aws&lt;/code> nunca montados (cliente) ↔ secretos fuera del pod + Tetragon vigilando &lt;code>security_file_open&lt;/code> (cluster).&lt;/li>
&lt;/ul>
&lt;p>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 &lt;code>/sandbox&lt;/code> de Claude Code en un portátil o un microVM en un PaaS—, la pregunta útil no es &amp;ldquo;¿qué herramienta usa?&amp;rdquo;, sino &amp;ldquo;¿cuál de los cinco vectores cierra, y cuál deja abierto?&amp;rdquo;. La herramienta se sustituye; el mapa de amenazas se queda.&lt;/p>
&lt;h2 id="lo-que-ningún-sandbox-resuelve">Lo que ningún sandbox resuelve&lt;/h2>
&lt;p>Tres límites que la propia documentación de Anthropic enuncia, y que conviene tener delante para no vender humo:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>El egress sigue siendo un riesgo&lt;/strong> en cualquier sandbox que permita conexiones salientes. Si el agente puede abrir una conexión, puede exfiltrar. Por eso el lockdown del cliente &lt;em>corta&lt;/em> la red y el cluster usa NetworkPolicy default-deny + Tetragon: no se confía en &amp;ldquo;filtrar bien&amp;rdquo;, se confía en &amp;ldquo;no dejar salir&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>La modificación de código sigue siendo posible&lt;/strong> en cualquier sandbox con el directorio del proyecto montado en escritura. El remedio no es técnico-de-sandbox, es &lt;strong>git&lt;/strong>: con el remoto intacto y sin permiso de &lt;code>push&lt;/code>, el peor caso es corromper el working copy local —&lt;code>git checkout .&lt;/code> y a empezar—. El daño no llega al remoto.&lt;/li>
&lt;li>&lt;strong>Ningún sandbox impide que un prompt comprometido llegue a la API.&lt;/strong> 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 &lt;em>junto&lt;/em> al aislamiento, no en su lugar.&lt;/li>
&lt;/ol>
&lt;p>La conclusión operativa: &lt;strong>el aislamiento encoge el radio de explosión; la defensa en profundidad es lo que cierra el círculo.&lt;/strong> 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.&lt;/p>
&lt;h2 id="ver-también">Ver también&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/posts/runbook-aislar-agentes-ia-bubblewrap-tetragon/">Runbook: enjaular al agente de IA — bubblewrap en el cliente, Tetragon en el cluster&lt;/a> — el compañero operativo de este post: los ficheros &lt;code>.ai-jail&lt;/code>, el &lt;code>--bootstrap&lt;/code> de permisos y las &lt;code>TracingPolicy&lt;/code> de Tetragon que se copian y pegan. Con comandos.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/cilium-ebpf-dranet-numa-de-red-inferencia/">La puerta de la cocina que el maître no miró: NUMA de red, Cilium eBPF y DRANET&lt;/a> — el datapath eBPF de Cilium sobre el que se apoya Tetragon en el cluster; la misma capa de kernel, otro uso.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/guardrails-safety-llm/">Guardrails y safety en LLM&lt;/a> — la mitigación en el plano del &lt;em>contenido&lt;/em> (qué dice y qué se le dice al modelo); este post es la mitigación en el plano de la &lt;em>ejecución&lt;/em> (qué puede hacer el proceso del agente).&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/controles-tecnicos-ens-42001-eu-ai-act/">Controles técnicos: ENS × ISO 42001 × EU AI Act&lt;/a> — el marco de cumplimiento que el aislamiento de runtime materializa: Tetragon como evidencia técnica de &lt;code>op.mon&lt;/code>/&lt;code>op.exp&lt;/code>.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/catalogo-herramientas-oss-llmops/">Catálogo de herramientas OSS para LLMOps&lt;/a> — dónde encaja la capa de seguridad runtime en el stack abierto completo.&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>bubblewrap: &lt;a href="https://github.com/containers/bubblewrap">https://github.com/containers/bubblewrap&lt;/a>&lt;/li>
&lt;li>Landlock LSM: &lt;a href="https://landlock.io">https://landlock.io&lt;/a> · &lt;a href="https://docs.rs/landlock">https://docs.rs/landlock&lt;/a>&lt;/li>
&lt;li>ai-jail (Fabio Akita): &lt;a href="https://github.com/akitaonrails/ai-jail">https://github.com/akitaonrails/ai-jail&lt;/a>&lt;/li>
&lt;li>gVisor: &lt;a href="https://gvisor.dev">https://gvisor.dev&lt;/a>&lt;/li>
&lt;li>Firecracker: &lt;a href="https://firecracker-microvm.github.io">https://firecracker-microvm.github.io&lt;/a>&lt;/li>
&lt;li>Kata Containers: &lt;a href="https://katacontainers.io">https://katacontainers.io&lt;/a>&lt;/li>
&lt;li>Tetragon (Cilium): &lt;a href="https://tetragon.io">https://tetragon.io&lt;/a>&lt;/li>
&lt;li>Vercel Sandbox — concepts: &lt;a href="https://vercel.com/docs/vercel-sandbox/concepts">https://vercel.com/docs/vercel-sandbox/concepts&lt;/a>&lt;/li>
&lt;li>E2B: &lt;a href="https://github.com/e2b-dev/E2B">https://github.com/e2b-dev/E2B&lt;/a>&lt;/li>
&lt;li>Claude Code sandboxing: &lt;a href="https://docs.claude.com/en/docs/claude-code">https://docs.claude.com/en/docs/claude-code&lt;/a>&lt;/li>
&lt;li>Codex CLI: &lt;a href="https://github.com/openai/codex">https://github.com/openai/codex&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>