Tetragon: el primo de seguridad de Cilium que ve cada syscall en el kernel
TL;DR
Tetragon es el motor de seguridad y observabilidad de runtime que el proyecto Cilium publicó como complemento al CNI. Su trabajo no es enrutar paquetes —para eso ya está Cilium— sino observar lo que pasa dentro de los procesos del nodo en tiempo real: qué binario se ejecuta en cada pod, qué archivos abre, qué syscalls invoca, qué capabilities pide, qué conexiones de red establece, qué módulos del kernel se cargan. Lo hace cargando programas eBPF en los hook points del kernel (kprobes, tracepoints, uprobes, LSM hooks) y filtrando los eventos relevantes dentro del propio kernel con un lenguaje declarativo expresado como CRD (TracingPolicy y TracingPolicyNamespaced). El resultado es un flujo de eventos enriquecidos con metadata Kubernetes (pod, namespace, labels) que cuesta menos del 1% de CPU y, lo que diferencia a Tetragon de la competencia, puede bloquear acciones dentro del kernel —matar el proceso con SIGKILL o sobrescribir el retorno de un syscall— antes de que terminen de ejecutarse, sin race conditions. Frente a Falco (que parsea syscalls en userspace, 5-10% overhead, detection-only), Tetragon es “más barato y con enforcement”; frente al kernel desnudo, es “una capa declarativa que tu compañero de operaciones puede leer”. Este artículo es la introducción extensa que necesitas para abordarlo en serio: arquitectura, todos los hooks y selectors, los modos de operación, una guía de casos de uso (auditoría de exec, acceso a archivos sensibles, container escape, cryptomining, detección de rootkits, observabilidad de red) y las trampas que se ven en producción.
Este artículo es la parte 2 de la serie sobre eBPF. La parte 1 —eBPF de cero a Cilium: cómo el kernel aprendió a saltarse su propia pila TCP/IP— cubrió eBPF básico, los hooks de networking (XDP, TC, sock_ops), cómo Cilium implementa el datapath y los CRDs del BGP Control Plane v2. Aquí cogemos esos mismos hooks de eBPF y los usamos para algo distinto: observar y, si hace falta, frenar lo que hacen los procesos del cluster.
La analogía: auditd con esteroides en eBPF
Quien lleve unos años administrando Linux ha usado auditd. Es el subsistema clásico del kernel para auditar syscalls: configuras una regla con auditctl (por ejemplo, “monitoriza cualquier open sobre /etc/shadow”) y el kernel envía eventos a un daemon en userspace que los persiste. Funciona, pero tiene dos limitaciones que pesan en clusters Kubernetes modernos:
- Sin contexto Kubernetes. auditd reporta procesos por PID y UID. Saber qué pod, qué namespace, qué imagen, qué labels —la información que de verdad importa para responder a un incidente— requiere correlar a posteriori con datos de cri-o o containerd. Es operacionalmente miserable.
- Sin enforcement granular. auditd puede generar eventos, pero no puede tomar la decisión de matar el proceso ofensor antes de que termine la syscall. Eso lo dejas a una capa superior que lee los eventos, los procesa y mata el proceso… si llega a tiempo. Carrera por design.
Tetragon es auditd con esteroides: las mismas ideas conceptuales —hooks en syscalls, eventos a userspace— pero implementadas con eBPF moderno, con filtrado dentro del kernel para no pagar el coste de despertar el daemon por cada syscall irrelevante, con metadata Kubernetes inyectada por un agente que conoce el cluster, y con acciones que se ejecutan dentro del propio kernel sin esperar a que userspace decida. Si la regla dice “mata cualquier proceso que abra /etc/shadow desde el namespace prod”, la decisión se toma en el kprobe del kernel y SIGKILL se entrega antes de que el open se complete. No hay race; no hay ventana entre detección y acción.
Qué es Tetragon, arquitectónicamente
Tetragon es un agent que se despliega como DaemonSet (un pod por nodo) y un conjunto de CRDs que definen las políticas a aplicar. El agent tiene cuatro responsabilidades:
- Cargar programas eBPF en los hook points que las TracingPolicies activas demanden.
- Mantener un cache de metadata Kubernetes (pods, namespaces, labels) leyendo el API server, para poder enriquecer cada evento con el contexto correcto.
- Recolectar los eventos que los programas eBPF emiten (vía ring buffers) y serializarlos.
- Exportar los eventos a destinos configurables:
stdoutJSON (típico en sidecars o agentes log-collection), gRPC streaming (para consumirlos desde Hubble u otro consumer), archivo, o Fluentd/Loki/SIEM.
Los programas eBPF no son escritos por el usuario. Tetragon genera el bytecode a partir de las TracingPolicies: lee la política declarativa, decide qué hooks atacar, qué argumentos leer del kernel, qué filtros aplicar en línea y qué acciones ejecutar. El usuario solo escribe YAML.
Esta separación policy declarativa → bytecode eBPF generado es lo que hace a Tetragon usable. Escribir programas eBPF a mano es trabajo de un especialista; escribir una TracingPolicy es trabajo de un SRE con un buen ejemplo a la vista.
Los dos CRDs: TracingPolicy y TracingPolicyNamespaced
Tetragon expone exactamente dos CRDs principales:
TracingPolicy(cluster-scoped,cilium.io/v1alpha1): se aplica a todo el cluster, todos los nodos, todos los pods. Adecuada para políticas de plataforma (todo el cluster debe ser auditado igual): por ejemplo, “registra todoexecveen todos los pods” o “mata cualquier proceso que intente cargar un módulo del kernel”.TracingPolicyNamespaced(namespaced, mismo grupo y versión): se define dentro de un namespace y solo se aplica a los pods de ese namespace. Adecuada para políticas con autonomía por tenant: por ejemplo, “en el namespaceprod-payments, mata cualquierconnectsaliente a una IP fuera del rango corporativo”.
Ambos CRDs tienen exactamente la misma estructura interna. La diferencia es de scope. La distinción se introdujo precisamente para permitir multi-tenancy: que el equipo de seguridad central defina políticas TracingPolicy cluster-wide y que cada tenant pueda añadir las suyas con TracingPolicyNamespaced sin necesitar permisos cluster-admin.
Anatomía de una TracingPolicy
Una política se compone de:
- Hook points: qué eventos del kernel observar.
- Argumentos: qué datos leer cuando el hook se dispara.
- Selectors: filtros que se evalúan dentro del kernel para descartar eventos no relevantes y, opcionalmente, ejecutar acciones cuando coinciden.
Hook points soportados
La documentación oficial enumera cinco familias de hook points:
kprobes: hookean una función del kernel. Las syscalls son un caso particular (cuandosyscall: true) porque su ABI es distinta del de las funciones internas. Ejemplos típicos:sys_open,sys_openat,sys_connect,tcp_connect,do_mount,commit_creds. Es el hook más versátil y el que se usa el 80% del tiempo.tracepoints: hookean tracepoints estáticos compilados en el kernel. Más estables entre versiones de kernel que los kprobes (no dependen de nombres de funciones que pueden cambiar). Ejemplos:syscalls/sys_enter_openat,sched/sched_process_exec.uprobes: hookean funciones de bibliotecas o binarios en userspace. Sirven para observar primitivas de runtime como funciones de libssl, libc, Go runtime, JVM.tracepointsUSDT (User Statically Defined Tracepoints): tracepoints estáticos definidos en binarios userspace (como los que MySQL, PostgreSQL, OpenJDK exponen). Útiles para observabilidad de aplicaciones.lsmHooks(LSM, Linux Security Module): hooks del subsistema LSM, donde se enchufa SELinux/AppArmor. Permiten políticas de seguridad muy similares a las de MAC tradicional pero programables con eBPF. Ejemplo:file_open,inode_unlink,socket_bind.
Argumentos
Cada hook puede leer los argumentos de la función a la que está atado. Los tipos soportados cubren los primitivos (int, uint64, bool, string, char_buf) y abstracciones más altas (file, path, sock, linux_binprm, capability, bpf_attr, cred). Los tipos altos son punteros a estructuras del kernel que Tetragon sabe parsear; en lugar de tener que leer un offset, escribes type: file y Tetragon te da el path completo del archivo del descriptor.
Hay un detalle importante de capacidad: en kernels ≥ 5.4, Tetragon puede leer hasta 327 360 bytes de un argumento si se activa el flag de buffers grandes. Es la diferencia entre poder auditar execve con todos sus argv largos completos vs truncarlos a 256 bytes y perder contexto.
Selectors: filtrado en el kernel
Los selectors son lo que hace a Tetragon barato. Sin ellos, cada syscall del nodo dispararía un evento que viajaría kernel → ring buffer → agent → procesado → filtrado → descartado. Con selectors, el filtrado ocurre dentro del propio programa eBPF, en el kernel, y solo los eventos que importan llegan al userspace.
Los selectors disponibles incluyen:
matchArgs: filtra por el valor de un argumento. Operadores:Equal,NotEqual,Prefix,Postfix,GreaterThan,LessThan,Mask,SPort(source port),DPort(dest port),Family(AF_INET vs AF_INET6),State(estado del socket).matchPIDs: filtra por PID; útil para targeted observation.matchBinaries: filtra por el binario que ejecuta el syscall (path absoluto), conOperator: In,NotIn,Prefix. Imprescindible para evitar el ruido de procesos legítimos del sistema.matchNamespaces: filtra por namespace Linux (Pid, Mnt, Net, Ipc, Cgroup, User). Permite políticas específicas para procesos en contenedores vs el host.matchCapabilities: filtra por capabilities efectivas del proceso. Bloquear acciones que requieranCAP_SYS_ADMINque se ejecuten en pods que no deberían tenerlas.matchNamespaceChanges: detecta cambios de namespace (típico de container escape).matchCapabilityChanges: detecta cambios de capabilities (escalada de privilegios).matchActions: las acciones que se ejecutan cuando todos los matchers anteriores aciertan.
Acciones: del simple Post al Sigkill
Cuando un selector matchea, se ejecuta una action. Tetragon define varias:
Post: emite un evento al userspace (el caso de observability). SoportarateLimitpara evitar inundar el agent si la condición se dispara mil veces por segundo. La sintaxis acepta5para 5 segundos,5mpara 5 minutos,1hpara 1 hora.Sigkill: envíaSIGKILLal proceso ofensor desde dentro del kernel, antes de que la syscall complete. Esto es lo único que garantiza enforcement sin race.Override: sobrescribe el valor de retorno del syscall. Útil para hacer creer al proceso que el syscall falló (Override -EPERM) sin matarlo. Mejor experiencia para apps que pueden manejar errores; peor para apps que asumen éxito.Signal: envía cualquier señal arbitraria (no soloSIGKILL).NoPost: no emite evento, útil cuando se combina con otro selector que sí emite y solo quieres acción sin telemetría duplicada.FollowFDyUnfollowFD: marcan un file descriptor para seguir su ciclo de vida y enriquecer eventos siguientes con el path original. Útil para audit de “qué proceso leyó este archivo después de abrirlo”.TrackSockyUntrackSock: idem para sockets.GetUrlyDnsLookup: hacen peticiones HTTP o resoluciones DNS desde el kernel. Pensado para integraciones con sistemas externos (webhooks de seguridad, lookups de reputación de IP).NotifyEnforceryCleanupEnforcerNotification: comunicación con el subsistema de enforcement de Tetragon para acciones complejas.
Modos: detection vs enforcement
Una política se puede declarar en uno de dos modos explícitos:
enforce: las acciones de enforcement (Sigkill,Override,Signal) están activas. Esto es producción.monitoring: las acciones de enforcement son ignoradas; solo se emiten eventosPost. Esto es el modo “vamos a ver qué pasaría si esto estuviese activado”, crítico para probar políticas sin romper aplicaciones.
El control se hace con el campo spec.options[].name: policy-mode y value: monitoring o enforce. Es la mejor práctica: empezar en monitoring, recolectar eventos durante días, ajustar los selectors hasta que no salgan falsos positivos, y entonces cambiar a enforce.
Ejemplo completo: bloquear escrituras a /etc/passwd en namespace prod
Una política realista, comentada línea a línea:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: block-passwd-write
namespace: prod
spec:
kprobes:
- call: "fd_install"
syscall: false # función del kernel, no syscall
args:
- index: 0
type: int # file descriptor
- index: 1
type: "file" # struct file*
selectors:
- matchArgs:
- index: 1
operator: "Equal"
values:
- "/etc/passwd"
matchActions:
- action: Sigkill # mata el proceso
rateLimit: "1m" # max una vez por minuto
options:
- name: policy-mode
value: enforce # modo enforcement activo
fd_install se ejecuta cada vez que un proceso obtiene un nuevo file descriptor; el segundo argumento es la struct file del archivo. Tetragon sabe resolverla a su path absoluto. El matchArgs compara ese path con /etc/passwd. Si matchea, Sigkill mata el proceso antes de que el descriptor llegue siquiera a ser usable. rateLimit: 1m impide que el agent se sature si una aplicación malintencionada lo intenta en bucle.
Casos de uso habituales
Vamos al uso real. Estos son los seis casos que aparecen en cualquier despliegue serio de Tetragon en 2026.
1. Auditoría de ejecución (execve)
El caso de uso más básico y, sin embargo, el más valioso. ¿Qué binarios se están ejecutando en cada pod? En un container que se supone que corre solo nginx, ver de pronto un sh o un wget es bandera roja casi siempre.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: audit-execve
spec:
tracepoints:
- subsystem: "sched"
event: "sched_process_exec"
args:
- index: 4
type: linux_binprm # struct linux_binprm*
selectors:
- matchActions:
- action: Post # solo eventos, no enforcement
Sin filtros: cada execve del cluster genera un evento. Con metadata K8s, el evento incluye pod, namespace, container, imagen, labels. Lo conviertes en flujo de eventos hacia tu SIEM y montas reglas: “alerta si veo sh, bash, nc, curl, wget, python ejecutándose en cualquier pod del namespace prod-api”.
Variación con enforcement: en lugar de Post, usar matchBinaries con Operator: NotIn y una whitelist, y Sigkill si el binario no está en la lista. Caja muy rígida pero efectiva en pods que son “single-binary” (como un microservicio Go).
2. Acceso a archivos sensibles
Detectar (o bloquear) lecturas y escrituras a archivos críticos: /etc/shadow, /etc/kubernetes/, montajes de Secrets, /var/run/docker.sock, /proc/*/cmdline.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: sensitive-file-access
spec:
kprobes:
- call: "security_file_open" # LSM-ish via kprobe
syscall: false
args:
- index: 0
type: "file"
selectors:
- matchArgs:
- index: 0
operator: "Prefix"
values:
- "/etc/shadow"
- "/var/run/secrets/"
- "/var/run/docker.sock"
matchBinaries:
- operator: "NotIn"
values:
- "/usr/bin/kubelet" # acceso legítimo de kubelet
matchActions:
- action: Post
El matchBinaries: NotIn es importante: kubelet y otros agentes legítimos del nodo acceden a estos paths constantemente y generarían ruido. Filtramos esos en el kernel.
En enforcement: cambiar Post por Override con argError: -1 (EPERM), de modo que la apertura falle pero el proceso ofensor siga vivo y produzca el error para que las herramientas de tracing lo recojan.
3. Conexiones de red salientes no autorizadas
Detectar conexiones outbound a destinos fuera del rango corporativo. Útil para detectar exfiltración de datos o command-and-control de malware.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicyNamespaced
metadata:
name: block-external-egress
namespace: prod
spec:
kprobes:
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
selectors:
- matchArgs:
- index: 0
operator: "NotDAddr" # destino NO en estos CIDRs
values:
- "10.0.0.0/8"
- "192.168.0.0/16"
- "172.16.0.0/12"
matchActions:
- action: Sigkill
options:
- name: policy-mode
value: enforce
Esto mata cualquier intento de conexión TCP a una IP que no esté en los CIDRs corporativos, en namespace prod. Cilium ya hace esto con NetworkPolicy, pero Tetragon tiene dos ventajas complementarias:
- Te da el proceso que intentó la conexión, no solo “el pod X intentó conectar a Y”.
- Funciona también para protocolos exóticos donde NetworkPolicy es menos expresiva.
4. Detección de container escape
Container escape es la pesadilla operacional: un proceso dentro de un contenedor consigue romper el aislamiento (vía un kernel exploit, una capability mal puesta, un mount hostPath mal configurado) y obtener acceso al host. Tres señales típicas:
- Cambio de namespace del proceso (sale del namespace
piddel contenedor). setnsounshareen procesos no-init.- Acceso a
/proc/1/rooto/dev/desde un contenedor.
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-container-escape
spec:
kprobes:
- call: "__x64_sys_setns"
syscall: true
args:
- index: 0
type: int
- index: 1
type: int
selectors:
- matchNamespaces:
- namespace: Pid
operator: NotIn
values: ["host_ns"] # solo procesos NO en pid namespace del host
matchActions:
- action: Sigkill
- call: "__x64_sys_unshare"
syscall: true
args:
- index: 0
type: int
selectors:
- matchNamespaceChanges:
- unshare: true
matchActions:
- action: Post
__x64_sys_setns con destino el namespace del host desde un proceso en contenedor es prácticamente siempre malicioso (los containers legítimos no necesitan esto en runtime).
5. Cryptomining
Los procesos de minería tienen perfiles bastante reconocibles:
- Procesos con nombres como
xmrig,minerd,cgminer, o procesos legítimos comopythonejecutando scripts con uso intensivo de CPU. - Conexiones outbound a pools de minería conocidas (lista pública de IPs y dominios).
- Uso anómalo de
/dev/cpu_dma_latencypara evitar throttling.
Una política combinada:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: detect-cryptomining
spec:
tracepoints:
- subsystem: "sched"
event: "sched_process_exec"
args:
- index: 4
type: linux_binprm
selectors:
- matchArgs:
- index: 4
operator: "Postfix"
values:
- "/xmrig"
- "/minerd"
- "/cgminer"
matchActions:
- action: Sigkill
kprobes:
- call: "tcp_connect"
syscall: false
args:
- index: 0
type: "sock"
selectors:
- matchArgs:
- index: 0
operator: "DPort"
values: ["3333", "5555", "7777", "14444"] # puertos comunes de pools
matchActions:
- action: Post # solo registra
La doble política: matar binarios con nombres clásicos (cinturón) y registrar conexiones a puertos de pools (tirantes), para alertar también cuando alguien renombre xmrig a nginx-helper o use puertos exóticos.
6. Detección de rootkits y módulos del kernel sospechosos
Los rootkits modernos cargan módulos del kernel para parchear funciones (hide procesos, hide conexiones de red, esconder archivos). Detectarlos:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: kernel-module-load
spec:
kprobes:
- call: "do_init_module"
syscall: false
args:
- index: 0
type: "string"
selectors:
- matchActions:
- action: Post
- call: "security_kernel_read_file"
syscall: false
args:
- index: 0
type: "file"
- index: 1
type: int
selectors:
- matchArgs:
- index: 1
operator: "Equal"
values: ["READING_MODULE"]
matchActions:
- action: Post
En un cluster Kubernetes “bien configurado” no se cargan módulos nuevos del kernel en runtime; cualquier evento aquí es altamente sospechoso. Combina con enforcement para máquinas donde los módulos deberían estar fijos: Sigkill al que intenta cargar uno.
Bonus: detección de modificación de eBPF maps por terceros
Como tendencia 2025-2026: cargar programas eBPF maliciosos para esconder presencia. Tetragon puede observar el bpf syscall:
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
name: audit-bpf-syscalls
spec:
kprobes:
- call: "__x64_sys_bpf"
syscall: true
args:
- index: 0
type: int # bpf cmd
- index: 1
type: bpf_attr
selectors:
- matchBinaries:
- operator: "NotIn"
values:
- "/usr/bin/cilium-agent"
- "/usr/bin/tetragon"
- "/usr/bin/bpftool"
matchActions:
- action: Post
Cualquier proceso que no sea uno de los agentes legítimos cargando programas eBPF: te interesa saberlo.
Comparativa con Falco
Falco es el competidor más cercano: también es runtime security para Kubernetes, también basado originalmente en eBPF (y antes en kernel modules), también con políticas declarativas. Tres años atrás eran funcionalmente parecidos. En 2026 la divergencia es clara:
| Dimensión | Tetragon | Falco |
|---|---|---|
| Filosofía | Cilium-native, integrado | Standalone, genérico |
| Filtrado | En el kernel (eBPF) | Parsing en userspace |
| Overhead típico | <1% CPU | 5-10% CPU |
| Enforcement | Sí, in-kernel (Sigkill, Override) | No nativo (depende de plugins) |
| Race conditions | No (acción atómica con syscall) | Sí en enforcement vía plugins |
| Detección de falsos positivos | Baja (contexto K8s en kernel) | Más alta (parsing posterior) |
| Latencia de detección | 5-26 ms | ~10 ms (más constante) |
| Madurez del ecosistema | Joven, en crecimiento | Maduro, mucho material |
| Comunidad | Cilium / CNCF Incubating | CNCF Graduated |
| Integraciones | Hubble nativo | Falcosidekick, muchas |
| CRDs por política | TracingPolicy / Namespaced | Sin CRDs; reglas en YAML |
Cuándo elegir cada uno:
- Tetragon si ya usas Cilium, si necesitas enforcement en el kernel (no detection-only), si el overhead te importa (cargas con muchas syscalls), y si valoras la integración con Hubble. Detección de container escape y cryptomining es donde más se mide su ventaja sobre Falco.
- Falco si quieres una herramienta independiente del CNI, si necesitas el catálogo de reglas pre-hechas y la comunidad amplia, si tu cluster no es Cilium, o si la integración con SIEMs y notificadores ya hechos (Falcosidekick) te ahorra trabajo.
- Los dos si la organización es grande: Falco para amplitud de detección, Tetragon para enforcement quirúrgico en cargas críticas. Es lo que más se ve en empresas que llevan años con Falco y añaden Tetragon para casos específicos.
Hubble + Tetragon: observabilidad unificada
Hubble es el componente de observabilidad de tráfico de Cilium: muestra flow logs L3-L7 con cero impacto en latencia. Tetragon expone sus eventos por gRPC con el mismo formato y vocabulario que Hubble, lo que permite:
- Verlos en la misma UI (Hubble UI muestra eventos Tetragon como una “capa” más).
- Correlar eventos de red (Hubble) con eventos de proceso (Tetragon) en el mismo timeline.
- Exportarlos juntos a Loki/Tempo/SIEM como un flujo único.
La sinergia clave: Hubble te dice “este pod hizo una conexión TCP a 1.2.3.4:80”. Tetragon te dice “este pod ejecutó curl 1.2.3.4 desde un binario bash lanzado por pid 1234”. Juntos te dan la historia completa.
Despliegue y operación
Helm
Instalación canónica con Helm:
helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon \
--namespace kube-system \
--set tetragon.exportFilename=/var/log/tetragon/tetragon.log \
--set tetragon.exportFileMaxSizeMB=50 \
--set tetragon.exportFileRotationInterval=24h
Tetragon despliega su DaemonSet, sus CRDs y un service para Hubble. Por defecto, expone los eventos en stdout del pod del agent (los recoge cualquier log aggregator del cluster).
CLI tetra
Tetragon trae una CLI llamada tetra para investigación interactiva:
# stream en tiempo real de eventos del nodo
tetra getevents -o compact --pods <pod-name>
# events JSON estructurado para procesar con jq
tetra getevents -o json --since 5m --namespace prod
# ver políticas cargadas
tetra tracingpolicy list
Es la mejor herramienta para depurar políticas en monitoring antes de pasarlas a enforce.
Exportar a SIEM
Tres rutas habituales:
- stdout + log aggregator: el agent escribe JSON al stdout, Fluent Bit/Vector lo recoge y lo envía a Splunk/Datadog/Elastic. Simple, funciona con cualquier infraestructura de logging.
- gRPC streaming: para integraciones de baja latencia. Un consumer gRPC propio o Hubble Relay.
- Archivo + rotación: para entornos air-gapped o auditorías regulatorias que exigen logs persistentes con rotación controlada.
Performance
Los benchmarks publicados consistentemente sitúan a Tetragon en <1% de CPU del nodo en cargas reales, comparado con 5-10% de Falco en las mismas cargas. La razón es la separación arquitectónica: Tetragon filtra en el kernel y solo lleva a userspace los eventos que realmente importan; Falco lleva todos los syscalls a userspace y los filtra allí. En clusters con miles de pods haciendo cientos de miles de syscalls por segundo, la diferencia se nota en la factura.
Trampas operativas comunes
monitoring permanente
La mayor trampa es no llegar nunca a enforce: empezar bien con políticas en monitoring, recolectar eventos, ajustar selectors, y luego nunca conmutar. Resultado: tienes detection sin prevention, exactamente lo que Falco te daba sin pagar la complejidad de Tetragon. Si vas a usar Tetragon, planifica el camino a enforce de las políticas críticas.
Selectors demasiado laxos
Una política con un único matchActions: Post sin selectors específicos genera eventos por cada syscall del hook elegido. En un nodo serio son decenas de miles por segundo, que llenan logs, saturan exporters y esconden la señal en el ruido. Empieza siempre con filtros estrictos (matchBinaries, matchNamespaces, matchPIDs) y abre cuando sepas qué buscas.
Kernel demasiado viejo
Tetragon necesita features de eBPF modernas. Kernels < 5.4 no tienen el soporte de buffers grandes (necesario para execve con argv completos). Kernels < 5.10 no tienen muchos de los hooks LSM. Kernel 5.15+ es el mínimo recomendado para producción y 6.1+ para tener todas las features.
Hooks en funciones del kernel renombradas
Los kprobes están atados a nombres de funciones del kernel que pueden cambiar entre versiones. Una política que use __x64_sys_setns puede fallar silenciosamente en un kernel donde la función se llama __do_sys_setns. Soluciones: usar tracepoints estáticos cuando estén disponibles (más estables), o tener políticas alternativas con varios call por compatibilidad.
Sigkill en namespaces críticos
Aplicar Sigkill a procesos en kube-system o cilium-system puede romper el cluster. Las políticas de enforcement deben excluir explícitamente los namespaces de plataforma con matchNamespaces Operator: NotIn, o limitar el scope con TracingPolicyNamespaced para asegurar que no acciona en sistemas que no debe.
rateLimit ausente
Una política sin rateLimit en Post puede sufrir un fan-out catastrófico si la condición se cumple millones de veces en un instante (típico en bucles de ataque o en bugs de aplicación). El agent se satura, los eventos se pierden, los logs se desbordan. Pon siempre rateLimit sensato en políticas de detección, especialmente en hooks de alta frecuencia como tcp_connect o execve.
Lo que no hemos cubierto (próximos artículos)
- eBPF LSM hooks en profundidad: cómo se relacionan con SELinux/AppArmor y cuándo Tetragon es la herramienta correcta vs MAC clásico.
- Hubble UI con Tetragon overlay: configuración de la UI para mostrar la observabilidad de proceso y la de red en el mismo timeline.
- Integración con OPA/Kyverno: cómo Tetragon complementa policy engines de admission (Kyverno valida en admission; Tetragon valida en runtime).
- Forensics con eBPF: combinando Tetragon con herramientas como Beyla u OpenTelemetry para trazar la cadena completa de un incidente desde la conexión inicial hasta la syscall final.
Referencias
Documentación oficial (mayo 2026):
- Tetragon — sitio oficial — punto de entrada.
- Tetragon docs — Tracing Policy — referencia conceptual.
- Tetragon docs — Hook points — kprobes, tracepoints, uprobes, LSM, USDT.
- Tetragon docs — Selectors — referencia completa de filtros.
- Tetragon docs — Enforcement Mode.
- Tetragon docs — Kubernetes Identity Aware Policies —
TracingPolicyNamespaced. - Tetragon GitHub.
Comparativas y análisis:
- Comparative Analysis of eBPF-Based Runtime Security Monitoring (paper SciTePress 2025) — benchmark con números independientes.
- Best eBPF Security Solutions for Kubernetes (ARMO, 2026) — comparativa Falco vs Tetragon vs KubeArmor.
- Falco vs. Tetragon (Asim Mirza, Medium) — análisis con casos de uso.
- Deep Dive into Tetragon (A Security Engineer) — recorrido por dentro del agente.
- Tetragon Series, Part 2: Enforcing Sensitive File Access (Medium).
Ecosistema:
- Cilium Hubble — observabilidad de red.
- Falco — sitio oficial — el otro grande del campo.
- KubeArmor — la tercera opción, con AppArmor + eBPF.
Cross-references:
- Parte 1 de la serie: eBPF de cero a Cilium: cómo el kernel aprendió a saltarse su propia pila TCP/IP — los fundamentos de eBPF que aquí damos por leídos.
- Kubernetes con Cilium BGP: servicios accesibles sin Ingress — punto de partida del ecosistema Cilium en este blog.