<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Seguridad on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/seguridad/</link><description>Recent content in Seguridad on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Tue, 09 Jun 2026 17:00:00 +0200</lastBuildDate><atom:link href="https://blog.lo0.es/tags/seguridad/index.xml" rel="self" type="application/rss+xml"/><item><title>Runbook: enjaular al agente de IA — bubblewrap en el cliente, Tetragon en el cluster</title><link>https://blog.lo0.es/posts/runbook-aislar-agentes-ia-bubblewrap-tetragon/</link><pubDate>Tue, 09 Jun 2026 17:00:00 +0200</pubDate><guid>https://blog.lo0.es/posts/runbook-aislar-agentes-ia-bubblewrap-tetragon/</guid><description>&lt;blockquote>
&lt;p>Compañero &lt;strong>operativo&lt;/strong> de &lt;a href="https://blog.lo0.es/posts/aislar-agentes-ia-cliente-cluster/">El contratista con la llave maestra&lt;/a>. Aquel post explica el &lt;em>porqué&lt;/em> y el &lt;em>dónde&lt;/em> —el modelo de amenaza, las cinco familias de aislamiento, qué dominio usa cada una—; este es el &lt;em>cómo&lt;/em>, con comandos. Si no lo has leído, léelo antes: aquí doy por sabido qué es el radio de explosión, por qué &lt;code>bwrap&lt;/code> corre sin root y qué vigila Tetragon. El procedimiento va en dos tracks independientes —&lt;strong>cliente&lt;/strong> y &lt;strong>cluster&lt;/strong>— porque, como argumenta el post hermano, el control se extrapola pero la primitiva se reescribe.&lt;/p>
&lt;/blockquote>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Dos procedimientos reproducibles. &lt;strong>Cliente (workstation):&lt;/strong> instala &lt;code>ai-jail&lt;/code> (envuelve &lt;code>bubblewrap&lt;/code>), genera el &lt;code>.ai-jail&lt;/code> por proyecto, audita con &lt;code>--dry-run&lt;/code>, fija las allowlists con &lt;code>--bootstrap&lt;/code>, usa &lt;code>--lockdown&lt;/code> para lo que no te fíes, y deja al agente sin permiso de &lt;code>git push&lt;/code>. &lt;strong>Cluster (RKE2 con Cilium + Tetragon):&lt;/strong> pon el baseline de pod (&lt;code>securityContext&lt;/code> sin privilegios, &lt;code>seccomp: RuntimeDefault&lt;/code>, &lt;code>NetworkPolicy&lt;/code> default-deny), mete el pod del agente no confiable en una microVM con &lt;code>runtimeClassName: kata&lt;/code>, y despliega las &lt;code>TracingPolicy&lt;/code> de Tetragon en &lt;strong>dos fases&lt;/strong> —observar con &lt;code>action: Post&lt;/code> para levantar el baseline, luego promover a &lt;code>action: Sigkill&lt;/code> sobre &lt;code>tcp_connect&lt;/code> (egress) y &lt;code>security_file_open&lt;/code> (rutas de secretos)—. La regla de oro de la fase Tetragon: &lt;strong>adopta primero, bloquea después&lt;/strong>; nunca metas un &lt;code>Sigkill&lt;/code> en producción sin haber visto antes los eventos en modo observación.&lt;/p>
&lt;h2 id="el-flujo-de-los-dos-tracks">El flujo de los dos tracks&lt;/h2>
&lt;div class="diagram" style="max-width:800px;margin:1.5rem auto;">
&lt;svg viewBox="0 0 800 250" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Dos tracks operativos: cliente (instalar, configurar, bootstrap, lockdown) y cluster (baseline, RuntimeClass, observar, enforce)">
&lt;defs>&lt;marker id="rm2" 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="400" y="22" text-anchor="middle" font-family="sans-serif" font-size="13" font-weight="700" fill="currentColor">Track CLIENTE — workstation&lt;/text>
&lt;rect x="20" y="36" width="150" height="46" rx="7" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.4"/>
&lt;text x="95" y="56" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#0d3a66">1 · Instalar&lt;/text>
&lt;text x="95" y="72" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#0d3a66">ai-jail + bwrap&lt;/text>
&lt;rect x="200" y="36" width="150" height="46" rx="7" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.4"/>
&lt;text x="275" y="56" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#0d3a66">2 · .ai-jail&lt;/text>
&lt;text x="275" y="72" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#0d3a66">--dry-run&lt;/text>
&lt;rect x="380" y="36" width="150" height="46" rx="7" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.4"/>
&lt;text x="455" y="56" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#0d3a66">3 · --bootstrap&lt;/text>
&lt;text x="455" y="72" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#0d3a66">allow/deny/ask&lt;/text>
&lt;rect x="560" y="36" width="150" height="46" rx="7" fill="#d4ecff" stroke="#1f5fa8" stroke-width="1.4"/>
&lt;text x="635" y="52" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#0d3a66">4 · lockdown&lt;/text>
&lt;text x="635" y="68" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#0d3a66">+ git sin push&lt;/text>
&lt;path d="M170,59 L198,59" stroke="#666" stroke-width="1.5" fill="none" marker-end="url(#rm2)"/>
&lt;path d="M350,59 L378,59" stroke="#666" stroke-width="1.5" fill="none" marker-end="url(#rm2)"/>
&lt;path d="M530,59 L558,59" stroke="#666" stroke-width="1.5" fill="none" marker-end="url(#rm2)"/>
&lt;line x1="20" y1="118" x2="780" y2="118" stroke="#ccc" stroke-width="1" stroke-dasharray="3 3"/>
&lt;text x="400" y="150" text-anchor="middle" font-family="sans-serif" font-size="13" font-weight="700" fill="currentColor">Track CLUSTER — RKE2 + Cilium/Tetragon&lt;/text>
&lt;rect x="20" y="164" width="150" height="46" rx="7" fill="#e6d9f2" stroke="#5a2db0" stroke-width="1.4"/>
&lt;text x="95" y="184" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#42208a">1 · Baseline&lt;/text>
&lt;text x="95" y="200" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#42208a">secCtx+NetPol&lt;/text>
&lt;rect x="200" y="164" width="150" height="46" rx="7" fill="#e6d9f2" stroke="#5a2db0" stroke-width="1.4"/>
&lt;text x="275" y="184" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#42208a">2 · RuntimeClass&lt;/text>
&lt;text x="275" y="200" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#42208a">kata microVM&lt;/text>
&lt;rect x="380" y="164" width="150" height="46" rx="7" fill="#fde9d6" stroke="#a85a00" stroke-width="1.6"/>
&lt;text x="455" y="184" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#8a4a00">3 · Observar&lt;/text>
&lt;text x="455" y="200" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#8a4a00">Tetragon · Post&lt;/text>
&lt;rect x="560" y="164" width="150" height="46" rx="7" fill="#fbd4b8" stroke="#a85a00" stroke-width="1.8"/>
&lt;text x="635" y="184" text-anchor="middle" font-family="sans-serif" font-size="11" font-weight="600" fill="#8a4a00">4 · Enforce&lt;/text>
&lt;text x="635" y="200" text-anchor="middle" font-family="sans-serif" font-size="9.5" fill="#8a4a00">Tetragon · Sigkill&lt;/text>
&lt;path d="M170,187 L198,187" stroke="#666" stroke-width="1.5" fill="none" marker-end="url(#rm2)"/>
&lt;path d="M350,187 L378,187" stroke="#666" stroke-width="1.5" fill="none" marker-end="url(#rm2)"/>
&lt;path d="M530,187 L558,187" stroke="#666" stroke-width="1.5" fill="none" marker-end="url(#rm2)"/>
&lt;text x="400" y="236" text-anchor="middle" font-family="sans-serif" font-size="10" font-style="italic" fill="#555">adopta primero (observar), bloquea después (enforce)&lt;/text>
&lt;/svg>
&lt;/div>
&lt;hr>
&lt;h1 id="track-a--cliente-workstation-del-desarrollador">Track A — Cliente (workstation del desarrollador)&lt;/h1>
&lt;h2 id="a0--instalar-ai-jail-y-bubblewrap">A0 — Instalar ai-jail y bubblewrap&lt;/h2>
&lt;p>&lt;code>ai-jail&lt;/code> envuelve el sandbox; en Linux necesita &lt;code>bubblewrap&lt;/code> aparte, en macOS no necesita dependencia extra.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ai-jail (macOS y Linux)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">brew tap akitaonrails/tap &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> brew install ai-jail
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># o, con cargo:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cargo install ai-jail
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># o, con mise:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">mise use -g ubi:akitaonrails/ai-jail
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># bubblewrap en Linux (elige tu distro)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo pacman -S bubblewrap &lt;span class="c1"># Arch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo apt install bubblewrap &lt;span class="c1"># Debian / Ubuntu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">sudo dnf install bubblewrap &lt;span class="c1"># Fedora&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Comprueba que el binario está y que &lt;code>bwrap&lt;/code> corre sin root (no debe pedir &lt;code>sudo&lt;/code>):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ai-jail --version
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bwrap --ro-bind / / --unshare-all &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;bwrap ok sin root&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Si &lt;code>bwrap&lt;/code> falla pidiendo privilegios, tu kernel tiene los &lt;em>unprivileged user namespaces&lt;/em> deshabilitados; habilítalos (&lt;code>sysctl kernel.unprivileged_userns_clone=1&lt;/code> en Debian/Ubuntu antiguos) antes de seguir.&lt;/p>
&lt;h2 id="a1--el-fichero-ai-jail-por-proyecto">A1 — El fichero .ai-jail por proyecto&lt;/h2>
&lt;p>En el primer arranque dentro del proyecto, &lt;code>ai-jail&lt;/code> crea un &lt;code>.ai-jail&lt;/code> (TOML) &lt;strong>commiteable al repo&lt;/strong>: cualquier compañero que clone hereda la misma política.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> ~/Projects/mi-app
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ai-jail claude &lt;span class="c1"># crea .ai-jail y lanza Claude Code dentro del sandbox&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>El fichero generado:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-toml" data-lang="toml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># .ai-jail — configuración del sandbox (commitéalo al repo)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">command&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;claude&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">rw_maps&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[]&lt;/span> &lt;span class="c"># directorios extra con escritura&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">ro_maps&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="p">[]&lt;/span> &lt;span class="c"># directorios extra de solo lectura&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Antes de confiar en el sandbox, audítalo.&lt;/strong> &lt;code>--dry-run --verbose&lt;/code> imprime cada punto de montaje, cada flag de aislamiento y el comando &lt;code>bwrap&lt;/code> completo, sin ejecutar nada:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ai-jail --dry-run --verbose claude
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Lee la salida y confirma tres cosas: que &lt;code>$HOME&lt;/code> se monta como tmpfs (no el real), que &lt;code>~/.ssh&lt;/code>, &lt;code>~/.aws&lt;/code> y &lt;code>~/.gnupg&lt;/code> &lt;strong>no aparecen&lt;/strong> entre los montajes, y que el único directorio con escritura es el del proyecto. Si necesitas un directorio extra:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ai-jail --rw-map ~/Projects/shared-lib claude &lt;span class="c1"># extra con escritura&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ai-jail --map /opt/datasets claude &lt;span class="c1"># extra de solo lectura&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Otros agentes, mismo binario:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ai-jail codex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ai-jail opencode
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ai-jail bash &lt;span class="c1"># shell pelado para depurar el sandbox&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">ai-jail -- python script.py &lt;span class="c1"># cualquier comando&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="a2--las-allowlists-de-permisos-con---bootstrap">A2 — Las allowlists de permisos con &amp;ndash;bootstrap&lt;/h2>
&lt;p>&lt;code>--bootstrap&lt;/code> genera las configuraciones de permisos de cada agente, con allow/deny/ask sensatos, y hace backup antes de sobrescribir:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ai-jail --bootstrap
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Lo que produce, en resumen:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Agente&lt;/th>
&lt;th>Fichero&lt;/th>
&lt;th>Política base&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Claude Code&lt;/td>
&lt;td>&lt;code>~/.claude/settings.json&lt;/code>&lt;/td>
&lt;td>&lt;strong>allow&lt;/strong>: &lt;code>git status/diff/log&lt;/code>, &lt;code>ls&lt;/code>, &lt;code>grep&lt;/code>, &lt;code>cargo&lt;/code>, &lt;code>npm&lt;/code>, &lt;code>python&lt;/code>, &lt;code>docker compose&lt;/code> · &lt;strong>ask&lt;/strong>: &lt;code>git push&lt;/code>, &lt;code>rm&lt;/code>, &lt;code>docker run&lt;/code> · &lt;strong>deny&lt;/strong>: &lt;code>rm -rf&lt;/code>, &lt;code>sudo&lt;/code>, &lt;code>chmod 777&lt;/code>, &lt;code>git push --force&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Codex&lt;/td>
&lt;td>&lt;code>~/.codex/config.toml&lt;/code>&lt;/td>
&lt;td>&lt;code>approval_policy = &amp;quot;on-request&amp;quot;&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>OpenCode&lt;/td>
&lt;td>&lt;code>~/.config/opencode/opencode.json&lt;/code>&lt;/td>
&lt;td>permisos de &lt;code>bash&lt;/code>, &lt;code>edit&lt;/code>, &lt;code>write&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>La clave operativa: &lt;code>git push&lt;/code> está en &lt;strong>ask&lt;/strong>, no en &lt;strong>allow&lt;/strong>, y &lt;code>git push --force&lt;/code> en &lt;strong>deny&lt;/strong>. El agente puede commitear, ramear y rebasar localmente cuanto quiera; nada de eso toca el remoto. (Si usas el &lt;code>/sandbox&lt;/code> de Claude Code, fija además &lt;code>&amp;quot;allowUnsandboxedCommands&amp;quot;: false&lt;/code> para cerrar el &lt;em>escape hatch&lt;/em> &lt;code>dangerouslyDisableSandbox&lt;/code>, que de fábrica es opt-out.)&lt;/p>
&lt;h2 id="a3--lockdown-para-lo-que-no-te-fíes">A3 — Lockdown para lo que no te fíes&lt;/h2>
&lt;p>Para auditar código de terceros o correr un agente sobre un proyecto que no conoces, &lt;code>--lockdown&lt;/code> va más allá: proyecto montado en &lt;strong>read-only&lt;/strong>, GPU/Docker/display deshabilitados, &lt;code>--rw-map&lt;/code>/&lt;code>--map&lt;/code> ignorados, &lt;code>$HOME&lt;/code> tmpfs puro sin dotfiles del host, red cortada con &lt;code>--unshare-net&lt;/code> y environment limpiado con &lt;code>--clearenv&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">ai-jail --lockdown bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Es el sandbox más restrictivo posible sin llegar a una VM. Úsalo como defecto mental para todo lo que no sea tu propio código en tu propia máquina.&lt;/p>
&lt;h2 id="a4--la-red-de-seguridad-git-sin-push">A4 — La red de seguridad: git sin push&lt;/h2>
&lt;p>No es un flag, es una propiedad del entorno que cambia el cálculo de riesgo. Si el proyecto está en git con remoto, y el agente &lt;strong>no&lt;/strong> tiene permiso de &lt;code>push&lt;/code>, el peor caso —que corrompa cada fichero del proyecto— se revierte con:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git checkout . &lt;span class="c1"># vuelve al último commit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># y si tocó .git (improbable): borra el dir y re-clona&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>El remoto nunca se tocó. &lt;strong>Sandbox para el filesystem + git para el código + push manual&lt;/strong> es ya un nivel razonable para uso diario: &lt;code>ai-jail&lt;/code> protege tus datos y el sistema, git protege el código, y la decisión de publicar sigue siendo tuya.&lt;/p>
&lt;hr>
&lt;h1 id="track-b--cluster-rke2-con-cilium--tetragon">Track B — Cluster (RKE2 con Cilium + Tetragon)&lt;/h1>
&lt;p>El agente no confiable —o la inferencia que ejecuta código generado— corre como pod. El mismo principio del cliente, otras primitivas. Asumimos un cluster genérico RKE2 con Cilium como CNI y Tetragon ya desplegado (el &lt;code>DaemonSet&lt;/code> del agente eBPF en cada nodo).&lt;/p>
&lt;h2 id="b0--el-baseline-del-pod">B0 — El baseline del pod&lt;/h2>
&lt;p>Antes de cualquier eBPF, lo de serie. &lt;code>securityContext&lt;/code> sin privilegios, raíz read-only, seccomp por defecto:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Pod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ai-agent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agentes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ai-agent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">securityContext&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runAsNonRoot&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runAsUser&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10001&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">seccompProfile&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">RuntimeDefault&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">containers&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">registry.interno/ai-agent:pinned&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">securityContext&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">allowPrivilegeEscalation&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">readOnlyRootFilesystem&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">capabilities&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">drop&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;ALL&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumeMounts&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- {&lt;span class="w"> &lt;/span>&lt;span class="nt">name: work, mountPath&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">/work } &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># único escribible&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">volumes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">work&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">emptyDir&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>{}&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Y el corte de egress por defecto —el gemelo cluster del &lt;code>--unshare-net&lt;/code>—. NetworkPolicy default-deny de salida en el namespace, abriendo solo DNS y lo imprescindible:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">networking.k8s.io/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">NetworkPolicy&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">default-deny-egress&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agentes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">podSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>{}&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">policyTypes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Egress&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">egress&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">to&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">namespaceSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>{&lt;span class="w"> &lt;/span>&lt;span class="nt">kubernetes.io/metadata.name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kube-system }&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- {&lt;span class="w"> &lt;/span>&lt;span class="nt">protocol: UDP, port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">53&lt;/span>&lt;span class="w"> &lt;/span>}&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- {&lt;span class="w"> &lt;/span>&lt;span class="nt">protocol: TCP, port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">53&lt;/span>&lt;span class="w"> &lt;/span>}&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="b1--runtimeclass-kata-el-pod-no-confiable-en-su-propia-microvm">B1 — RuntimeClass Kata: el pod no confiable en su propia microVM&lt;/h2>
&lt;p>Para código realmente no confiable, sácalo del kernel compartido. Con Kata desplegado existe un &lt;code>RuntimeClass&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">node.k8s.io/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">RuntimeClass&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kata&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">handler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kata&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Y el pod lo pide con una línea —&lt;code>runtimeClassName: kata&lt;/code>—, ejecutándose en su propia microVM con kernel dedicado en lugar de compartir el del nodo:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runtimeClassName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">kata &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># ← el pod corre en una microVM, no en el kernel del nodo&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># ...resto igual que B0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Es el gemelo cluster del aislamiento por construcción: un exploit de kernel dentro del pod no alcanza al nodo.&lt;/p>
&lt;h2 id="b2--tetragon-fase-observación-post">B2 — Tetragon, fase observación (Post)&lt;/h2>
&lt;p>Ahora la capa que distingue una plataforma con visibilidad de runtime. &lt;strong>Primero observar, nunca matar de entrada.&lt;/strong> Una &lt;code>TracingPolicyNamespaced&lt;/code> —scoped al namespace y a la etiqueta del agente— que reporta (no mata) tres cosas: ejecuciones de proceso, conexiones de red y aperturas de rutas sensibles. &lt;code>action: Post&lt;/code> solo emite el evento.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cilium.io/v1alpha1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">TracingPolicyNamespaced&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agente-observa&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agentes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">podSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ai-agent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">kprobes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># --- conexiones salientes ---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">call&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;tcp_connect&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">syscall&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;sock&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">selectors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchActions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">action&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Post&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># --- aperturas de ficheros sensibles ---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">call&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;security_file_open&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">syscall&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;file&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">selectors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchArgs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">operator&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Prefix&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">values&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/var/run/secrets&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/work/.git/config&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchActions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">action&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Post&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(Las ejecuciones de proceso no necesitan kprobe: Tetragon emite &lt;code>process_exec&lt;/code>/&lt;code>process_exit&lt;/code> de forma nativa.) Despliega y observa los eventos en vivo desde el pod de Tetragon del nodo:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">kubectl apply -f agente-observa.yaml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># eventos legibles, filtrando por el namespace:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">kubectl &lt;span class="nb">exec&lt;/span> -n kube-system ds/tetragon -c tetragon -- &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> tetra getevents -o compact --namespace agentes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Deja esto rodando una jornada típica del agente. Apunta a &lt;strong>qué destinos&lt;/strong> conecta de verdad (tu registry interno, tu mirror de HF, tu endpoint de vLLM) y &lt;strong>qué rutas&lt;/strong> abre. Eso es tu baseline: la lista de lo legítimo. Sin este paso, un &lt;code>Sigkill&lt;/code> mata trabajo bueno y te genera un incidente de disponibilidad —justo lo que el ENS te pide evitar—.&lt;/p>
&lt;h2 id="b3--tetragon-fase-enforcement-sigkill">B3 — Tetragon, fase enforcement (Sigkill)&lt;/h2>
&lt;p>Con el baseline en la mano, promueve a bloqueo. Dos reglas. La primera: &lt;strong>mata cualquier conexión cuyo destino no esté en la allowlist&lt;/strong> —&lt;code>NotDAddr&lt;/code> invierte el match: dispara para todo lo que &lt;em>no&lt;/em> sea esas redes—. La segunda: &lt;strong>mata cualquier intento de abrir una ruta de secretos&lt;/strong>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cilium.io/v1alpha1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">TracingPolicyNamespaced&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agente-enforce&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">namespace&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">agentes&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">podSelector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchLabels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ai-agent&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">kprobes&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># --- egress: mata todo lo que NO sea la allowlist ---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">call&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;tcp_connect&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">syscall&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;sock&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">selectors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchArgs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">operator&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;NotDAddr&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">values&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;127.0.0.1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;10.0.0.0/8&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># red interna del cluster&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;172.16.10.20&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># registry interno (ejemplo)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchActions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">action&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Sigkill&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="c"># --- lectura de secretos: mata el proceso ---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">call&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;security_file_open&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">syscall&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">type&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;file&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">selectors&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">matchArgs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">index&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">operator&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Prefix&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">values&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/var/run/secrets/kubernetes.io/serviceaccount/token&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;/work/.ssh&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matchActions&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">action&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Sigkill&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">kubectl apply -f agente-enforce.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Ahora el agente puede hacer lo que quiera dentro del pod, pero &lt;strong>en el instante&lt;/strong> en que intenta conectar a un destino no permitido o leer el token de la service account, Tetragon lo mata en el kernel —antes de que el paquete salga o el &lt;code>read&lt;/code> devuelva bytes—. Es el gemelo cluster de la blocklist de &lt;code>curl&lt;/code> y del &lt;code>~/.ssh&lt;/code> no montado, pero aplicado en runtime y sobre &lt;em>cualquier&lt;/em> binario, no solo los que conoces.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Aviso operativo.&lt;/strong> El enforcement con &lt;code>Sigkill&lt;/code> requiere kernel reciente con soporte de la acción en eBPF (5.10+ es seguro). Despliega &lt;code>agente-enforce&lt;/code> primero en un namespace de pruebas, y mantén &lt;code>agente-observa&lt;/code> activo en paralelo: si el bloqueo dispara, el evento &lt;code>Post&lt;/code> te dice exactamente qué lo provocó. Adopta primero, bloquea después.&lt;/p>
&lt;/blockquote>
&lt;h2 id="la-tabla-de-equivalencias-cliente--cluster">La tabla de equivalencias cliente ↔ cluster&lt;/h2>
&lt;p>El mismo vector, las dos primitivas. Esto es &amp;ldquo;extrapolar la tecnología&amp;rdquo; hecho explícito:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Vector de amenaza&lt;/th>
&lt;th>Cliente (workstation)&lt;/th>
&lt;th>Cluster (RKE2)&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>$HOME&lt;/code> / raíz escribible&lt;/td>
&lt;td>&lt;code>$HOME&lt;/code> como tmpfs efímero (&lt;code>bwrap&lt;/code>)&lt;/td>
&lt;td>&lt;code>readOnlyRootFilesystem: true&lt;/code> + &lt;code>emptyDir&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Egress arbitrario&lt;/td>
&lt;td>blocklist &lt;code>curl&lt;/code>/&lt;code>wget&lt;/code> · &lt;code>--unshare-net&lt;/code>&lt;/td>
&lt;td>NetworkPolicy default-deny + Tetragon &lt;code>NotDAddr&lt;/code>→&lt;code>Sigkill&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Lectura de secretos&lt;/td>
&lt;td>&lt;code>~/.ssh&lt;/code>/&lt;code>~/.aws&lt;/code>/&lt;code>~/.gnupg&lt;/code> no montados&lt;/td>
&lt;td>secretos fuera del pod + Tetragon &lt;code>security_file_open&lt;/code>→&lt;code>Sigkill&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Escape del kernel&lt;/td>
&lt;td>Landlock (2ª barrera VFS)&lt;/td>
&lt;td>&lt;code>runtimeClassName: kata&lt;/code> (microVM, kernel propio)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Sin escape hatch&lt;/td>
&lt;td>proceso dentro de &lt;code>bwrap&lt;/code>, sin salida&lt;/td>
&lt;td>sin &lt;code>privileged&lt;/code>, &lt;code>drop ALL&lt;/code>, &lt;code>allowPrivilegeEscalation:false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Daño al código&lt;/td>
&lt;td>git remoto sin &lt;code>push&lt;/code> → &lt;code>git checkout .&lt;/code>&lt;/td>
&lt;td>GitOps + revisión de PR, el agente no aplica a &lt;code>main&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Visibilidad&lt;/td>
&lt;td>&lt;code>--dry-run --verbose&lt;/code> (estático, pre-run)&lt;/td>
&lt;td>Tetragon &lt;code>tetra getevents&lt;/code> (dinámico, en runtime)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="checklist-de-gotchas">Checklist de gotchas&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>No metas un &lt;code>Sigkill&lt;/code> sin pasar por &lt;code>Post&lt;/code>.&lt;/strong> El baseline de observación no es opcional: es lo que separa &amp;ldquo;bloquear un C2&amp;rdquo; de &amp;ldquo;tirar tu propio job de fine-tuning&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>El &lt;code>.ai-jail&lt;/code> se commitea; los secretos no.&lt;/strong> El TOML es política, no credenciales. Verifica que no metes rutas con datos sensibles en &lt;code>rw_maps&lt;/code>.&lt;/li>
&lt;li>&lt;strong>&lt;code>readOnlyRootFilesystem&lt;/code> rompe apps que escriben en &lt;code>/tmp&lt;/code>.&lt;/strong> Monta un &lt;code>emptyDir&lt;/code> en &lt;code>/tmp&lt;/code> además del de trabajo.&lt;/li>
&lt;li>&lt;strong>NetworkPolicy sin regla de DNS deja al pod ciego.&lt;/strong> Abre el puerto 53 a &lt;code>kube-system&lt;/code> o nada resuelve.&lt;/li>
&lt;li>&lt;strong>Kata no es gratis.&lt;/strong> Añade latencia de arranque y no todo workload con dispositivos especiales (GPU passthrough) encaja; resérvalo para lo no confiable, no para todo.&lt;/li>
&lt;li>&lt;strong>El &lt;code>/sandbox&lt;/code> de Claude Code no cubre MCP ni hooks&lt;/strong> salvo que actives &lt;code>sandbox-runtime&lt;/code>. Si tu agente usa servidores MCP, asume que corren con permisos completos hasta que lo hagas.&lt;/li>
&lt;li>&lt;strong>&lt;code>NotDAddr&lt;/code> con IPs literales envejece mal.&lt;/strong> Documenta la allowlist y revísala cuando cambie el registry o el endpoint de inferencia; considera CIDRs internos estables en vez de IPs sueltas.&lt;/li>
&lt;/ul>
&lt;h2 id="ver-también">Ver también&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://blog.lo0.es/posts/aislar-agentes-ia-cliente-cluster/">El contratista con la llave maestra: aislar agentes de IA del workstation al cluster&lt;/a> — el panorama que este runbook ejecuta: modelo de amenaza, las cinco familias de aislamiento y por qué cliente y cluster usan primitivas distintas.&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ó: Cilium eBPF y DRANET&lt;/a> — la capa eBPF de Cilium sobre la que Tetragon engancha sus kprobes; el datapath que ya tienes en el cluster.&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> — los eventos de Tetragon como evidencia técnica de &lt;code>op.mon&lt;/code>/&lt;code>op.exp&lt;/code>; el enforcement como medida de protección.&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 contenido; este runbook, la del plano de la ejecución.&lt;/li>
&lt;li>&lt;a href="https://blog.lo0.es/posts/siete-fases-despliegue-plataforma-llm-on-premise/">Siete fases de despliegue de una plataforma LLM on-premise&lt;/a> — dónde encaja el endurecimiento de runtime en la secuencia de despliegue (F4 identidad/políticas, F5 plataforma).&lt;/li>
&lt;/ul>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>ai-jail (Fabio Akita), GPL-3.0: &lt;a href="https://github.com/akitaonrails/ai-jail">https://github.com/akitaonrails/ai-jail&lt;/a>&lt;/li>
&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;/li>
&lt;li>Tetragon — TracingPolicy: &lt;a href="https://tetragon.io/docs/concepts/tracing-policy/">https://tetragon.io/docs/concepts/tracing-policy/&lt;/a>&lt;/li>
&lt;li>Tetragon — enforcement (Sigkill/Override): &lt;a href="https://tetragon.io/docs/concepts/enforcement/">https://tetragon.io/docs/concepts/enforcement/&lt;/a>&lt;/li>
&lt;li>Kata Containers — Kubernetes RuntimeClass: &lt;a href="https://katacontainers.io">https://katacontainers.io&lt;/a>&lt;/li>
&lt;li>Kubernetes — Pod Security &amp;amp; seccomp: &lt;a href="https://kubernetes.io/docs/tutorials/security/seccomp/">https://kubernetes.io/docs/tutorials/security/seccomp/&lt;/a>&lt;/li>
&lt;li>Kubernetes — Network Policies: &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/">https://kubernetes.io/docs/concepts/services-networking/network-policies/&lt;/a>&lt;/li>
&lt;li>Cilium: &lt;a href="https://cilium.io">https://cilium.io&lt;/a>&lt;/li>
&lt;/ul></description></item><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>