---
title: "ELK + Wazuh — Intégration et règles de détection"
domain: monitoring
subdomain: kibana
type: snippet
tags: [wazuh, elk, kibana, siem, sigma, correlation, alerting, dashboards, detection]
difficulty: advanced
status: stable
updated: "2026-05-14"
---
import { Tip } from '@/components/mdx';

## Stack complète

```bash
cd monitoring/
bash generate-certs.sh
docker compose --profile all up -d
```

| Service | URL | Credentials |
|---|---|---|
| Kibana (ELK) | `http://localhost:5601` | elastic / voir .env |
| Wazuh Dashboard | `https://localhost:443` | admin / voir .env |
| Elasticsearch | `http://localhost:9200` | elastic / voir .env |
| Wazuh API | `https://localhost:55000` | wazuh-wui / voir .env |

## Alimenter Elasticsearch avec les alertes Wazuh

Wazuh écrit ses alertes dans `/var/ossec/logs/alerts/alerts.json`. Filebeat les relaie vers Elasticsearch.

```bash
# Sur le host du manager, ajouter dans filebeat.yml :
filebeat.inputs:
  - type: log
    paths:
      - /var/lib/docker/volumes/*wazuh-manager-data/_data/logs/alerts/alerts.json
    json.keys_under_root: true
    json.overwrite_keys: true
    tags: ["wazuh", "security"]
    fields:
      log_type: wazuh-alert
    fields_under_root: true

output.elasticsearch:
  hosts: ["http://localhost:9200"]
  username: elastic
  password: "${ELASTIC_PASSWORD}"
  index: "wazuh-alerts-%{+YYYY.MM.dd}"
```

## Index Pattern Kibana pour alertes Wazuh

```bash
# Créer l'index pattern wazuh-alerts-*
curl -X POST "http://localhost:5601/api/index_patterns/index_pattern" \
  -H "Content-Type: application/json" \
  -H "kbn-xsrf: true" -u elastic:changeme -d '
{
  "index_pattern": {
    "title": "wazuh-alerts-*",
    "timeFieldName": "timestamp"
  }
}'
```

## Requêtes KQL — Kibana

```
# Alertes de niveau critique (>= 12)
rule.level >= 12

# Brute-force SSH
rule.id: 5763 AND data.srcip: *

# Modifications FIM (fichiers système)
rule.groups: syscheck AND full_log: "/etc/*"

# Connexions réussies avec compte privilégié
rule.id: 5501 AND data.srcuser: root

# Malware détecté
rule.groups: rootcheck OR rule.groups: virustotal

# Alertes par agent spécifique
agent.name: "srv-prod-01" AND rule.level >= 7

# Timeline : toutes les alertes des 24 dernières heures
timestamp >= now-24h AND rule.level >= 3
```

## Elasticsearch — Requêtes directes

```bash
# Top 10 des règles les plus déclenchées
curl -s "http://elastic:changeme@localhost:9200/wazuh-alerts-*/_search" \
  -H "Content-Type: application/json" -d '
{
  "size": 0,
  "aggs": {
    "top_rules": {
      "terms": { "field": "rule.description.keyword", "size": 10 }
    }
  }
}' | jq '.aggregations.top_rules.buckets'

# IPs sources les plus actives
curl -s "http://elastic:changeme@localhost:9200/wazuh-alerts-*/_search" \
  -H "Content-Type: application/json" -d '
{
  "size": 0,
  "query": { "range": { "timestamp": { "gte": "now-24h" } } },
  "aggs": {
    "top_ips": {
      "terms": { "field": "data.srcip.keyword", "size": 20 }
    }
  }
}' | jq '.aggregations.top_ips.buckets'

# Alertes critiques (niveau >= 12)
curl -s "http://elastic:changeme@localhost:9200/wazuh-alerts-*/_search" \
  -H "Content-Type: application/json" -d '
{
  "query": {
    "bool": {
      "must": [
        { "range": { "rule.level": { "gte": 12 } } },
        { "range": { "timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "sort": [{ "timestamp": { "order": "desc" } }],
  "size": 20
}' | jq '.hits.hits[]._source | {timestamp, agent_name: .agent.name, rule: .rule.description, level: .rule.level}'
```

## Alerting Kibana (Watcher / Rules)

```bash
# Créer une règle d'alerte sur brute-force SSH
curl -X POST "http://localhost:5601/api/alerting/rule" \
  -H "Content-Type: application/json" \
  -H "kbn-xsrf: true" -u elastic:changeme -d '
{
  "name": "SSH Brute Force Alert",
  "rule_type_id": ".es-query",
  "schedule": { "interval": "5m" },
  "params": {
    "index": ["wazuh-alerts-*"],
    "timeField": "timestamp",
    "timeWindowSize": 10,
    "timeWindowUnit": "m",
    "thresholdComparator": ">=",
    "threshold": [5],
    "esQuery": "{\"query\":{\"match\":{\"rule.id\":\"5763\"}}}"
  },
  "actions": []
}'
```

## Cas d'usage SIEM — Détection

### Détection brute-force SSH

```
Wazuh Rule IDs : 5763 (Multiple failed SSH), 5551 (sshd)
Seuil : > 5 tentatives en 2 min depuis la même IP
Active Response : firewall-drop (blocage 1h)
Index KQL : rule.id: 5763
```

### Détection de scan réseau

```
Wazuh Rule ID : 40101 (Nmap scan detected)
Logstash : parser les logs Snort/Suricata
Index KQL : rule.groups: ids AND data.action: DROP
```

### Modification de fichiers système

```
Wazuh FIM Rule IDs : 550, 553, 554
Répertoires surveillés : /etc, /bin, /usr/sbin
KQL : rule.groups: syscheck AND full_log: "/etc/passwd"
Sévérité : 7 (modification), 12 (suppression de fichier critique)
```

### Élévation de privilèges Linux

```
Wazuh Rule IDs : 5902 (sudo), 5930 (su)
KQL : rule.id: (5902 OR 5930) AND data.dstuser: root
```

### Connexion depuis une IP géographiquement anormale

```bash
# Activer le module GeoIP dans Logstash (pipeline inclus)
# Kibana Maps : visualiser les IPs sources par pays
# Alerte : connexion SSH réussie depuis pays != FR
```

## Dashboard recommandé — Kibana

Créer un dashboard "Security Overview" avec :

| Visualisation | Type | Champ |
|---|---|---|
| Alertes par niveau (aujourd'hui) | Bar chart | `rule.level` |
| Top 10 agents les plus alertés | Pie chart | `agent.name` |
| Carte GeoIP des IPs sources | Maps | `geoip.location` |
| Timeline des alertes critiques | Line chart | `@timestamp` (level≥12) |
| Dernières alertes critiques | Data table | `rule.description`, `agent.name`, `timestamp` |
| Statut des agents Wazuh | Metric | `agent.status` |

<Tip>
Créer un **ILM index template** pour les index `wazuh-alerts-*` : rétention 90 jours en hot, compression warm après 7 jours. Elasticsearch gère la rotation automatiquement et évite la saturation du disque.
</Tip>
