Falco y eBPF:
detección en tiempo de ejecución para contenedores y hosts
Serie Blue Team | unfantasmaenelsistema.com
El hardening de una imagen, las políticas de admisión de Kubernetes y el escaneo de vulnerabilidades trabajan antes de que el contenedor arranque. Pero cuando el proceso ya está corriendo y alguien abre una shell dentro de él, escribe en `/etc/`, monta el socket de Docker o abre una conexión saliente que ese servicio nunca hace, hace falta algo que lo vea mientras ocurre. Esa capa se llama seguridad en tiempo de ejecución (runtime security), y en el mundo open source la referencia es Falco.
Falco es un proyecto de la CNCF (graduado en 2024) que observa las llamadas al sistema del kernel, las compara con un conjunto de reglas y emite una alerta cuando algo se sale de lo esperado. La pieza que hace esa observación posible sin parchear el kernel ni cargar módulos propietarios es eBPF. Este artículo explica qué es eBPF y por qué cambia las reglas del juego, cómo está montado Falco por dentro, qué detectan sus reglas de referencia, cómo se afina para que no se convierta en ruido, y cómo encaja con Tetragon, Tracee y el control de admisión.
Qué aprenderás en este artículo
Qué es eBPF y por qué permite observar el kernel de forma segura, sin módulos propietarios · La arquitectura de Falco: fuente de eventos (driver), motor de reglas y salidas · Las reglas de referencia y qué actividad real detectan en contenedores y en el host · Cómo se afinan los falsos positivos con listas de excepciones y macros · Alternativas y complementos: Tetragon, Tracee, y dónde termina la detección y empieza el bloqueo · El encaje con el control de admisión (OPA/Gatekeeper, Kyverno) y el coste real de tenerlo
1. eBPF: ejecutar código en el kernel sin romperlo
eBPF (siglas de extended Berkeley Packet Filter) es una máquina virtual dentro del kernel de Linux que permite cargar y ejecutar pequeños programas en respuesta a eventos del propio kernel: una llamada al sistema, la entrada o salida de una función, la recepción de un paquete, un cambio de contexto. El programa se engancha a un punto de interés (un «hook»), se ejecuta cuando ese evento sucede y puede enviar datos al espacio de usuario a través de estructuras compartidas llamadas «mapas».
Lo relevante para seguridad es cómo consigue eBPF hacer esto sin comprometer la estabilidad del sistema. Antes de cargar un programa eBPF, el kernel lo pasa por un verificador: analiza todos los caminos de ejecución posibles y rechaza el programa si puede entrar en un bucle infinito, acceder a memoria fuera de límites o dejar el kernel en un estado inconsistente. Un programa que pasa el verificador no puede colgar la máquina. Esa garantía es lo que diferencia a eBPF de un módulo de kernel tradicional, donde un fallo de puntero es un cuelgue total del sistema (kernel panic).
| Enfoque | Cómo obtiene los eventos | Riesgo / coste |
|---|---|---|
| Módulo de kernel clásico | Código nativo cargado en el kernel (el driver kmod histórico de Falco) | Un fallo tumba el host; hay que recompilar por versión de kernel |
| eBPF moderno (CO-RE) | Programa eBPF verificado, portable entre kernels vía BTF | Bajo: el verificador acota el programa; sin recompilar por kernel |
| Auditoría del kernel (auditd) | Subsistema audit del kernel, reglas de syscall | Un solo consumidor del socket de audit por host; formato verboso |
| Polling desde espacio de usuario (p. ej. leer /proc) | Consultas periódicas al estado del sistema | Ciego entre consultas; no ve actividad efímera |
El término CO-RE (compilar una vez, ejecutar en todas partes) merece una nota: junto con la información de tipos del kernel (BTF), permite compilar el programa eBPF una vez y ejecutarlo en kernels con estructuras internas distintas, sin recompilar en cada equipo. Es lo que hace que un agente basado en eBPF moderno se despliegue como un binario normal y no como un proyecto de compilación en cada nodo.
eBPF no es solo seguridad
La misma tecnología está detrás de la observabilidad de red (Cilium), del perfilado de rendimiento (bpftrace, los flame graphs de Brendan Gregg), del balanceo de carga (Katran) y de herramientas de diagnóstico como `execsnoop` u `opensnoop` del paquete bcc. Falco es una aplicación concreta de una capacidad de propósito general.
2. La arquitectura de Falco
Falco se compone de tres bloques: de dónde saca los eventos, cómo decide si un evento importa, y qué hace cuando importa.
2.1 La fuente de eventos: el driver
Falco necesita ver las llamadas al sistema de toda la máquina. Para conseguirlas usa un driver, y hoy hay tres opciones:
- eBPF moderno (`modern_ebpf`): la opción recomendada. Se carga como un programa eBPF CO-RE, no requiere módulo ni compilación, y necesita un kernel razonablemente reciente (5.8 o superior) con BTF.
- eBPF con sonda legada (`ebpf`): una sonda eBPF que se compila para el kernel del host; opción intermedia para kernels antiguos.
- Módulo de kernel (`kmod`): el driver histórico, un módulo que se inserta en el kernel. Sigue disponible pero se desaconseja para despliegues nuevos por el riesgo que implica.
El driver entrega un flujo de eventos crudos (syscalls con sus argumentos, más metadatos de proceso y de contenedor) a la librería libsinsp, que los enriquece: resuelve el nombre del contenedor y la imagen, el pod y el namespace de Kubernetes, el árbol de proceso padre, el usuario. Ese contexto es lo que hace que una regla pueda decir «shell dentro de un contenedor» en lugar de solo «execve de /bin/bash».
2.2 El motor de reglas
Las reglas de Falco se escriben en YAML y tienen tres piezas: una condición (la expresión que se evalúa contra cada evento), una salida (el texto de la alerta, con campos interpolados) y una prioridad (`EMERGENCY` … `DEBUG`). Para no repetir expresiones, Falco usa macros (condiciones con nombre, reutilizables) y listas (conjuntos de valores). El conjunto oficial, `falco_rules.yaml`, trae decenas de macros como `container`, `spawned_process` o `shell_procs` que las reglas combinan.
# Estructura de una regla de Falco (ejemplo del conjunto de referencia, resumido)
- macro: container
condition: (container.id != host)
- macro: spawned_process
condition: (evt.type in (execve, execveat) and evt.dir=<)
- list: shell_binaries
items: [ash, bash, csh, ksh, sh, tcsh, zsh, dash]
- rule: Terminal shell in container
desc: Se ha iniciado una shell interactiva dentro de un contenedor
condition: >
spawned_process and container
and proc.name in (shell_binaries)
and proc.tty != 0
output: >
Shell abierta en contenedor
(user=%user.name container=%container.name image=%container.image.repository
proc=%proc.cmdline parent=%proc.pname)
priority: NOTICE
tags: [container, shell, mitre_execution, T1059]Los campos disponibles (`proc.*`, `container.*`, `fd.*`, `k8s.*`, `user.*`, `evt.*`) son los mismos que expone Sysdig, el proyecto del que nació Falco. La condición se evalúa en el propio proceso de Falco, en espacio de usuario, sobre el flujo que sube el driver.
2.3 Las salidas
Cuando una regla dispara, Falco emite la alerta por los canales que tenga configurados: la salida estándar (formato texto o JSON), syslog, un fichero, o un HTTP POST a un endpoint. Ese endpoint suele ser Falcosidekick, un componente que abre el abanico a más de 50 destinos: Slack, Teams, un SIEM (Elastic, Splunk), Prometheus/Alertmanager, un bucket de almacenamiento, una función serverless, o Falco Talon como motor de respuesta. En Kubernetes se despliega como DaemonSet (un pod de Falco por nodo) mediante el chart de Helm oficial.
| Componente | Función | Dónde vive |
|---|---|---|
| driver (modern_ebpf) | Captura las syscalls de todo el host | Kernel, cargado por Falco |
| libsinsp / libscap | Parseo y enriquecimiento (contenedor, k8s, árbol de proceso) | Proceso de Falco |
| motor de reglas | Evalúa condiciones YAML contra cada evento | Proceso de Falco |
| Falcosidekick | Enruta las alertas a destinos externos | Pod aparte / DaemonSet |
| Falcosidekick-UI | Panel web para revisar alertas | Despliegue opcional |
| Falco Talon | Respuesta automatizada (matar pod, aislar, etiquetar) | Despliegue opcional |
3. Qué detectan las reglas de referencia
El valor inmediato de Falco está en su conjunto de reglas por defecto, mantenido por la comunidad y publicado por separado del binario (el artefacto `falco-rules`, versionado). Estas son las categorías que cubre, con ejemplos de reglas reales:
| Actividad detectada | Regla de ejemplo | Por qué importa |
|---|---|---|
| Shell interactiva en contenedor | Terminal shell in container | Un contenedor de aplicación no debería tener a nadie escribiendo comandos dentro |
| Escritura en directorios del sistema | Write below etc / Write below root / Write below binary dir | Modificar /etc, /bin o /usr/bin en caliente es señal de manipulación o persistencia |
| Lectura de datos sensibles | Read sensitive file untrusted (p. ej. /etc/shadow) | Acceso a credenciales locales por un proceso que no es el habitual |
| Conexión saliente inesperada | Unexpected outbound connection / Outbound connection to C2 servers | Un servicio que solo debería recibir tráfico y de pronto llama a fuera |
| Montaje del socket de Docker | Launch privileged container / Docker socket mounted / mount from container | Acceso al socket de Docker desde un contenedor equivale a root en el host |
| Contenedor privilegiado o con capacidades | Launch privileged container / Change namespace via setns | Ruptura de aislamiento; camino habitual de escape de contenedor |
| Modificación de binarios o herramientas de red | Launch remote file copy tools / Netcat remote code execution | Descarga de herramientas (curl, wget) o uso de netcat dentro de un contenedor |
| Cambios en la configuración de Falco o del kernel | Modify container entrypoint / Linux kernel module injection | Intento de cegar la propia telemetría o cargar un rootkit |
| Actividad sospechosa en el plano de Kubernetes | K8s Serviceaccount created / Attach/Exec Pod (audit) | Con el plugin de k8s audit, `kubectl exec` y creación de cuentas de servicio |
Falco puede consumir dos fuentes además de las syscalls, mediante su sistema de plugins: el registro de auditoría de la API de Kubernetes (para ver `exec`, `attach`, creación de roles y secretos) y registros de proveedores cloud (CloudTrail de AWS, Okta, GitHub). Con eso, una sola herramienta cubre actividad en el nodo y en el plano de control.
El conjunto por defecto está pensado para dispararse poco… si el clúster es limpio
Reglas como «Write below etc» o «Read sensitive file» son ruidosas en clústeres donde las imágenes escriben en /etc al arrancar o hacen apt-get en el arranque del contenedor. Eso no es un fallo de Falco: es que esas prácticas son exactamente lo que la regla busca. El afinado (sección 4) es parte del despliegue, no un extra.
4. Afinado: convivir con los falsos positivos
Una regla de Falco que genera cien alertas al día por comportamiento legítimo se ignora en una semana. El afinado consiste en describir con precisión qué es «normal» en tu entorno para que solo quede lo anómalo. Hay varias herramientas, de menos a más invasiva:
- Listas de excepciones dentro de la regla (`exceptions`): el mecanismo recomendado desde Falco 0.28. Permite añadir a una regla concreta combinaciones de campos que no deben disparar (por ejemplo, «este proceso, en esta imagen, escribiendo en esta ruta») sin reescribir la condición.
- Macros y listas propias: si varias reglas comparten una excepción (una lista de imágenes de confianza, un conjunto de puertos internos), se define una vez como lista y se referencia.
- Anular la regla completa: redefinir una regla del conjunto oficial con el mismo nombre en tu fichero local para cambiar su condición, su prioridad o desactivarla (`enabled: false`).
- Reglas propias: para lo que el conjunto por defecto no cubre — un binario que nunca debería ejecutarse en producción, una ruta concreta que solo escribe un proceso concreto.
# Excepción a una regla del conjunto oficial, en un fichero local (rules.d/)
# Se carga DESPUÉS de falco_rules.yaml y solo aporta la excepción.
- rule: Write below etc
exceptions:
- name: proc_writer_conocido
fields: [proc.name, fd.directory, container.image.repository]
comps: [=, =, =]
values:
- [config-reloader, /etc/nginx, miorg/nginx-sidecar]
- [update-ca-certificates, /etc/ssl/certs, miorg/base-debian]
# Regla propia: en producción, /app nunca ejecuta un intérprete nuevo
- rule: Interprete inesperado en pod de produccion
desc: Ejecucion de python/perl/ruby en un contenedor de la app en prod
condition: >
spawned_process and container
and k8s.ns.name = "produccion"
and proc.name in (python3, perl, ruby, node)
and not proc.pname in (supervisord, tini)
output: >
Interprete lanzado en prod (pod=%k8s.pod.name img=%container.image.repository
proc=%proc.cmdline parent=%proc.pname user=%user.name)
priority: WARNING
tags: [container, mitre_execution]El flujo de trabajo habitual: desplegar Falco en modo observación una o dos semanas, recoger todo lo que dispara, clasificar cada alerta recurrente como «esto es normal aquí» (excepción) o «esto habría que arreglarlo» (hallazgo), y solo entonces conectar las salidas al canal donde alguien las va a mirar. Falco trae `falco –validate` para comprobar la sintaxis de las reglas antes de aplicarlas.
Herramientas de afinado
falcoctl gestiona la instalación y actualización de los conjuntos de reglas y plugins desde artefactos OCI. El proyecto falco-testing permite escribir pruebas para tus reglas. Y los playground en línea de falco.org ayudan a probar condiciones contra eventos de ejemplo sin desplegar nada.
5. Alternativas y complementos
Falco no es la única herramienta de runtime security basada en eBPF, y las demás resuelven matices distintos.
| Proyecto | Enfoque | Diferencia con Falco |
|---|---|---|
| Tetragon (Isovalent/Cilium, CNCF) | Observabilidad y bloqueo (enforcement) en el kernel vía eBPF | Puede bloquear (matar el proceso, SIGKILL) desde el propio kernel, no solo alertar; políticas TracingPolicy |
| Tracee (Aqua) | Detección en runtime y forense, eBPF CO-RE | Fuerte en firmas de comportamiento y en análisis de eventos; también detección de explotación de CVE conocidas |
| Falco | Detección y alerta, motor de reglas YAML muy legible | El ecosistema más maduro de reglas, salidas y comunidad; el enforcement es externo (Talon) y reactivo |
| KubeArmor | Enforcement a nivel de pod con LSM (BPF-LSM, AppArmor) | Orientado a políticas de menor privilegio por workload, más que a detección de anomalías |
| Auditd + Sysmon for Linux | Auditoría del kernel clásica | Sin conciencia nativa de contenedor/k8s; más pesado de correlacionar; útil como capa host |
La distinción importante es detección frente a prevención. Falco observa y avisa: entre que la regla dispara y que alguien (o Talon) actúa, pasa un tiempo durante el cual el proceso sospechoso sigue vivo. Tetragon y KubeArmor pueden cortar la acción en el momento, a costa de un modelo de políticas más rígido y del riesgo de bloquear algo legítimo. Muchas organizaciones despliegan Falco para tener visibilidad amplia y añaden enforcement selectivo solo en los workloads críticos.
5.1 Dónde encaja el control de admisión
El control de admisión de Kubernetes (OPA/Gatekeeper, Kyverno) actúa en un momento anterior y complementario: rechaza el pod antes de crearlo si viola una política — imagen sin firmar, `privileged: true`, `hostPath` a rutas sensibles, ausencia de `securityContext`. Es prevención estática en el momento del despliegue. Falco entra después, cuando el pod ya admitido hace algo que su manifiesto no revelaba. Son capas distintas de la misma defensa:
- Admisión (Kyverno/Gatekeeper): «este pod no puede ser privilegiado» — se evalúa al crear el recurso.
- Runtime (Falco): «este pod, que no es privilegiado, acaba de intentar un `setns` o de montar el socket de Docker» — se evalúa mientras se ejecuta.
- Enforcement runtime (Tetragon/Talon): «y por tanto lo matamos / aislamos el nodo / etiquetamos el pod para cuarentena».
6. Coste y cobertura
Instrumentar todas las syscalls de un host tiene un precio, y conviene medirlo antes de prometer despliegue en flota.
- CPU y memoria: el DaemonSet de Falco con driver eBPF moderno suele consumir del orden de una fracción de núcleo y 128–512 MB de RAM por nodo en cargas normales; sube con el volumen de syscalls (nodos con mucha actividad de red o de proceso) y con reglas caras. Es comparable a otros agentes de nodo, pero hay que dimensionarlo, no darlo por gratis.
- Requisitos de kernel: el driver `modern_ebpf` necesita kernel 5.8+ con `CONFIG_DEBUG_INFO_BTF`. Distribuciones recientes (Ubuntu 20.04+ con HWE, Amazon Linux 2023, Bottlerocket, GKE/EKS/AKS actuales) lo cumplen; kernels viejos obligan al driver legado o al módulo.
- Entornos gestionados restringidos: en algunos contenedores serverless (Fargate, Cloud Run) no tienes acceso al kernel del nodo y Falco no puede desplegarse como DaemonSet; ahí la detección se apoya en los logs de auditoría de la plataforma.
- Cobertura de la telemetría: Falco ve syscalls y, con plugins, k8s audit y logs cloud. No ve tráfico de red descifrado, ni el contenido de la memoria de un proceso, ni actividad que no pase por una syscall.
- Punto ciego durante el arranque: si Falco arranca después que un pod, se pierde lo que ese pod hizo en sus primeros segundos. En un DaemonSet bien configurado la ventana es pequeña, pero existe.
La parada del agente es en sí misma un evento
Un atacante con privilegios en el nodo puede intentar parar el DaemonSet de Falco o descargar el programa eBPF. Que el pod de Falco deje de reportar debe generar una alerta en el sistema que recibe sus salidas (una alarma por ausencia de latido: si no llega telemetría en X minutos, avisa). Falco no se protege a sí mismo de root en el host.
7. Puesta en marcha en un laboratorio
Para practicar sin arriesgar nada, un clúster local (kind, minikube o k3d) y el chart oficial bastan. El objetivo del laboratorio es doble: ver una alerta de principio a fin y aprender a afinar una regla ruidosa.
# 1) Instalar Falco con salidas a stdout en JSON y la UI (laboratorio) helm repo add falcosecurity https://falcosecurity.github.io/charts helm install falco falcosecurity/falco \ --namespace falco --create-namespace \ --set driver.kind=modern_ebpf \ --set falcosidekick.enabled=true \ --set falcosidekick.webui.enabled=true # 2) Ver el flujo de alertas kubectl logs -n falco -l app.kubernetes.io/name=falco -c falco -f # 3) Disparar la regla 'Terminal shell in container' a propósito kubectl run demo --image=nginx --restart=Never kubectl exec -it demo -- bash # <-- Falco debe emitir un NOTICE # 4) Validar la sintaxis de un fichero de reglas propio antes de aplicarlo falco --validate /etc/falco/rules.d/local_rules.yaml
A partir de ahí, ejercicios útiles: montar `/var/run/docker.sock` en un pod y ver qué reglas saltan; escribir en `/etc/` desde un contenedor; lanzar `curl` a una IP externa desde un pod que no debería; y luego escribir la excepción que silencia el caso legítimo sin silenciar el malicioso. El proyecto event-generator de falcosecurity automatiza la generación de actividad sospechosa controlada para probar el despliegue.
Marco legal y ámbito de uso
Falco procesa datos que reflejan la actividad de personas: comandos que ejecuta cada usuario dentro de los contenedores, sesiones, accesos a ficheros, conexiones de red. Desplegarlo sobre sistemas corporativos exige cumplir la normativa de protección de datos y laboral: información previa y por escrito a la plantilla sobre qué se registra y con qué finalidad, proporcionalidad, minimización y plazos de conservación (RGPD y LOPDGDD, y el art. 20.3 del Estatuto de los Trabajadores en cuanto al control empresarial). El acceso o la interceptación no autorizados de sistemas y datos ajenos están tipificados en los arts. 197 bis, 197 ter y 264 del Código Penal. Todo lo de este artículo se aplica a infraestructura propia o a una organización que te ha autorizado por escrito a monitorizarla; las pruebas se hacen en un laboratorio aislado.
Conclusión
Falco cubre un hueco que las capas previas — imagen endurecida, escaneo, control de admisión — dejan abierto por diseño: lo que un contenedor o un host hace después de arrancar. eBPF es lo que hace esa vigilancia asumible: un verificador del kernel garantiza que el programa de observación no puede tumbar la máquina, y CO-RE lo convierte en un binario que se despliega sin compilar nada en cada nodo.
El trabajo real no está en instalarlo — el chart lo pone en marcha en minutos — sino en el afinado: describir qué es normal en tu entorno hasta que cada alerta que llega merezca una mirada. Y en tener claro el perímetro de la herramienta: Falco detecta y avisa. Cortar la acción en el momento es trabajo de Tetragon, KubeArmor o un motor de respuesta como Talon; impedir que el pod inseguro llegue a crearse, del control de admisión. Falco es la pieza que te dice que algo pasó, con contexto suficiente para actuar.
Referencias y recursos
falco.org — documentación oficial, y falco.org/docs/rules para la sintaxis del motor de reglas · github.com/falcosecurity/rules — el conjunto de reglas de referencia, versionado · github.com/falcosecurity/falcosidekick y falcosecurity/falco-talon — salidas y respuesta · github.com/falcosecurity/event-generator — generador de actividad sospechosa para probar el despliegue · ebpf.io — introducción a eBPF, el verificador y CO-RE; y los proyectos Tetragon y Tracee · Nuestros artículos sobre seguridad en Docker, seguridad en Kubernetes, auditd, osquery y cómo funciona un EDR — el contexto donde encaja Falco
unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)

