Despliegue de SOC

Guía: Despliegue de Wazuh SIEM en Docker con Hardening Avanzado

Tener una visibilidad de lo que ocurre en tu infraestructura no es un lujo, es una obligación. Este proyecto documenta el despliegue de mi Centro de Operaciones de Seguridad (SOC) con Wazuh. Vamos a montar una arquitectura en Docker endurecida (hardened), con redes aisladas, gestión de certificados privados y erradicación de contraseñas por defecto.

¿Qué es Wazuh y por qué lo necesitas?

Wazuh es una plataforma de seguridad de código abierto que unifica SIEM (Gestión de Eventos e Información de Seguridad) y XDR (Detección y Respuesta Extendidas).

  • ¿Para qué sirve? Recopila, indexa y analiza logs de todos tus servidores, firewalls y aplicaciones. Detecta intrusiones, vulnerabilidades, cambios no autorizados en archivos (FIM) y evalúa el cumplimiento normativo.
  • ¿Qué coste tiene? El software es 100% gratuito y Open Source. El único coste es el hardware donde lo alojes (computación y almacenamiento).
  • ¿Qué logramos con esta herramienta? Pasar de ser reactivos (“creo que nos han atacado”) a ser proactivos (“tenemos 5 intentos de login SSH fallidos desde una IP sospechosa en el firewall, procedo a bloquearla automáticamente”).

La Arquitectura: Diseñando el docker-compose.yml

Para este laboratorio utilizaremos un despliegue Single-Node (todo en un mismo servidor) orquestado mediante Docker Compose. Esto nos permite levantar tres piezas fundamentales:

  1. Wazuh Indexer: El motor de búsqueda (basado en OpenSearch) que almacena los datos.
  2. Wazuh Manager: El cerebro que analiza los datos y dispara las alertas.
  3. Wazuh Dashboard: La interfaz web para visualizar la inteligencia de amenazas.

Aislamiento de Red y Privilegios (Zero Trust)

En nuestro stack de Docker, hemos aplicado dos principios vitales de seguridad:

  1. Red Interna Dedicada: Los contenedores no usan la red por defecto. Creamos una red privada (wazuh_net) exclusiva para ellos. El Manager y el Indexer hablan entre sí por esta red invisible desde el exterior. Solo exponemos al host los puertos estrictamente necesarios.
  2. Ejecución sin privilegios (Non-Root): Por diseño, los contenedores críticos (como el indexer) no ejecutan sus procesos principales como root. Utilizan usuarios internos sin privilegios (UID 1000), lo que mitiga enormemente el riesgo si un atacante logra comprometer el contenedor (Container Escape).

El Código Base: Nuestro docker-compose.yml

No nos andamos con rodeos. Este no es el archivo de despliegue genérico del fabricante, sino nuestra configuración hardened. Aquí definimos los volúmenes persistentes, forzamos el aislamiento de red amarrando la escucha a interfaces privadas específicas del host (reemplaza las IPs de ejemplo por las tuyas de gestión/LAN o VPN) y gestionamos los secretos correctamente.

version: '3.8'

services:
  single-node-wazuh.indexer-1:
    image: wazuh/wazuh-indexer:4.7.2
    container_name: wazuh.indexer
    restart: always
    # Seguridad: NO exponemos el puerto 9200 al host. 
    # Solo es accesible desde la red interna de Docker.
    environment:
      - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g" # Control estricto de memoria JVM
    volumes:
      - /srv/docker-data/wazuh/indexer:/var/lib/wazuh-indexer
      - ./config/wazuh_indexer/wazuh.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer.pem:ro
    networks:
      - wazuh_net

  single-node-wazuh.manager-1:
    image: wazuh/wazuh-manager:4.7.2
    container_name: wazuh.manager
    restart: always
    ports:
      # Forzamos la escucha únicamente en la IP privada de nuestra LAN/Gestión
      - "192.168.1.50:1514:1514"
      - "192.168.1.50:1515:1515"
    environment:
      - INDEXER_URL=https://wazuh.indexer:9200
      - INDEXER_PASSWORD=${WAZUH_INDEXER_PASSWORD} # Inyectado por variable, nunca hardcodeado
    volumes:
      - /srv/docker-data/wazuh/manager:/var/ossec/data
    networks:
      - wazuh_net
    depends_on:
      - single-node-wazuh.indexer-1

  single-node-wazuh.dashboard-1:
    image: wazuh/wazuh-dashboard:4.7.2
    container_name: wazuh.dashboard
    restart: always
    ports:
      # Hardening: El panel web solo expone el puerto HTTPS interno (5601) 
      # mapeado al 443 a través de la IP de nuestra interfaz VPN (ej. WireGuard/OpenVPN)
      - "10.8.0.1:443:5601"
    environment:
      - OPENSEARCH_HOSTS=https://wazuh.indexer:9200
      - INDEXER_PASSWORD=${WAZUH_INDEXER_PASSWORD} # Inyectado por variable, nunca hardcodeado
    networks:
      - wazuh_net
    depends_on:
      - single-node-wazuh.indexer-1

networks:
  wazuh_net:
    driver: bridge # Red aislada del resto del host

Gestión de Secretos (.env): Fíjate que las contraseñas no están escritas en el código. Esto evita que, si subimos este archivo a un repositorio público en GitHub, filtremos las credenciales. Para que funcione, crea un archivo oculto llamado .env en la misma carpeta que el docker-compose.yml con este contenido: WAZUH_INDEXER_PASSWORD=TuClaveMaestraReal Docker Compose leerá este archivo automáticamente al arrancar e inyectará el secreto en los contenedores.

La Trampa de Docker vs. UFW: Es vital recordar que Docker manipula las reglas de iptables por encima de UFW para gestionar sus redirecciones de red (cadena FORWARD). Si expones un puerto en el compose de forma abierta ("1514:1514"), Docker lo abrirá al mundo entero (0.0.0.0), saltándose cualquier bloqueo genérico de tu firewall. Por eso es crítico bindeo explícito a IPs privadas del host (IP:puerto:puerto) dentro del archivo Compose.


El Corazón de la Seguridad: Nuestra propia CA (Certificate Authority)

wazuhcert

Antes de levantar el sistema, debemos asegurar que nadie pueda interceptar el tráfico entre nuestros contenedores. Para ello, generamos una PKI (Infraestructura de Clave Pública) interna.

En lugar de depender de terceros, el despliegue genera una CA Raíz privada:

  • root-ca.key: Es nuestra firma digital maestra. Se genera una vez y se usa para firmar los certificados de nuestros nodos.
  • root-ca.pem: Es el documento público que todos los contenedores tienen.

Cuando el Dashboard se conecta al Indexer, ambos validan que sus certificados están firmados por nuestra CA. Si un contenedor malicioso intentara inyectarse en la red, sería rechazado inmediatamente al no poseer un certificado válido.


Persistencia y Seguridad Perimetral

Un SIEM genera gigabytes de logs. Si dejamos que Docker guarde los volúmenes en su ruta efímera por defecto, podríamos saturar el disco del sistema operativo host.

La solución: Mapear los volúmenes de datos a una partición en un disco de estado sólido o NVMe dedicado (por ejemplo, /srv/docker-data/wazuh). Esto garantiza operaciones de I/O altísimas y supervivencia de los datos ante reinicios o desastres del sistema.

Con nuestro enfoque de bindeo explícito en el docker-compose.yml, los sockets quedan amarrados de forma segura a interfaces controladas (red interna y VPN). A nivel de sistema operativo host, el cortafuegos perimetral del servidor se mantiene limpio y restrictivo, delegando en las interfaces privadas de red la exclusividad del tráfico de ciberseguridad.


El Hardening Final: Cambiando la clave maestra

Este es el paso donde la mayoría de los administradores fallan: dejar la contraseña por defecto (SecretPassword). Como Wazuh protege su base de datos interna mediante RBAC (Control de Acceso Basado en Roles) en la capa de OpenSearch, no puedes cambiar esta clave desde el panel web. Hay que inyectar la configuración en caliente por terminal.

Aquí tienes la guía paso a paso para hacerlo como un profesional:

Paso 1: Generar el Hash de tu nueva clave

Nunca guardamos contraseñas de administrador en texto claro en los archivos de configuración del Indexer. Entramos a un contenedor temporal para generar un hash seguro de nuestra nueva contraseña (sustituye [TuNuevaClave] por la tuya):

sudo docker run --rm -it wazuh/wazuh-indexer:4.7.2 bash /usr/share/wazuh-indexer/plugins/opensearch-security/tools/hash.sh -p [TuNuevaClave]

Copia el hash generado (empezará por algo similar a $2y$12$...).

Paso 2: Modificar el archivo de usuarios internos

Edita el archivo de tu volumen local donde residen los usuarios del indexador:

sudo nano config/wazuh_indexer/internal_users.yml

Busca la sección del usuario admin y sustituye el valor de su hash por el que acabas de copiar. Guarda y cierra.

Paso 3: Inyección de seguridad en el Indexer

Ahora usamos el script securityadmin.sh dentro del contenedor del Indexer. Este comando obliga a la base de datos a leer el archivo modificado y aplicar los cambios sin reiniciar el clúster.

(Nota técnica: Inyectamos la variable JAVA_HOME porque el entorno de Bash dentro del contenedor a veces no la carga por defecto, lo que hace que el script falle en silencio).

Ejecuta lo siguiente, asegurándote de que el nombre del contenedor (single-node-wazuh.indexer-1) coincida con el tuyo:

sudo docker exec -it single-node-wazuh.indexer-1 bash -c "export JAVA_HOME=/usr/share/wazuh-indexer/jdk && /usr/share/wazuh-indexer/plugins/opensearch-security/tools/securityadmin.sh -cd /usr/share/wazuh-indexer/opensearch-security/ -icl -nhnv -cacert /usr/share/wazuh-indexer/certs/root-ca.pem -cert /usr/share/wazuh-indexer/certs/admin.pem -key /usr/share/wazuh-indexer/certs/admin-key.pem -h localhost"

Si todo ha ido bien, verás la validación del clúster terminando con un mensaje: Done with success. La contraseña por defecto acaba de ser eliminada de la base de datos de OpenSearch.

Paso 4: Sincronizar el entorno de Docker (.env)

Como hemos cambiado la llave maestra en el motor de base de datos, ahora tanto el Manager como el Dashboard se han quedado “ciegos” y lanzarán errores de conexión porque siguen intentando usar la clave vieja que lee el Compose.

Gracias a que diseñamos el entorno modularizado, no necesitamos alterar nuestro código de orquestación. Editamos nuestro archivo de variables oculto:

sudo nano .env

Sustituye la contraseña vieja por tu nueva clave en texto claro en la variable WAZUH_INDEXER_PASSWORD. Guarda los cambios y cierra el archivo.

Para finalizar, forzamos a Docker Compose a leer el nuevo secreto y recrear en caliente los contenedores dependientes del entorno:

sudo docker compose up -d

Conclusión

Desplegar Wazuh es solo la mitad del trabajo; asegurarlo es lo que diferencia a un aficionado de un profesional de infraestructuras IT. Hemos logrado una arquitectura resiliente, con red Zero Trust, comunicaciones TLS con CA propia y una identidad sellada criptográficamente.

Nuestro Centro de Mando está listo y blindado. En la siguiente fase del proyecto, comenzaremos a desplegar agentes en nuestros servidores y endpoints para que el SIEM empiece a ingerir datos.

Explore Next

Privacy Shield

Other Projects