WatchGuard-Firewall-Logs in Wazuh: Von „No decoder matched“ zur sauberen, voll geparsten Analyse

Wer WatchGuard-Firewall-Logs über einen Syslog-Collector in Wazuh einspeist, trifft schnell auf diese frustrierende Meldung:

No decoder matched

Genau das ist Nikola passiert:
Die Logs der WatchGuard-Firewall landen per rsyslog → lokale Datei → Wazuh-Agent → Manager im System, tauchen in archives.log auf – aber keiner der gefundenen WatchGuard-Decoder von GitHub greift.

In diesem Artikel schauen wir uns an:

  • warum „No decoder matched“ erscheint
  • wie man einen sinnvollen Decoder-Aufbau für WatchGuard-Logs gestaltet
  • wie man aus einfachen „Allow/Deny“-Regeln zu reichhaltig geparsten Events kommt

1. Ausgangssituation: WatchGuard-Logs aus SyslogCollector

Die Logs kommen von einer WatchGuard-Firewall, z. B.:

2025-11-21T00:28:01.556323+00:00 D1-ROM1-FW-A 80D802B41D509 D1-ROM1-FW firewall: msg_id="3000-0148" Allow Collector Firebox 73 udp 20 64 10.x.x.x 208.x.x.x 46616 53  geo_dst="USA" record_type="AAAA" question="mail.mydomain.com"  (DNS-00)

2025-11-21T00:28:01.556528+00:00 D1-ROM1-FW-A 80D802B41D509 D1-ROM1-FW firewall: msg_id="3000-0148" Deny WAN - Aruba Firebox 40 tcp 20 239 194.x.x.x 209.x.x.x 41277 591 offset 5 S 1497867397 win 4  geo_src="BGR"  geo_dst="ITA" flags="SR" duration="0" sent_pkts="1" rcvd_pkts="0" sent_bytes="40" rcvd_bytes="0"  (Unhandled External Packet-00)

2025-11-21T00:28:01.946033+00:00 D1-ROM1-FW-A 80D802B41D509 D1-ROM1-FW https-proxy[4148]: msg_id="2CFF-0000" Allow Collector WAN - Aruba tcp 10.x.x.x 213.x.x.x 42018 443 msg="HTTPS Request" proxy_act="Default-HTTPS-Client" tls_profile="TLS-Client-HTTPS.Standard" ...

Typisch:

  • Syslog-Prefix (Datum, Host SysLogCollector, Pfad, Timestamp)
  • dann der eigentliche WatchGuard-Teil mit:
    • Host / Device-Name (D1-ROM1-FW-A, D1-ROM1-FW)
    • Subsystem (firewall:, https-proxy[...])
    • msg_id="...", Action (Allow / Deny), Richtlinie, Bytes, Protokoll, Zonen, IPs, Ports, Geo-Infos, Kategorien etc.

Die vorhandenen Community-Decoder passten nicht exakt zum Format – Ergebnis:
Kein Decoder matcht → Wazuh behandelt die Zeilen nur als plain Logtext.

2. Wichtiger Punkt: „No decoder matched“ heißt nicht „Log ist kaputt“ – sondern „Decoder passt nicht“

[Bony John] hat es schön auf den Punkt gebracht:

  • Wenn „No decoder matched“ angezeigt wird,
    kein vorhandener Decoder trifft auf das Format zu.

Das kann bedeuten:

  • Regex des Decoders ist falsch oder zu streng
  • Prematch passt nicht
  • Logformat weicht von der erwarteten WatchGuard-Variante ab
  • oder der „Decoder von GitHub“ war für ein anderes WatchGuard-Template geschrieben

Darum ist der richtige Weg: Eigene Decoder schreiben, passend zu den echten Logs.

3. Lösung: Parent-/Child-Decoder für WatchGuard-Logs

Statt zig Einzeldecoder im Blindflug zu testen, hat Bony ein sauberes, mehrstufiges Decoder-Design vorgeschlagen.

3.1 Parent-Decoder: Erkennen, dass es überhaupt WatchGuard ist

Beispiel:

<decoder name="watchguard">
    <prematch>D1-ROM1-FW</prematch>
</decoder>

Das:

  • prüft nur, ob der String D1-ROM1-FW im Log vorkommt
  • markiert das Event als „WatchGuard-Log“
  • dient als Anker für weitere Child-Decoder

Du könntest hier genauso gut auf firewall: oder https-proxy[ matchen – Hauptsache stabil.

3.2 Erster Child-Decoder: Basisdaten für alle Varianten

Ein erster Child-Decoder extrahiert Felder, die in den meisten Logs gleich strukturiert vorkommen, z. B.:

  • Device-ID / -Host
  • msg_id
  • Action (Allow / Deny)
  • Policy / Rule Name
  • Bytes / Protokoll / Zonen
  • Quell- und Ziel-IP/Port

Beispiel (vereinfacht):

<decoder name="watchguard">
    <parent>watchguard</parent>
    <regex>(\w+)\s*(\S*)\s*(\S*):\s*msg_id="(\S*)"\s*(\w*)\s*(\.*)\s+(\d+)\s*(\w+)\s*(\d*)\s*(\d*)\s+(\S*)\s*(\S*)\s*(\d*)\s*(\d*)\s*</regex>
    <order>device_id,device_name,device_type,msg_id,action,policy,bytes,protocol,src_zone,dst_zone,srcip,dstip,srcport,dstport</order>
</decoder>

Das ist nur ein Beispiel – wichtig ist das Muster:

  • parent="watchguard" → knüpft an das Prematch an
  • regex → zieht in einem Schwung die gemeinsamen Felder
  • order → bennent sie sauber (action, srcip, dstip, …)

3.3 Weitere Child-Decoder: Spezifische Events (DNS, TCP Deny, HTTPS)

Nach dem Port-Block ändern sich die Logs – DNS-, TCP- und HTTPS-Events haben unterschiedliche Zusatzfelder.

Darum nutzt Bony weitere Child-Decoder für die unterschiedlichen Endstücke:

  • DNS-Events, z. B.: <decoder name="watchguard"> <parent>watchguard</parent> <regex>geo_dst="(\w*)" record_type="(\S*)" question="(\S*)" \((\S*)\)</regex> <order>geo_dst,record_type,domain,category</order> </decoder>
  • TCP-Deny-Events mit Geo-Daten, Flags, Duration, Packet-/Byte-Counts: <decoder name="watchguard"> <parent>watchguard</parent> <regex>offset (\d*)\s*(\w*)\s*(\d*)\s*\.*geo_src="(\w*)"\s*geo_dst="(\S*)"\s*flags="(\S*)"\s*duration="(\d*)"\s*sent_pkts="(\d*)" rcvd_pkts="(\d*)" sent_bytes="(\d*)" rcvd_bytes="(\d*)"\s*\((\.*)\)$</regex> <order>offset,tcp_flag,seq_id,geo_src,geo_dst,flags,duration,sent_pkts,rcvd_pkts,sent_bytes,rcvd_bytes,category</order> </decoder>

Für HTTPS-Proxy-Logs (https-proxy[...]) könnte man nach dem gleichen Muster einen weiteren Child-Decoder bauen, der z. B. msg="HTTPS Request", tls_version, sni, action, app_id, Bytes etc. extrahiert.

4. Nikos eigenes Setup: Minimaldecoder + einfache Regeln

Niko hatte sich am Wochenende bereits selbst einen Einstieg gebaut:

Predecoder:

<decoder name="firewall_log">
    <prematch>^\.*firewall:</prematch>
</decoder>

Dann mehrere kleine Child-Decoder:

  • msg_id="..." → Feld ID
  • fqdn_dst_match="..." → Feld FQDN_DST
  • (Allow) bzw. (Deny) → Feld Action
  • (tcp) / (udp) → Feld PROTOCOL

Und dazu Regeln:

<group name="Watchguard,">
  <rule id="110000" level="0">
    <decoded_as>firewall_log</decoded_as>
    <description>WatchGuard-FW-Event</description>
  </rule>

  <rule id="110001" level="5">
    <if_sid>110000</if_sid>
    <match>Deny</match>
    <description>Packet Was Denied.</description>
  </rule>

  <rule id="110002" level="5">
    <if_sid>110000</if_sid>
    <match>Allow</match>
    <description>Packet Was Allowed.</description>
  </rule>
</group>

Das ist ein guter, pragmatischer Start:

  • Man sieht schnell: „Allow“ vs. „Deny“
  • Events werden schon im SIEM sichtbar hervorgehoben

Bony bestätigt das sinngemäß:
Das funktioniert – aber du kannst noch viel mehr aus den Logs herausholen.

5. Wann lohnt sich ein „voller“ Decoder?

Ein minimaler Decoder reicht, wenn du:

  • nur Alerts „Allow vs. Deny“ brauchst
  • keine tiefe Analyse nach Geo, Ports, Policies, Bytes etc. machst

Ein „reicher“ Decoder wie der von Bony lohnt sich, wenn du:

  • Dashboards zu:
    • Top-Policies, Top-Geo-Quellen/-Ziele, abgewiesenen Ports usw. willst
  • gezielte Regeln bauen möchtest:
    • z. B. „Deny aus bestimmten Ländern zu sensiblen Ports mit bestimmten Flags“
  • später Threat Hunting machen willst, z. B.:
    • SYN-Scans, auffällige Offsets, viel Traffic zu wenig IPs usw.

Dann zahlen sich saubere Felder wie geo_src, geo_dst, duration, sent_pkts, category absolut aus.

6. Fazit

Der Thread zeigt sehr schön:

  • „No decoder matched“ ist keine Sackgasse – sondern der Startpunkt für maßgeschneiderte Lösungen.
  • WatchGuard-Logs sind strukturiert genug, um mit Parent-/Child-Decodern sauber aufgelöst zu werden.
  • Man kann klein anfangen (Allow/Deny) und schrittweise mehr Felder ergänzen.
  • Mit einem guten Decoder-Set erhält man nicht nur Events, sondern ein mächtiges Netzwerk-Observability-Werkzeug.

Mehr zu Wazuh …

Mehr zum Wazuh Ambassador Program …