Seguridad en Tiempo de Ejecución con Falco

Falco es un motor de detección de amenazas en tiempo de ejecución de código abierto desarrollado por Sysdig que monitoriza las llamadas al sistema (syscalls) en el kernel Linux para detectar comportamientos anómalos en contenedores y Kubernetes, como accesos no autorizados a archivos, ejecución de shells inesperados o cambios en privilegios. A diferencia de los escáneres estáticos, Falco detecta amenazas activas durante la ejecución, completando la postura de seguridad de contenedores. Esta guía cubre el despliegue de Falco y la configuración de reglas y alertas.

Requisitos Previos

  • Linux con kernel 4.14+ (Ubuntu 22.04, Debian 12, CentOS 9 o Rocky 9)
  • Acceso al kernel (driver de kernel de Falco o eBPF)
  • Mínimo 2 GB de RAM y 1 CPU
  • Para Kubernetes: Helm 3 instalado
  • Cabeceras del kernel instaladas (para compilar el módulo)

Instalación de Falco

# Ubuntu/Debian: instalar cabeceras del kernel primero
apt-get install -y linux-headers-$(uname -r)

# Añadir el repositorio de Falco
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | \
  gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] \
  https://download.falco.org/packages/deb stable main" | \
  tee /etc/apt/sources.list.d/falcosecurity.list

apt-get update && apt-get install -y falco

# CentOS/Rocky Linux
rpm --import https://falco.org/repo/falcosecurity-packages.asc
cat > /etc/yum.repos.d/falcosecurity.repo << 'EOF'
[falcosecurity]
name=Falco repository
baseurl=https://download.falco.org/packages/rpm/stable/$basearch
enabled=1
gpgcheck=1
gpgkey=https://falco.org/repo/falcosecurity-packages.asc
EOF
dnf install -y falco

# Durante la instalación, elegir el driver:
# 1. kmod (módulo de kernel) - más compatible
# 2. ebpf - más moderno y sin reinicio necesario
# 3. modern-ebpf - solo kernels recientes (5.8+)

# Configurar el driver eBPF (si se eligió durante la instalación)
falco-driver-loader bpf

# Verificar la instalación
falco --version

# Iniciar el servicio
systemctl enable --now falco
systemctl status falco

Configuración de Reglas

Falco viene con un conjunto de reglas predeterminadas en /etc/falco/falco_rules.yaml:

# Ver las reglas activas
cat /etc/falco/falco_rules.yaml | grep "^- rule:" | head -20

# Estructura de una regla Falco
# - rule: Nombre de la regla
#   desc: Descripción de lo que detecta
#   condition: Condición de detección (basada en syscalls y campos)
#   output: Mensaje de alerta con campos de contexto
#   priority: Criticidad (DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL)
#   tags: Categorías de la regla

# Configuración principal de Falco
cat /etc/falco/falco.yaml

Configurar la salida de alertas en /etc/falco/falco.yaml:

# Editar la sección de outputs en falco.yaml
# Las alertas pueden enviarse a: stdout, archivo, syslog, programa, HTTP

cat > /etc/falco/falco.yaml << 'EOF'
# Configuración de reglas
rules_file:
  - /etc/falco/falco_rules.yaml
  - /etc/falco/falco_rules.local.yaml  # Reglas personalizadas (sobrescriben/añaden)

# Nivel mínimo de prioridad para generar alertas
priority: warning

# Búfer del log para alta velocidad de eventos
buffered_outputs: false

# Salida a la consola/stdout
stdout_output:
  enabled: true

# Salida a archivo de log
file_output:
  enabled: true
  keep_alive: false
  filename: /var/log/falco/falco.log

# Salida a syslog
syslog_output:
  enabled: false

# Salida a programa externo (para alertas personalizadas)
program_output:
  enabled: false
  keep_alive: false
  program: "jq '{text: .output}' | curl -d @- -X POST https://hooks.slack.com/services/XXX/YYY/ZZZ"

# Salida HTTP/webhook
http_output:
  enabled: false
  url: http://localhost:2801/

# Estadísticas de rendimiento
metrics:
  enabled: true
  interval: 1h
EOF

Reglas Personalizadas

Crea reglas personalizadas en /etc/falco/falco_rules.local.yaml:

cat > /etc/falco/falco_rules.local.yaml << 'EOF'
# Reglas personalizadas locales - sobrescriben o complementan las predeterminadas

# DESACTIVAR reglas predeterminadas demasiado ruidosas
- rule: Read sensitive file untrusted
  condition: >
    sensitive_files and open_read
    and not proc.name in (trusted_programs)
    and not container.image.repository in (allowed_images)
  enabled: false  # Demasiados falsos positivos en nuestro entorno

# NUEVA REGLA: Detectar acceso a datos de configuración sensibles
- rule: Acceso a archivos de credenciales en contenedor
  desc: >
    Detecta cuando un proceso dentro de un contenedor accede a archivos
    que típicamente contienen credenciales o secretos
  condition: >
    open_read and container
    and (fd.name glob "/etc/passwd" or
         fd.name glob "/etc/shadow" or
         fd.name glob "*.pem" or
         fd.name glob "*.key" or
         fd.name glob "*id_rsa*")
    and not proc.name in (sshd, sudo, gpg)
  output: >
    Acceso a archivo de credenciales detectado en contenedor
    (usuario=%user.name proceso=%proc.name archivo=%fd.name
     contenedor=%container.name imagen=%container.image.repository)
  priority: WARNING
  tags: [security, credenciales]

# NUEVA REGLA: Shell inesperado en contenedor de producción
- rule: Shell ejecutado en contenedor de aplicacion
  desc: Detecta la ejecución de shells interactivos en contenedores de aplicación
  condition: >
    spawned_process and container
    and shell_procs
    and not container.image.repository in (
      "docker.io/library/busybox",
      "alpine"
    )
    and proc.pname != "runc"
  output: >
    Shell ejecutado en contenedor de aplicación (POSIBLE INTRUSIÓN)
    (usuario=%user.name shell=%proc.name contenedor=%container.name
     imagen=%container.image.repository pid=%proc.pid)
  priority: ERROR
  tags: [security, shell, intrusion]

# NUEVA REGLA: Descarga de herramientas en contenedor
- rule: Herramienta de red descargada en contenedor
  desc: Detecta la descarga de herramientas potencialmente peligrosas
  condition: >
    spawned_process and container
    and proc.name in (curl, wget, nc, ncat, nmap, masscan)
    and not container.image.repository in (allowed_network_tools_images)
  output: >
    Herramienta de red ejecutada en contenedor
    (herramienta=%proc.name contenedor=%container.name args=%proc.args)
  priority: WARNING
  tags: [security, network, tools]
EOF

# Validar las reglas personalizadas
falco --validate /etc/falco/falco_rules.local.yaml

# Recargar Falco con las nuevas reglas
systemctl restart falco

# Verificar que las reglas se cargaron
falco --list | grep "Acceso a archivos"

Sistema de Alertas

# Ver las alertas en tiempo real
journalctl -u falco -f

# Formato JSON de las alertas (útil para parsear)
# Las alertas tienen este formato:
# {
#   "output": "18:30:00.000000000: Warning Shell ejecutado...",
#   "priority": "Warning",
#   "rule": "Shell ejecutado en contenedor de aplicacion",
#   "time": "2024-01-15T18:30:00.000000000Z",
#   "output_fields": {
#     "container.name": "webapp",
#     "proc.name": "bash",
#     ...
#   }
# }

# Habilitar salida en formato JSON en falco.yaml:
# json_output: true
# json_include_output_property: true

# Contar alertas por tipo en las últimas 24 horas
journalctl -u falco --since "24 hours ago" | \
  grep "^.*Warning\|Error\|Critical" | \
  awk '{print $NF}' | sort | uniq -c | sort -rn

Integración con Slack y Webhooks

# Script de reenvío de alertas Falco a Slack
cat > /usr/local/bin/falco-to-slack.sh << 'SCRIPT'
#!/bin/bash
SLACK_WEBHOOK_URL="${SLACK_WEBHOOK_URL:?Variable no definida}"
MIN_PRIORITY="${MIN_PRIORITY:-WARNING}"  # Solo alertar para WARNING o superior

# Leer la alerta de la entrada estándar (enviada por Falco)
ALERT=$(cat)

# Extraer campos de la alerta JSON
PRIORITY=$(echo "$ALERT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('priority','UNKNOWN'))")
RULE=$(echo "$ALERT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('rule','Unknown rule'))")
OUTPUT=$(echo "$ALERT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('output','No output'))")

# Determinar el color del mensaje según la prioridad
case "$PRIORITY" in
    CRITICAL|ERROR) COLOR="#FF0000" ;;  # Rojo
    WARNING)        COLOR="#FFA500" ;;  # Naranja
    NOTICE)         COLOR="#FFFF00" ;;  # Amarillo
    *)              COLOR="#808080" ;;  # Gris
esac

# Enviar mensaje a Slack
curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d "{
    \"attachments\": [{
      \"color\": \"$COLOR\",
      \"title\": \"Alerta de Seguridad Falco: $RULE\",
      \"text\": \"\`\`\`$OUTPUT\`\`\`\",
      \"footer\": \"Falco Runtime Security\",
      \"ts\": $(date +%s)
    }]
  }"
SCRIPT
chmod +x /usr/local/bin/falco-to-slack.sh

# Configurar Falco para usar el script (en falco.yaml):
# program_output:
#   enabled: true
#   keep_alive: false
#   program: "SLACK_WEBHOOK_URL=https://hooks.slack.com/services/XXX /usr/local/bin/falco-to-slack.sh"

Despliegue en Kubernetes

# Instalar Falco en Kubernetes con Helm
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

# Instalar Falco como DaemonSet (se ejecuta en cada nodo)
helm install falco falcosecurity/falco \
  --namespace falco \
  --create-namespace \
  --set driver.kind=ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/XXX" \
  --set falcosidekick.config.slack.minimumpriority="warning"

# Verificar el despliegue
kubectl get pods -n falco
kubectl logs -n falco -l app.kubernetes.io/name=falco -f

# Ver las alertas de Falco en Kubernetes
kubectl logs -n falco -l app.kubernetes.io/name=falco | \
  grep -E "Warning|Error|Critical"

# Desplegar reglas personalizadas como ConfigMap
kubectl create configmap falco-custom-rules \
  --from-file=custom-rules.yaml=/etc/falco/falco_rules.local.yaml \
  -n falco

# Referenciar el ConfigMap en el Helm chart
helm upgrade falco falcosecurity/falco \
  --namespace falco \
  --set customRules."custom-rules\.yaml"="$(cat /etc/falco/falco_rules.local.yaml)"

Respuesta a Incidentes

# Script de respuesta automática a alertas críticas de Falco
cat > /usr/local/bin/falco-incident-response.sh << 'SCRIPT'
#!/bin/bash
# Respuesta automática: aislar contenedor comprometido

ALERT=$(cat)
CONTAINER_ID=$(echo "$ALERT" | python3 -c "
import sys, json
d = json.load(sys.stdin)
fields = d.get('output_fields', {})
print(fields.get('container.id', ''))
")

if [ -z "$CONTAINER_ID" ] || [ "$CONTAINER_ID" = "<NA>" ]; then
    exit 0
fi

PRIORITY=$(echo "$ALERT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('priority',''))")

# Solo actuar en alertas de máxima criticidad
if [ "$PRIORITY" = "CRITICAL" ] || [ "$PRIORITY" = "ERROR" ]; then
    echo "[$(date)] RESPUESTA: Pausando contenedor comprometido $CONTAINER_ID"
    
    # Pausar el contenedor (congelar sin eliminar, preservar evidencia)
    docker pause "$CONTAINER_ID" 2>/dev/null
    
    # Capturar el estado del proceso para análisis forense
    docker top "$CONTAINER_ID" > /var/log/falco/incident-${CONTAINER_ID}-procs.txt 2>/dev/null
    docker inspect "$CONTAINER_ID" > /var/log/falco/incident-${CONTAINER_ID}-inspect.json 2>/dev/null
    
    # Enviar notificación de incidente
    logger -t falco-incident "INCIDENTE: Contenedor $CONTAINER_ID aislado por alerta de seguridad"
fi
SCRIPT
chmod +x /usr/local/bin/falco-incident-response.sh

Solución de Problemas

# Ver estado del servicio y logs
systemctl status falco
journalctl -u falco -n 50 --no-pager

# Verificar que el driver está cargado
lsmod | grep falco    # Para módulo de kernel
# o para eBPF:
ls -la /sys/kernel/debug/tracing/events/ | grep falco

# Probar que Falco detecta eventos (generar un evento de prueba)
# En una terminal, iniciar Falco con verbose:
falco --list  # Listar todas las reglas activas

# En otra terminal, ejecutar algo que debería generar alerta:
docker run --rm -it alpine sh -c "cat /etc/shadow"
# Debe aparecer alerta en los logs de Falco

# Validar archivo de reglas
falco --validate /etc/falco/falco_rules.yaml
falco --validate /etc/falco/falco_rules.local.yaml

# Ver las macros y listas disponibles en las reglas
falco --list -a | grep macro

# Problema de rendimiento: Falco consume demasiada CPU
# Revisar las reglas más activas
falco --stats-interval 5 2>&1 | grep "events/s"

Conclusión

Falco proporciona visibilidad en tiempo real sobre el comportamiento de contenedores y workloads Kubernetes, detectando intrusiones y comportamientos anómalos que los escáneres de vulnerabilidades estáticos no pueden identificar. La combinación de reglas predefinidas mantenidas por la comunidad con reglas personalizadas adaptadas a tu infraestructura, junto con la integración con sistemas de alerta como Slack, convierte a Falco en una pieza esencial de cualquier estrategia de seguridad en tiempo de ejecución para entornos de contenedores en producción.