Einleitung
CEF (Common Event Format) ist ein etabliertes Log-Format mit einem festen Header (Version|Vendor|Product|…) und einer variablen Extension als Key-Value-Liste. Genau diese Mischung sorgt in Wazuh häufig für zwei typische Probleme: Decoder „catcht“ nicht zuverlässig (wegen leicht abweichender CEF-Header-Darstellung) oder Decoder kollidiert mit anderen CEF-Decodern (weil die Prematches zu generisch sind). CEF selbst ist als Header+Extension definiert, wobei die Extension aus Key-Value-Paaren besteht. OpenText+2OpenText+2
Dieser Beitrag zeigt anhand von Ubiquiti UniFi CEF-Logs, wie man Decoder so baut, dass sie (1) den CEF-Header robust parsen, (2) wichtige Extension-Felder extrahieren und (3) keine Konflikte mit anderen CEF-Quellen (z. B. Imperva/Incapsula) erzeugen.
Ausgangslage / Problemstellung
Es liegen UniFi-Logs im CEF-Format vor, u. a. in dieser Form:
2026-01-07T10:59:27+00:00 mmm-Up CEF: 0|Ubiquiti|UniFi Network|10.0.162|402|WiFi Client Roamed|1|UNIFIcategory=... UNIFIwifiName=Switch-up Workers ...
Der ursprüngliche Decoder verwendete:
- einen sehr schmalen
prematchaufCEF:0|Ubiquiti|UniFi Network(ohne Berücksichtigung von Leerzeichen nachCEF:), - Regex-Platzhalter wie
(\.)/(\.+)(praktisch „Punkt(e)“, nicht „beliebige Zeichen“), - und erzeugte in der weiteren Iteration Konflikte mit einem anderen CEF-Decoder (Imperva), weil die Erkennung nicht sauber voneinander getrennt war.
Technische Analyse
1) CEF ist nicht immer „CEF:0|…“ – Whitespace und Syslog-Header verändern das Matching
Viele Geräte/Forwarder liefern CEF nicht als reinen String CEF:0|…, sondern mit einem Präfix (Timestamp/Hostname) und teilweise als CEF: 0|… (mit Leerzeichen). In Wazuh werden bei syslog-artigem Header in der Pre-Decoding-Phase Felder wie timestamp, hostname und program_name extrahiert. Wazuh Dokumentation+1
Wenn Wazuh in Phase 1 program_name: 'CEF' erkennt, ist <program_name>CEF</program_name> als Filter oft zuverlässiger als ein prematch, der „am String-Anfang“ erwartet wird.
2) Regex-Syntax: (\.) matcht einen Punkt – nicht „beliebige Zeichen“
In Wazuh-Decodern ist die Feldextraktion über <regex> + <order> definiert; Regex erfordert dabei zwingend prematch oder program_name (im Decoder selbst oder beim Parent). Wazuh Dokumentation+1
(\.) bedeutet: „matche genau einen Punkt“. Für „bis zum nächsten Pipe“ ist bei CEF-Headern stattdessen sinnvoll:
([^|]*)(alles außer|, auch leer möglich) oder(.+?)(nicht-gierig, wenn man sauber begrenzt)
3) Decoder-Konflikte: „CEF“ ist kein Vendor – Prematch muss die Quelle eindeutig machen
Wenn mehrere Produkte CEF liefern, ist ein Decoder-Parent wie <program_name>CEF</program_name> allein zu breit. Dann „konkurrieren“ CEF-Decodersets. Sauber wird es, wenn der Parent zusätzlich über prematch auf Vendor/Product einschränkt (z. B. |Ubiquiti|UniFi Network| vs. |Incapsula|SIEMintegration|). Das reduziert Überschneidungen drastisch.
4) Extension-Felder: \S+ reicht nicht, wenn Values Leerzeichen enthalten
Im Beispiel ist UNIFIwifiName=Switch-up Workers ein Value mit Leerzeichen. Ein Regex UNIFIwifiName=(\S+) würde nur Switch-up erfassen. Für solche Felder braucht man entweder:
- ein Pattern, das bis zum nächsten
KEY=läuft (Lookahead), oder - eine konservative Extraktion über mehrere spezialisierte Kinderdecoder (für Felder ohne Spaces) und separate Behandlung der „problematischen“ Keys.
Für Lookaheads ist es oft sinnvoll, PCRE2 zu nutzen (<regex type="pcre2">). Wazuh unterstützt unterschiedliche Regex-Typen inkl. PCRE2. Stack Overflow+1
Lösung / Best Practices
1) Parent-Decoder: program_name + eindeutiger prematch auf Vendor/Product
Damit UniFi nicht Imperva „in die Quere kommt“, sollte der Parent sowohl program_name als auch Vendor/Product enthalten:
<decoder name="cef-ubiquiti-unifi">
<program_name>CEF</program_name>
<prematch>\|\s*Ubiquiti\|\s*UniFi Network\|</prematch>
</decoder>
\s*toleriert Whitespace.- Der prematch ist bewusst so gewählt, dass er Imperva (
Incapsula|SIEMintegration) nicht treffen kann.
2) CEF-Header robust parsen (Version, EventID, Name, Severity)
Der CEF-Header ist klar definiert (CEF:Version|Vendor|Product|Device Version|Signature ID|Name|Severity|…). OpenText+1
Ein robuster Child-Decoder, der Whitespace nach CEF: toleriert und sauber an Pipes trennt:
<decoder name="cef-ubiquiti-unifi-header">
<parent>cef-ubiquiti-unifi</parent>
<regex>CEF:\s*0\|Ubiquiti\|UniFi Network\|([^|]*)\|([^|]*)\|([^|]*)\|([^|]*)\|</regex>
<order>ubiquiti.version,ubiquiti.event_id,ubiquiti.name,ubiquiti.severity</order>
</decoder>
Damit vermeidet man die typischen Fehler durch (\.) und stellt sicher, dass der Decoder unabhängig vom Präfix (Timestamp/Hostname) greift.
3) Extension-Felder extrahieren: erst „sichere“ Keys, dann Sonderfälle
Für Felder ohne Leerzeichen ist (\S+) oder IP-Regex okay:
<decoder name="cef-ubiquiti-unifi-kv-category">
<parent>cef-ubiquiti-unifi</parent>
<regex>UNIFIcategory=(\S+)</regex>
<order>unifi.category</order>
</decoder>
<decoder name="cef-ubiquiti-unifi-kv-subcategory">
<parent>cef-ubiquiti-unifi</parent>
<regex>UNIFIsubCategory=(\S+)</regex>
<order>unifi.subcategory</order>
</decoder>
<decoder name="cef-ubiquiti-unifi-kv-clientip">
<parent>cef-ubiquiti-unifi</parent>
<regex>UNIFIclientIp=(\d{1,3}(?:\.\d{1,3}){3})</regex>
<order>srcip</order>
</decoder>
Für Keys mit Leerzeichen im Value (z. B. UNIFIwifiName=Switch-up Workers) empfiehlt sich PCRE2 mit Lookahead „bis zum nächsten KEY=“:
<decoder name="cef-ubiquiti-unifi-kv-wifiname">
<parent>cef-ubiquiti-unifi</parent>
<regex type="pcre2">UNIFIwifiName=(.*?)(?=\s+\w+=|$)</regex>
<order>unifi.wifi_name</order>
</decoder>
Wichtig: Das Lookahead (?=\s+\w+=|$) ist bewusst generisch (nächster Key= oder Zeilenende). PCRE2-Unterstützung ist in Wazuh-Decodern relevant, wenn man solche Konstrukte nutzen möchte. wazuh-documentation-49-master.readthedocs.io+1
4) Konflikte vermeiden: Eindeutige Decoder-Namen und eindeutige Parent-Prematches
Auch wenn Wazuh Decoder-Hierarchien unterstützt, sollte man in Custom-Decodern:
- eindeutige
name-Werte wählen (nicht mehrfachubiquiti-decoder-childverwenden), - pro Produkt/Vendor einen klaren Parent bauen,
- und nicht auf „CEF allgemein“ matchen, wenn mehrere CEF-Quellen existieren.
5) Testen mit wazuh-logtest und danach Manager neu laden
Wazuh empfiehlt zum Debugging den Einsatz von wazuh-logtest, um Decoding (Phase 1/2) nachvollziehbar zu prüfen. Wazuh Dokumentation+2Wazuh Dokumentation+2
Für produktive Alert-Generierung müssen Decoder-Änderungen anschließend durch Neustart/Reload des Managers wirksam werden (je nach Setup/Workflow). Wazuh Dokumentation
Lessons Learned / Best Practices
- Program Name nutzen, aber nie allein:
<program_name>CEF</program_name>ist stabil, muss aber mit Vendor/Product imprematchkombiniert werden, um Konflikte zu vermeiden. Wazuh Dokumentation+1 - CEF-Header immer mit
[^|]*trennen: Das ist für Pipe-separierte Header deutlich robuster als „irgendwas mit Punkt“. OpenText+1 - Key-Value-Extension ist trickreich: Values können Leerzeichen enthalten; dafür PCRE2-Lookaheads einsetzen oder Felder selektiv extrahieren. CEF ist explizit als Header + Extension mit Key-Value-Paaren definiert. OpenText+2Delinea Dokumentationsbibliothek+2
- Regelmäßig mit echten Logs testen: Kleine Formatabweichungen wie
CEF: 0|stattCEF:0|sind in der Praxis normal und entscheiden über „matcht“ vs. „matcht nicht“.
Fazit
UniFi-CEF-Decoding in Wazuh scheitert selten an „CEF an sich“, sondern an Details: Whitespace, syslogartige Präfixe, falsche Regex-Platzhalter und zu generische Prematches, die mit anderen CEF-Decodern kollidieren. Mit einem klaren Parent pro Vendor/Product, einem robusten Header-Regex und gezielter Extension-Feldextraktion (inkl. PCRE2 für Values mit Leerzeichen) wird das Decoding stabil, konfliktfrei und langfristig wartbar.
Mehr zu Wazuh …
https://wazuh.com/?utm_source=ambassadors&utm_medium=referral&utm_campaign=ambassadors+program
Mehr zum Wazuh Ambassador Program …
https://wazuh.com/ambassadors-program/?utm_source=ambassadors&utm_medium=referral&utm_campaign=ambassadors+program
https://wazuh.slack.com/archives/C0A933R8E/p1767608892366969