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:
- Auditoría de Kubernetes con Wazuh
- Grafana con Wazuh Lite
- Control parental con Wazuh, AdGuard y Telegram
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,
timeoutpor 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 ensoc/reports/AAAA-MM-DD/…mdy lo envía a Telegram.send_telegram.sh: envía mensajes usando el token del bot (en mi casoraam_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.yamlenruta alertaswarningocriticala 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_idnumé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>.mdy 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-proxyen el namespacemonitoringy recoge hasta 5 alertas recientes con nivel 7 o superior. - CVEs (Trivy):
- Toma imágenes de
docker pscon puertos publicados a0.0.0.0o::(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
CRITICALyHIGH, con--ignore-unfixedpor defecto. - Limita número de imágenes y tiempo por imagen para no dejar el host colgado en un bucle de escaneo.
- Toma imágenes de
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):
| Variable | Rol típico |
|---|---|
SOC_TRIVY_SEVERITY | Por defecto CRITICAL,HIGH |
SOC_TRIVY_IGNORE_UNFIXED | Por defecto true (menos ruido si no hay parche) |
SOC_TRIVY_MAX_IMAGES | Limita cuántas imágenes se escanean |
SOC_TRIVY_IMAGE_TIMEOUT_SECONDS | Evita 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=warningocriticala 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 namespacemonitoring.
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:ImagePullBackOffpor 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/ysoc/inventory/exposure_map.mdal día. - Usar
soc/posture/exposure-checklist.mdde forma recurrente. - Afinar reglas locales en Wazuh con criterio (ver
soc/wazuh/README.mden el repo). - Ajustar
SOC_TRIVY_IGNORE_UNFIXEDo 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.
