Aller au contenu

Wazuh SIEM avec Docker : déploiement complet avec SSL et règles de conformité

Rogelio Guerra Riverón
Auteur
Rogelio Guerra Riverón
Construction de ma propre infrastructure web depuis zéro. Je documente chaque étape : serveurs, réseaux, conteneurs et tout ce qui se présente.

Qu’est-ce que l’ENS et pourquoi c’est important
#

L’Schéma National de Sécurité (ENS) est le cadre normatif obligatoire en matière de cybersécurité pour les Administrations Publiques espagnoles et pour les entreprises privées qui fournissent des services à ces dernières. Il est réglementé par le Real Decreto 311/2022 et établit les principes, exigences et mesures de sécurité que doivent appliquer les systèmes d’information qui traitent des données ou des services publics.

En pratique, l’ENS classe les systèmes selon l’impact qu’aurait un incident de sécurité sur l’organisation et les citoyens. Cette classification détermine le niveau de mesures à mettre en œuvre.

Les trois catégories de l’ENS
#

L’ENS définit trois niveaux de catégorisation :

Catégorie Basique Pour les systèmes dont la compromission aurait un impact limité. S’applique généralement aux sites web informatifs, services internes à faible risque ou systèmes contenant des données non sensibles. Les mesures requises sont les minimales du cadre.

Catégorie Moyenne Pour les systèmes dont la compromission causerait un préjudice considérable à l’organisation ou à des tiers. C’est la catégorie la plus courante dans les environnements de gestion interne : ERP, portails employés, systèmes RH, plateformes de gestion documentaire. Elle requiert des contrôles actifs de surveillance, gestion des incidents et contrôle d’accès.

Catégorie Élevée Pour les systèmes critiques dont la compromission pourrait causer un dommage grave ou très grave. S’applique aux systèmes gérant des infrastructures critiques, données de santé, systèmes judiciaires ou de défense. Exige les mesures les plus strictes du schéma.

La catégorie moyenne en pratique
#

Un système classé en catégorie moyenne doit avoir, entre autres contrôles :

  • Surveillance continue des événements de sécurité
  • Détection et gestion active des incidents
  • Contrôle d’accès privilégié et traçabilité des actions
  • Intégrité des fichiers système
  • Enregistrement et analyse centralisée des logs

C’est précisément là que Wazuh intervient.


Wazuh comme plateforme de conformité
#

Wazuh est une plateforme SIEM (Security Information and Event Management) open source qui couvre de manière native la plupart des contrôles de surveillance exigés par la catégorie moyenne : analyse des logs en temps réel, détection des intrusions, surveillance de l’intégrité des fichiers, inventaire logiciel et réponse active aux incidents.

Dans cet article, je déploie un nœud unique avec Docker, je génère les certificats TLS nécessaires et j’ajoute des règles personnalisées orientées vers les contrôles de sécurité les plus courants dans les environnements de catégorie moyenne.


Architecture de la pile
#

La pile Wazuh single-node comporte trois composants :

  • wazuh.manager — moteur d’analyse et de corrélation d’événements
  • wazuh.indexer — stockage basé sur OpenSearch
  • wazuh.dashboard — interface web (OpenSearch Dashboards)

La communication entre les composants utilise TLS mutuel avec des certificats propres, que nous générons avant le premier démarrage.

Structure du projet
#

wazuh/
├── docker-compose.yml
├── generate-certs.yml
├── gen-certs.sh
├── deploy.sh
├── .env
└── config/
    ├── certs.yml
    ├── wazuh_manager/
    │   ├── wazuh_manager.conf
    │   └── local_rules.xml
    ├── wazuh_indexer/
    │   ├── wazuh.indexer.yml
    │   └── internal_users.yml
    └── wazuh_dashboard/
        ├── opensearch_dashboards.yml
        └── wazuh.yml

Variables d’environnement
#

Créez le fichier .env à partir de l’exemple :

cp .env.example .env

Contenu minimal :

INDEXER_ADMIN_PASSWORD=TU_PASSWORD_SEGURO
KIBANA_PASSWORD=TU_PASSWORD_KIBANA
API_PASSWORD=TU_PASSWORD_API

SMTP_HOST=mail.tudominio.com
SMTP_FROM=alertas@tudominio.com
SMTP_TO=admin@tudominio.com

Utilisez des mots de passe d’au moins 12 caractères contenant des majuscules, des chiffres et des symboles. L’indexer les valide au démarrage.

Générer les certificats TLS
#

Wazuh nécessite des certificats TLS pour la communication interne entre le manager, l’indexer et le dashboard. La pile inclut un conteneur générateur :

docker compose -f generate-certs.yml run --rm generator

Cela crée config/wazuh_indexer_ssl_certs/ avec :

  • root-ca.pem — CA racine auto-signée
  • wazuh.manager.pem / wazuh.manager-key.pem
  • wazuh.indexer.pem / wazuh.indexer-key.pem
  • wazuh.dashboard.pem / wazuh.dashboard-key.pem

docker-compose.yml
#

services:
  wazuh.manager:
    image: wazuh/wazuh-manager:4.9.2
    hostname: wazuh.manager
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 655360
        hard: 655360
    ports:
      - "1514:1514/tcp"   # agentes TCP
      - "1515:1515/tcp"   # enrollment
      - "5140:5140/udp"   # syslog externo
    environment:
      INDEXER_URL: https://wazuh.indexer:9200
      INDEXER_USERNAME: admin
      INDEXER_PASSWORD: ${INDEXER_ADMIN_PASSWORD}
      FILEBEAT_SSL_VERIFICATION_MODE: full
      SSL_CERTIFICATE_AUTHORITIES: /etc/ssl/root-ca.pem
      SSL_CERTIFICATE: /etc/ssl/filebeat.pem
      SSL_KEY: /etc/ssl/filebeat.key
      API_USERNAME: wazuh-wui
      API_PASSWORD: ${API_PASSWORD}
    volumes:
      - wazuh_api_configuration:/var/ossec/api/configuration
      - wazuh_etc:/var/ossec/etc
      - wazuh_logs:/var/ossec/logs
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/etc/ssl/root-ca.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key
      - ./config/wazuh_manager/local_rules.xml:/var/ossec/etc/rules/local_rules.xml
      - ./config/wazuh_manager/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf

  wazuh.indexer:
    image: wazuh/wazuh-indexer:4.9.2
    hostname: wazuh.indexer
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    environment:
      OPENSEARCH_JAVA_OPTS: "-Xms1g -Xmx1g"
    volumes:
      - wazuh-indexer-data:/var/lib/wazuh-indexer
      - ./config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer-key.pem

  wazuh.dashboard:
    image: wazuh/wazuh-dashboard:4.9.2
    hostname: wazuh.dashboard
    restart: always
    ports:
      - "8443:5601"
    environment:
      INDEXER_USERNAME: admin
      INDEXER_PASSWORD: ${INDEXER_ADMIN_PASSWORD}
      WAZUH_API_URL: https://wazuh.manager
      DASHBOARD_USERNAME: kibanaserver
      DASHBOARD_PASSWORD: ${KIBANA_PASSWORD}
      API_USERNAME: wazuh-wui
      API_PASSWORD: ${API_PASSWORD}
    volumes:
      - ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
      - ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
      - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
    depends_on:
      - wazuh.indexer

volumes:
  wazuh_api_configuration:
  wazuh_etc:
  wazuh_logs:
  wazuh_queue:
  wazuh_var_multigroups:
  wazuh_integrations:
  wazuh_active_response:
  wazuh_agentless:
  wazuh_wodles:
  filebeat_etc:
  filebeat_var:
  wazuh-indexer-data:

Script de déploiement
#

#!/usr/bin/env bash
set -euo pipefail

echo "=== [1/4] Generando certificados SSL ==="
docker compose -f generate-certs.yml run --rm generator

echo "=== [2/4] Abriendo puertos en UFW ==="
ufw allow 1514/tcp comment "Wazuh agentes TCP"
ufw allow 1515/tcp comment "Wazuh enrollment"
ufw allow 5140/udp comment "Wazuh syslog"

echo "=== [3/4] Iniciando stack ==="
docker compose up -d

echo "=== [4/4] Esperando indexer (2-3 min) ==="
for i in $(seq 1 30); do
  if docker compose exec wazuh.indexer \
      curl -ks https://localhost:9200/_cat/health 2>/dev/null | grep -qE 'green|yellow'; then
    echo "Indexer OK"
    break
  fi
  echo "Esperando... ($i/30)"
  sleep 10
done

docker compose ps
echo "Dashboard disponible en https://TU-IP:8443"

Configuration du manager
#

Le fichier wazuh_manager.conf définit le comportement global. Points clés :

<ossec_config>
  <global>
    <jsonout_output>yes</jsonout_output>
    <alerts_log>yes</alerts_log>
    <email_notification>no</email_notification>
    <smtp_server>mail.tudominio.com</smtp_server>
    <email_from>alertas@tudominio.com</email_from>
    <email_to>admin@tudominio.com</email_to>
    <email_maxperhour>12</email_maxperhour>
    <agents_disconnection_time>10m</agents_disconnection_time>
    <white_list>127.0.0.1</white_list>
    <white_list>192.168.0.0/16</white_list>
  </global>

  <alerts>
    <log_alert_level>3</log_alert_level>
    <email_alert_level>12</email_alert_level>
  </alerts>

  <!-- Agentes via TCP -->
  <remote>
    <connection>secure</connection>
    <port>1514</port>
    <protocol>tcp</protocol>
    <queue_size>131072</queue_size>
  </remote>

  <!-- Syslog UDP para firewalls y dispositivos de red -->
  <remote>
    <connection>syslog</connection>
    <port>5140</port>
    <protocol>udp</protocol>
    <allowed-ips>0.0.0.0/0</allowed-ips>
  </remote>

  <syscheck>
    <disabled>no</disabled>
    <frequency>43200</frequency>
    <scan_on_start>yes</scan_on_start>
    <alert_new_files>yes</alert_new_files>
    <directories check_all="yes" report_changes="yes">/etc,/usr/bin,/usr/sbin</directories>
    <directories check_all="yes">/bin,/sbin,/boot</directories>
  </syscheck>
</ossec_config>

Règles de sécurité personnalisées
#

Wazuh inclut des milliers de règles prédéfinies. Les vôtres vont dans local_rules.xml avec des IDs à partir de 100000. Ce bloc implémente les contrôles de surveillance les plus courants dans les environnements avec des exigences de conformité de catégorie moyenne : détection des attaques, contrôle d’accès privilégié, intégrité des fichiers et disponibilité des services.

<group name="cumplimiento_custom,">

  <!-- DETECCIÓN DE ATAQUES — autenticación -->

  <!-- Fuerza bruta SSH: 8 intentos fallidos en 120 segundos -->
  <rule id="100001" level="10" frequency="8" timeframe="120">
    <if_matched_sid>5760</if_matched_sid>
    <description>Posible ataque de fuerza bruta SSH — $(attempts) intentos fallidos</description>
    <group>authentication_failures,</group>
  </rule>

  <!-- Cuenta de usuario bloqueada -->
  <rule id="100002" level="10">
    <if_sid>5503</if_sid>
    <description>Cuenta bloqueada por múltiples intentos fallidos de autenticación</description>
    <group>authentication_failures,</group>
  </rule>

  <!-- CONTROL DE ACCESO PRIVILEGIADO -->

  <!-- Escalada de privilegios con sudo -->
  <rule id="100003" level="9">
    <if_sid>5402</if_sid>
    <description>Escalada de privilegios detectada mediante sudo</description>
    <group>priv_escalation,</group>
  </rule>

  <!-- Nuevo usuario creado en el sistema -->
  <rule id="100010" level="8">
    <if_sid>5902</if_sid>
    <description>Nuevo usuario creado en el sistema — revisión recomendada</description>
    <group>account_changes,</group>
  </rule>

  <!-- INTEGRIDAD DE FICHEROS -->

  <!-- Modificación de fichero crítico del sistema -->
  <rule id="100020" level="10">
    <if_sid>550</if_sid>
    <description>Fichero crítico del sistema modificado — $(file)</description>
    <group>syscheck,integrity_check_host,</group>
  </rule>

  <!-- DISPONIBILIDAD DE SERVICIOS -->

  <!-- Servicio detenido inesperadamente -->
  <rule id="100030" level="8">
    <if_sid>2904</if_sid>
    <description>Servicio detenido de forma inesperada — $(service)</description>
    <group>service_control,</group>
  </rule>

</group>

Ce que couvre chaque bloc
#

RèglesZone de contrôleDescription
100001–100002Détection des attaquesForce brute et verrouillage de comptes
100003, 100010Accès privilégiéSudo et création d’utilisateurs
100020Intégrité du systèmeModifications de fichiers critiques
100030DisponibilitéArrêt inattendu de services

Les niveaux (8-10) déterminent quelles alertes génèrent une notification par email selon le seuil configuré dans email_alert_level.

Enrôler un agent Linux
#

Depuis le serveur où installer l’agent :

# Descargar e instalar (ajusta versión y arquitectura)
wget https://packages.wazuh.com/4.x/apt/pool/main/w/wazuh-agent/wazuh-agent_4.9.2-1_amd64.deb
dpkg -i wazuh-agent_4.9.2-1_amd64.deb

# Apuntar al manager
sed -i 's/MANAGER_IP/192.168.X.X/' /var/ossec/etc/ossec.conf

# Registrar y arrancar
/var/ossec/bin/agent-auth -m 192.168.X.X
systemctl enable wazuh-agent && systemctl start wazuh-agent

Accès au dashboard
#

Une fois la pile en cours d’exécution (l’indexer prend 2-3 minutes pour initialiser) :

https://TU-IP:8443
Usuario: admin
Contraseña: (la definida en INDEXER_ADMIN_PASSWORD)

Important : changez tous les mots de passe par défaut avant d’exposer le service sur le réseau.

Conclusion
#

Avec cette pile, vous disposez d’un SIEM complet sur un seul serveur capable de couvrir les contrôles de surveillance exigés par les cadres de conformité les plus courants. L’étape suivante naturelle consiste à ajouter plus d’agents, à configurer des alertes par email ou webhook pour les événements de haut niveau, et à consulter les tableaux de bord de conformité inclus par défaut dans Wazuh pour PCI-DSS, GDPR, HIPAA et ENS. Pour vérifier le niveau de conformité d’un système à l’ENS, le CCN-CERT publie les guides de sécurité (séries CCN-STIC) de référence.