eBPF + IA + Kubernetes: Detección de Amenazas en Tiempo Real para Cloud Native
Una arquitectura de referencia que combina eBPF para observabilidad a nivel kernel con modelos de ML para detectar ataques DDoS, escaneos de puertos y anomalías en Kubernetes con bajo impacto en el rendimiento.
Sobre este artículo: Lo que sigue es un diseño de referencia, no la descripción de un producto terminado ni de un sistema que haya medido en producción. Describo cómo encajarían las piezas (eBPF, un pipeline de eventos y un motor de detección con ML) para que sirva como punto de partida a quien quiera construir algo similar sobre herramientas open source existentes como Cilium, Tetragon o Falco. Donde menciono cifras, son órdenes de magnitud publicados por la industria, no mediciones propias.
¿Cómo detectar ataques cibernéticos en tiempo real sin impactar el rendimiento?
Imagina un sistema que puede observar cada paquete de red que pasa por tu infraestructura, detectar patrones sospechosos como ataques DDoS o escaneos de puertos, y alertarte en segundos, todo ello sin degradar de forma apreciable el rendimiento de tus nodos. Eso es lo que hace posible combinar eBPF (observabilidad a nivel del kernel) con modelos de Machine Learning para el análisis.
El problema que se quiere resolver
Los sistemas tradicionales de seguridad enfrentan un dilema:
- Monitoreo superficial: Rápido pero pierde detalles críticos
- Análisis profundo: Detecta todo pero ralentiza el sistema
La arquitectura que propongo intenta romper ese compromiso usando eBPF para capturar datos a velocidad del kernel, y modelos de ML para detectar tanto amenazas conocidas como anomalías nuevas.
¿Qué es eBPF?
eBPF (Extended Berkeley Packet Filter) es una tecnología que permite ejecutar código verificado dentro del kernel de Linux de forma segura, sin cargar módulos ni recompilar. Esto significa que podemos:
- Observar el tráfico de red sin copiar cada paquete al espacio de usuario
- Inspeccionar llamadas del sistema en tiempo real
- Rastrear el comportamiento de procesos y contenedores
- Hacerlo con un overhead bajo, mucho menor que el de un sidecar o un agente que capture paquetes desde userspace
Arquitectura propuesta
┌─────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ ┌─────────┐ │ │ ┌─────────┐ │ │ ┌─────────┐ │ │
│ │ │eBPF Agt │ │ │ │eBPF Agt │ │ │ │eBPF Agt │ │ │
│ │ └────┬────┘ │ │ └────┬────┘ │ │ └────┬────┘ │ │
│ └──────┼──────┘ └──────┼──────┘ └──────┼──────┘ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Collector & Aggregator │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │ Metrics │ │ Events │ │ Traces │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬───────┘ │ │
│ └─────────┼────────────────┼────────────────┼──────────┘ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ ML Detection Engine │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │
│ │ │ Anomaly │ │ Pattern │ │ Threat │ │ │
│ │ │ Detection │ │ Matching │ │ Scoring │ │ │
│ │ └─────────────┘ └─────────────┘ └──────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Alert Manager │ │
│ │ Slack │ PagerDuty │ Email │ Webhook │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Los tres componentes tienen responsabilidades claras:
- Agente eBPF (DaemonSet): programas eBPF enganchados a tracepoints de red (XDP/TC) y de syscalls. Su única tarea es extraer características ligeras (tuplas de conexión, tasas, flags TCP, syscalls por proceso) y enviarlas por un ring buffer al espacio de usuario. Aquí no hay ML: todo lo que corre en el kernel debe ser trivialmente barato.
- Collector & Aggregator: recibe eventos de todos los nodos, los enriquece con metadata de Kubernetes (pod, namespace, labels) y los agrega en ventanas temporales. En la práctica esto es un pipeline de streaming (Kafka, NATS o el propio exportador de Hubble/Tetragon) más un almacén de series temporales.
- ML Detection Engine: consume las ventanas agregadas y aplica un enfoque híbrido de reglas más modelos. Corre fuera del kernel y puede escalar de forma independiente.
Tipos de amenazas que cubre el diseño
Ataques de red
- DDoS: Volúmenes anormales de tráfico hacia servicios específicos
- Port Scanning: Intentos de mapeo de puertos abiertos desde un mismo origen
- SYN Floods: Patrones de inundación TCP (SYN sin ACK completado)
Comportamiento anómalo
- Lateral Movement: Comunicación inusual entre pods que normalmente no se hablan
- Data Exfiltration: Transferencias de datos hacia destinos externos fuera del patrón habitual
- Privilege Escalation: Cambios en permisos y capacidades de procesos
Amenazas de contenedores
- Container Escape: Intentos de salir del namespace del contenedor
- Crypto Mining: Uso intensivo de CPU con patrones de conexión típicos de pools de minería
- Rootkit Activity: Syscalls asociadas a manipulación del kernel
Varias de estas detecciones ya las ofrecen como reglas herramientas maduras como Falco o Tetragon. El valor añadido del ML está en la parte de anomalías, donde no existe una firma conocida.
Motor de detección: enfoque híbrido
El pseudocódigo siguiente ilustra la lógica, no es una implementación:
# Pseudocódigo del pipeline de detección
class ThreatDetector:
def __init__(self):
self.anomaly_model = IsolationForest() # Detección de anomalías
self.classifier = RandomForest() # Clasificación de amenazas
self.pattern_db = ThreatPatternDB() # Patrones conocidos (reglas)
def analyze(self, network_event):
# 1. Verificar patrones conocidos (rápido, alta confianza)
if match := self.pattern_db.match(network_event):
return Alert(type=match.threat_type, confidence=0.95)
# 2. Detectar anomalías (sin firma previa)
anomaly_score = self.anomaly_model.score(network_event)
if anomaly_score > THRESHOLD:
# 3. Clasificar tipo de amenaza
threat_type = self.classifier.predict(network_event)
return Alert(type=threat_type, confidence=anomaly_score)
return None
Por qué híbrido: las reglas dan explicabilidad y cero falsos positivos sobre lo que ya conoces; el modelo de anomalías cubre lo que no conoces, a cambio de más falsos positivos y menos explicabilidad. En un SOC real, la mayor parte del trabajo está en calibrar ese THRESHOLD y en entrenar el modelo con tráfico normal de tu clúster, no con datasets públicos.
Despliegue del agente en Kubernetes
Un agente eBPF necesita privilegios elevados y acceso al kernel del host. El manifiesto de referencia sería:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ebpf-agent
namespace: security-monitoring
spec:
selector:
matchLabels:
app: ebpf-agent
template:
metadata:
labels:
app: ebpf-agent
spec:
hostNetwork: true
hostPID: true
containers:
- name: agent
image: registry.example.com/ebpf-security-agent:0.1.0
securityContext:
privileged: true
volumeMounts:
- name: sys
mountPath: /sys
readOnly: true
- name: debug
mountPath: /sys/kernel/debug
volumes:
- name: sys
hostPath:
path: /sys
- name: debug
hostPath:
path: /sys/kernel/debug
En OpenShift esto implica además un SecurityContextConstraints dedicado (o privileged) y una ServiceAccount con ese SCC asignado. Es exactamente lo que hacen los operadores de Cilium, Tetragon y Falco, y es la razón por la que este componente debe tratarse como parte de la plataforma, no como una carga de trabajo más.
Consideraciones de rendimiento
No incluyo una tabla de métricas porque no he medido este diseño de extremo a extremo y cualquier número que pusiera sería inventado. Lo que sí se puede afirmar con base en lo que publican los proyectos del ecosistema:
- Overhead en el kernel: los programas eBPF bien escritos añaden un coste por paquete muy bajo. Los proyectos que los usan en producción (Cilium, Tetragon, Falco con su driver eBPF moderno) reportan habitualmente overheads de CPU en el rango de un dígito bajo por ciento en cargas normales. Trátalo como una estimación de la industria y mídelo en tu entorno: depende de cuántos hooks actives y de cuánto trabajo hagas por evento.
- Dónde se va el coste real: no en el kernel, sino en el userspace. El collector, el enriquecimiento con metadata de Kubernetes y sobre todo la inferencia del modelo son lo que consume CPU y memoria. Por eso el diseño separa estos componentes y los saca del nodo cuando es posible.
- Latencia de detección: la determina el tamaño de la ventana de agregación, no eBPF. Ventanas de segundos dan detección rápida pero más ruido; ventanas de minutos son más estables pero llegan tarde para un SYN flood.
- Memoria en el nodo: los mapas eBPF tienen tamaño fijo y se reservan al cargar el programa. Dimensiónalos según el número de conexiones concurrentes esperado, no según el máximo teórico.
Cómo llevarlo a la práctica
Si quieres construir algo así, mi recomendación es no escribir los programas eBPF desde cero:
- Cilium + Hubble como fuente de eventos de red y flujos L3/L4/L7 con identidad de Kubernetes ya resuelta
- Tetragon para eventos de procesos, syscalls y ejecución dentro de contenedores
- Falco como motor de reglas para la capa de patrones conocidos, exportando a un bus de eventos
- Un consumidor propio para la capa de anomalías, entrenado con el tráfico normal de tu clúster
- Grafana para visualización y Alertmanager para el enrutado de alertas
Extensiones naturales del diseño serían la generación automática de CiliumNetworkPolicy a partir de detecciones confirmadas y la exportación en formato STIX para correlación con threat intelligence externa.

Comentarios(0)