Guilgo Blog

Notas de mi quehacer diario con las técnologias.

Un SOC en casa (homelab) no es un teatro de alertas. Debe darte señal cuando importa, sin ruido.

Este post documenta cómo monto en mi homelab un agente SOC proactivo con cuatro principios:

  • Detección real: pods problemáticos en Kubernetes.
  • Señal del SIEM: alertas de nivel ≥ 7 desde Wazuh (vía proxy en el clúster).
  • Superficie de ataque: CVEs CRITICAL y HIGH solo en imágenes expuestas.
  • Salida controlada: Telegram solo con hallazgos o degradación de señal, y deduplicación para no repetir lo mismo en bucle.

Todo el diseño vive en soc/ y actúa como fuente de verdad del repositorio.

Esto no es otro dashboard. Es un bucle operativo:

clúster → SIEM → runtime → canal → acción

Cada iteración produce un estado claro: OK, degradado o requiere acción.

Si ya usas Wazuh en el lab, conecta con lo que ya hay en el blog:

Aquí el cambio es de enfoque: menos alertas, más contexto accionable.

Qué aporta frente a lo típico

  • Escaneo acotado → solo lo expuesto.
  • Señal accionable → severidad y política explícita, sin “ruido diferido”.
  • Deduplicación → el canal no se convierte en un bucle del mismo problema.
  • Degradación visible → si falta kubectl, Trivy o conectividad, se reporta para no confundir “silencio” con “OK”.

Modelo mental

  • Tiempo acotado: cada ejecución tiene límites (número de imágenes, timeout por escaneo) para no dejar el host colgado.
  • Prioridad (orientativa): disponibilidad > señal del SIEM > CVEs.
  • Canal único: Telegram como inbox, no como stream continuo: si entra, algo merece mirada o cierre con acción.

Qué vas a construir (visión rápida)

  • Scripts en soc/scripts/:
    • soc_proactive.sh: genera el informe, lo escribe en soc/reports/AAAA-MM-DD/…md y lo envía a Telegram.
    • send_telegram.sh: envía mensajes usando el token del bot (en mi caso raam_bot) leído desde un Secret de Kubernetes.
    • trivy_scan_image.sh: utilidad para escanear una imagen concreta a demanda.
  • Cron en soc/cron/soc-proactive.cron: ejecuta el chequeo cada 6 horas.
  • Integración opcional “event-driven” con Alertmanager: soc/k8s/alertmanagerconfig-soc-inbox.yaml enruta alertas warning o critical a un webhook de n8n.

Importante: no es un producto comercial; es un ensamblaje honesto sobre lo que tú operas. Self-hosted y Telegram implican confianza en tu propia configuración y en el canal, no “más seguro” por magia. El objetivo es visibilidad accionable con límites de tiempo y ruido.


Requisitos previos (sin esto no funciona)

Host donde se ejecuta el SOC

El diseño asume un host central (en mi setup, raam) con:

  • Kubernetes o k3s accesible con kubectl.
  • Docker accesible (docker ps).
  • Trivy instalado para CVEs.
  • curl para Telegram y consultas HTTP.

El script valida dependencias y, si faltan, lo deja constancia como degradación de señal (no te quedas creyendo que “todo está verde” si el escáner no corre).

Telegram

  • Un canal tipo inbox SOC donde publicar.
  • El bot es administrador del canal.
  • El chat_id numérico del canal (formato habitual -100…).

La guía de postura y cómo obtener el chat_id sin liar el webhook está en soc/posture/telegram.md dentro del repo.


Estructura del repositorio (lo mínimo)

  • soc/README.md: objetivo y mapa de carpetas.
  • soc/runbooks/: síntoma → verificación → causa → acción.
  • soc/posture/: checklist de exposición (“no abrir por accidente”).
  • soc/vuln/: política de CVEs (umbral, cadencia, qué no entra al canal).
  • soc/reports/: salida en Markdown de cada ejecución con hallazgos.

Paso 1 — Configura el destino de Telegram (chat_id)

soc_proactive.sh usa por defecto un placeholder (SOC_CHAT_ID="-TUCANAL" o similar). Para cambiarlo sin tocar el script, exporta SOC_CHAT_ID en el entorno del cron (o ajusta el valor por defecto si prefieres centralizarlo en el repo).

Para obtener el chat_id sin romper el webhook del bot, el runbook en soc/posture/telegram.md sugiere reenviar un mensaje del canal al bot en privado y leer el chat_id desde los logs del bot.


Paso 2 — Asegura que el token del bot existe en Kubernetes

El envío usa send_telegram.sh, que lee el token desde:

  • Secret en el namespace adecuado (ej. raam/raam-bot-secret)
  • clave: BOT_TOKEN

Si el secret no existe o no es legible, el envío falla y el script termina con error: mejor fallar claro que publicar en silencio a un destino imposible.


Paso 3 — Primera verificación a mano

Desde la raíz del repositorio donde tengas soc/:

bash soc/scripts/soc_proactive.sh

Comportamiento esperado:

  • Si no hay hallazgos, no imprime nada y termina con código 0.
  • Si hay hallazgos, genera un fichero en soc/reports/AAAA-MM-DD/soc-proactive-<timestamp>.md y envía un mensaje a Telegram.

Qué comprueba el script

  • Kubernetes: lista pods en estados indeseados: Pending, CrashLoopBackOff, ImagePullBackOff, ErrImagePull, Error, Failed.
  • Wazuh: consulta el servicio wazuh-alerts-proxy en el namespace monitoring y recoge hasta 5 alertas recientes con nivel 7 o superior.
  • CVEs (Trivy):
    • Toma imágenes de docker ps con puertos publicados a 0.0.0.0 o :: (exposición real hacia afuera).
    • Prioriza imágenes de “alto valor” (n8n, Wazuh, AdGuard, servicios web, etc. según tu lógica en el script).
    • Escanea solo CRITICAL y HIGH, con --ignore-unfixed por defecto.
    • Limita número de imágenes y tiempo por imagen para no dejar el host colgado en un bucle de escaneo.

Esto está alineado con la idea de priorizar parches cuando Wazuh marca CVEs: el canal Telegram no es el sitio para repasar cada MEDIUM de una base interna, sino lo que justifica acción o awareness en tu perimetro expuesto.


Paso 4 — Cron en el host (cada 6 horas)

En el host que ejecuta los scripts, como root, instala el fichero de cron del repo (ajusta la ruta a tu clon del repositorio):

cp /ruta/a/tu/repo/soc/cron/soc-proactive.cron /etc/cron.d/soc-proactive
chmod 644 /etc/cron.d/soc-proactive

El cron lanza bash soc/scripts/soc_proactive.sh y puede volcar trazas en soc/.soc_proactive.log (según el contenido de soc/cron/soc-proactive.cron).


Paso 5 — Ajustar el ruido (política de CVEs)

La política detallada está en soc/vuln/POLITICA-CVE.md. Resumen de intención:

  • Tratar CRITICAL con seriedad cuando el servicio está expuesto o es de alto valor.
  • Tratar HIGH cuando el contexto (exposición o valor) lo hace plausible que deban entrar al canal.
  • Dejar MED/LOW para revisión periódica, no para spam diario en Telegram.

Variables útiles sin tocar el código (valores orientativos):

VariableRol típico
SOC_TRIVY_SEVERITYPor defecto CRITICAL,HIGH
SOC_TRIVY_IGNORE_UNFIXEDPor defecto true (menos ruido si no hay parche)
SOC_TRIVY_MAX_IMAGESLimita cuántas imágenes se escanean
SOC_TRIVY_IMAGE_TIMEOUT_SECONDSEvita que una imagen bloquee todo el lote

Paso 6 (opcional) — Alertmanager → n8n

Además del chequeo por cron, puedes enrutar alertas del clúster hacia el inbox SOC. En el repo hay un AlertmanagerConfig:

  • Fichero: soc/k8s/alertmanagerconfig-soc-inbox.yaml
  • Enruta severity=warning o critical a un receiver webhook.
  • Suele ignorar Watchdog (alerta de “sigo vivo” que no aporta al SOC humano).
  • Apunta, en mi homelab, a n8n en el host (no en Kubernetes), por ejemplo http://10.5.5.3:5678/webhook/soc-alertmanager, accesible desde el namespace monitoring.

Matices de red: si n8n corre en Docker en el host, la IP y el firewall deben permitir desde el pod de Alertmanager hacia esa URL. Detalle en soc/k8s/README.md del repositorio.


Cómo leer un informe

Ejemplo de nombre: soc/reports/2026-04-26/soc-proactive-20260426T100210Z.md.

  • Bloque Kubernetes: si no está “OK”, prioriza disponibilidad y estabilidad antes de CVEs en abstracto.
  • Bloque Wazuh: si aparecen líneas [L7] (o el nivel que uses) con host y regla, son señales del SIEM que merecen triage, no pánico automático.
  • CVEs (Docker expuesto): si hay tabla Trivy, prioriza por servicio expuesto, severidad (CRITICAL primero) y si existe fix (actualizar imagen/tag y redeploy).

Runbooks: no improvisar cuando salta algo

  • soc/runbooks/kubejobfailed.md: jobs fallidos.
  • soc/runbooks/docker-hub-mirror.md: ImagePullBackOff por límites o bloqueos a Docker Hub.
  • soc/runbooks/wazuh-jwt-renewer.md: JWT caducado o dashboards sin datos.
  • soc/runbooks/php-apache-ataque-web.md: señales típicas de abuso web.
  • soc/runbooks/n8n-exposicion-cve.md: foco n8n (alto valor en muchos homelabs).

Extensiones cuando ya esté estable

  • Mantener soc/inventory/ y soc/inventory/exposure_map.md al día.
  • Usar soc/posture/exposure-checklist.md de forma recurrente.
  • Afinar reglas locales en Wazuh con criterio (ver soc/wazuh/README.md en el repo).
  • Ajustar SOC_TRIVY_IGNORE_UNFIXED o el número de imágenes para bajar falsos positivos sin apagar el radar.

Comandos rápidos (copiar y pegar)

Escanear una imagen concreta:

bash soc/scripts/trivy_scan_image.sh docker.n8n.io/n8nio/n8n:2.16.0

Probar el canal a mano (sustituye el chat_id real):

bash soc/scripts/send_telegram.sh "-100xxxxxxxxxx" "Prueba SOC: mensaje de verificación"

Ejecutar el chequeo proactivo:

bash soc/scripts/soc_proactive.sh

Cierre: un SOC proactivo en homelab no sustituye a un equipo 24/7, pero sí te da ritmo: cron, señal del SIEM, escaneo acotado y un inbox con reglas claras. Con runbooks y ruido controlado, el siguiente incidente empieza con hechos en Markdown, no con intuición a las tres de la mañana.