⚠️ Disclaimer
La méthode que je présente ici correspond à ma propre démarche d’apprentissage. Elle peut contenir des approximations ou des erreurs, car j’apprends et je progresse chaque jour un peu plus. Ne prenez donc pas ce que je fais comme une référence absolue, mais plutôt comme un retour d’expérience personnel.
Wazuh SIEM sur homelab : décodeurs custom, bug PCRE2 et logs pfSense RFC 5424
J’avais Wazuh qui tournait, des logs qui arrivaient, et un dashboard qui affichait surtout des alertes génériques. Clément Appercel ingénieur SIEM, ça sonne bien en entretien, mais si tu ne sais pas ce que ton SIEM capture vraiment, tu n’es pas loin du tableau Excel de monitoring.
Ce post raconte comment j’ai construit des décodeurs custom pour mes trois sources de logs (pfSense, Proxmox, Synology), les problèmes rencontrés en chemin, et surtout la méthode pour ne pas travailler à l’aveugle.
Les fichiers de config sont sur GitHub.
La situation de départ
Mon homelab tourne sur trois nœuds Proxmox, un pfSense en routeur/firewall, un NAS Synology DS723+, et un Mac Mini M4 Pro pour Docker. Tout est segmenté en VLANs depuis mars 2026.
Wazuh est déployé sur une VM Debian dans le VLAN LAB (192.168.30.x). Trois sources de logs :
- pfSense : syslog UDP 514 vers Wazuh
- Proxmox ASUS NUC : agent Wazuh installé directement sur l’hôte
- Synology DS723+ : syslog UDP 514 vers Wazuh
Sur le papier, tout arrivait. En pratique, quand j’ouvrais le dashboard Wazuh, je voyais des centaines d’alertes pfSense filterlog (trafic bloqué/autorisé) et quelques events Synology. Les logs DHCP de pfSense ? Absents. Les alertes Suricata IDS ? Absentes. Et pourtant pfSense était configuré pour tout envoyer.
Avant de toucher aux décodeurs : cartographier ce qui arrive
Le réflexe naturel aurait été de commencer à écrire des décodeurs. J’aurais perdu du temps.
La bonne méthode, c’est d’abord de faire l’inventaire de ce qui arrive réellement dans Wazuh. Le fichier archives.log contient tout ce que Wazuh reçoit, décodé ou non, du moment que logall: yes est activé dans ossec.conf. À partir de là, une commande suffit pour avoir la cartographie complète :
| |
Résultat sur mon infrastructure sur une journée :
| Type de log | Volume | % |
|---|---|---|
| filterlog | 46 443 | 95,9% |
| cron | 1 702 | 3,5% |
| dhcpd | 225 | 0,5% |
| syslogd | 20 | ~0% |
| suricata | 6 | ~0% |
| php / nginx | 15 | ~0% |
96% de filterlog. Normal pour un firewall. Mais les 4% restants (DHCP, Suricata, nginx), c’est exactement ce qu’on veut dans un SIEM : traçabilité des devices, alertes IDS, accès admin à l’interface.
Tout arrivait dans archives.log. Rien n’apparaissait dans le dashboard. Diagnostic : pas de décodeur, pas d’alerte.
Pourquoi pfSense n’est pas décodé par défaut
pfSense envoie ses logs au format RFC 5424 :
1 2026-03-23T10:56:43.099591+01:00 pfSense.home.arpa dhcpd 44738 - - DHCPACK on 192.168.40.103 to 9c:9e:6e:65:72:a4 via igc1.40
Wazuh s’attend à du RFC 3164, le format syslog “historique” :
Mar 23 10:56:43 pfSense dhcpd[44738]: DHCPACK on 192.168.40.103 to...
La différence concrète : un 1 en tête (numéro de version du protocole), un timestamp ISO 8601 avec fuseau horaire et microsecondes, et le nom de domaine complet au lieu du hostname court. Le pré-décodeur natif de Wazuh ne reconnaît pas cette enveloppe. Il passe au log suivant.
J’aurais pu basculer pfSense en RFC 3164, l’option existe dans les paramètres syslog. Mais la RFC 5424 donne plus d’infos, notamment le timestamp précis utile en forensic. J’ai préféré garder le format et construire les décodeurs.
Un bug Wazuh 4.14.3 qui n’est pas dans la doc
En construisant les décodeurs Synology, j’ai rencontré un comportement bizarre : le premier décodeur enfant PCRE2 fonctionnait, les suivants étaient silencieux. Résultat : les logs d’authentification Synology matchaient, mais les logs système ne déclenchaient rien.

Après plusieurs heures à tester des regex dans wazuh-logtest, le diagnostic : quand plusieurs enfants PCRE2 partagent le même parent, l’échec d’un enfant bloque l’évaluation des suivants. Ce n’est pas documenté. C’est reproductible.
Contournement : un parent par type de log, chacun avec un seul enfant. La structure devient plus verbeux mais stable.
| |
L’ordre dans le fichier est critique : le prematch spécifique avant le générique, sinon le générique capture tout.
Les décodeurs pfSense construits
Avec la cartographie en main, j’ai identifié les types de logs prioritaires et construit un parent par type.
DHCP : trois variantes de messages (DHCPACK, DHCPREQUEST, bail dupliqué) :
| |
Suricata IDS : les alertes arrivent bien dans le syslog, mais uniquement si l’option “Send Alerts to System Log” est cochée dans la config de l’interface WAN dans pfSense. Elle ne l’est pas par défaut.

| |
Ce qui n’a pas marché comme prévu
Les logs nginx (accès à l’interface d’administration pfSense) : le décodeur natif Wazuh web-accesslog les intercepte avant mon décodeur custom, parce que les décodeurs built-in chargent avant local_decoder.xml. Il extrait les mauvais champs (le timestamp RFC 5424 au lieu de l’IP source). Ces logs restent en level 0 pour l’instant. C’est un angle mort.
La règle <field name="status"> : dans les règles Wazuh, cette syntaxe ne fonctionne qu’avec des champs dynamiques custom, pas avec les champs statiques built-in (status, srcip, protocol…). Pour matcher sur la priorité d’une alerte Suricata, j’ai utilisé <match> sur le texte brut :
| |
Le manager refusait de démarrer avec l’autre syntaxe. Pas de message d’erreur clair dans systemctl status, juste “Configuration error. Exiting”. C’est journalctl -xeu wazuh-manager.service qui donnait le détail.
Les règles en place
18 règles custom au total, réparties en trois groupes :
- pfSense filterlog (100010-100014) : block/pass par protocole
- pfSense DHCP (100030-100032) : bail accordé, demande, conflit IP (level 7)
- pfSense Suricata IDS (100040-100043) : alerte générique (level 6), priorité 2 (level 8), hôte compromis ET (level 10), priorité 1 critique (level 12)
- Synology DSM (100020-100025) : événements système, auth success/failure, brute force (level 10), énumération utilisateurs (level 12)

La suite
Ce qui reste à faire sur ce projet :
- Décodeur nginx pfSense (accès WebUI admin) : contourner le conflit avec
web-accesslog - Active Response : blocage automatique des IPs via l’API pfSense quand Suricata déclenche une alerte brute force
- Vulnerability Detection sur l’agent Proxmox
- Mapping MITRE ATT&CK sur les règles custom (T1110 pour le brute force, T1046 pour les scans…)
Ce que j’en retiens
La partie technique n’était pas la plus difficile. Ce qui a pris du temps, c’est d’accepter de ne pas commencer à écrire des décodeurs immédiatement. Faire l’inventaire des logs d’abord, même si ça semble ralentir les choses, change complètement la qualité du résultat. Tu sais ce que tu couvres et ce que tu ne couvres pas. C’est ce qu’on attend d’un ingénieur SIEM en entreprise aussi.
Le bug PCRE2 de Wazuh 4.14.3, je ne l’aurais pas trouvé si j’avais juste suivi les docs officielles. C’est le genre de chose qui se découvre en déboguant méthodiquement, en lisant les sorties de wazuh-logtest ligne par ligne, en posant des hypothèses et en les testant. Pas très glamour, mais c’est comme ça que ça marche.
Dernière chose : pfSense envoie du RFC 5424 par défaut, et beaucoup de ressources en ligne parlent de RFC 3164. Si tu déploies Wazuh avec pfSense et que tu ne vois pas ce que tu attends, commence par vérifier dans archives.log. Les logs sont probablement là, ils ne sont juste pas décodés.
Les décodeurs et règles complets sont disponibles sur GitHub.