Wazuh Regelpriorität richtig steuern: Warum eine Catch-all-Regel „gewinnt“ – und wie du Mikrotik-Firewallregeln ohne Alert-Flut sauber kaskadierst


Einleitung (Einordnung, Relevanz für Security/SIEM/Wazuh)

Beim Bau eigener Wazuh-Regeln kommt es oft zu einem Missverständnis, das in SIEM-Setups schnell zu falschen Alarmen oder – schlimmer – zu verdeckten Erkennungsregeln führt: Rule-IDs steuern keine Priorität. Entscheidend sind die Matching-Logik, die Regelhierarchie und die Severity/Level-Semantik in Kombination mit der Alert-Schwelle des Managers.

Dieser Beitrag zeigt anhand eines Mikrotik-Firewall-Beispiels, warum eine allgemeine Audit-Regel alle spezifischen Regeln „überfährt“ und wie man das korrekt löst – inklusive einer praxistauglichen Konfiguration, die Zählregeln für Korrelation (if_matched_sid) ermöglicht, ohne das Wazuh-Dashboard mit Low-Value-Alerts zu fluten.


Ausgangslage / Problemstellung (Zusammenfassung, Symptome, Umgebung)

Ein Regelset für Mikrotik-Firewall-Logs besteht aus:

  • einer Basisregel (decoded_as=firewall-base)
  • drei spezifischen Child-Regeln für Traffic-Flows (LAN→WAN, LAN→LAN, WAN→LAN)
  • einer allgemeinen Catch-all-Regel (Audit), die alles erfassen soll, was nicht in die spezifischen Fälle fällt

Trotzdem wird in der Praxis immer die Audit-Regel ausgelöst – obwohl ihre Rule-ID „höher“ ist:

  • Spezifische Regeln: 100002, 100003, 100004 (level 1)
  • Catch-all: 120000 (level 3)

Beispiel-Log:

firewall,info [ANTENNA01]: [ANTENNA01] forward: in:LAN out:WAN2, connection-state:established,snat ... 192.168.3.253:53012->150.184.160.48:8080 ...

Zusätzlich existieren Korrelationsregeln wie:

<rule id="100014" level="12" frequency="20" timeframe="60">
  <if_matched_sid>100004</if_matched_sid>
  <same_srcip />
  <same_dstip />
  <different_dstport />
  <description>Port scan detected: IP $(srcip) scan $(dstip).</description>
</rule>

Dafür müssen die Basis-Child-Regeln mindestens Level 1 haben, da if_matched_sid auf vorherige Matches aufsetzt und typischerweise mit Frequency/Timeframe genutzt wird.


Technische Analyse (Ursachen, betroffene Komponenten, Architekturbezug, Stolpersteine)

1) Rule-ID ist keine Priorität

Wazuh verwendet Rule-IDs als Identifikatoren, nicht als Prioritätsmechanismus. Priorität entsteht durch Regelstruktur (Parent/Child über if_sid) und durch Level/Severity-Konzept sowie Matching-Kriterien. (Die Regeln selbst sind über XML-Syntax und Klassifikation definiert.)

2) Warum die Catch-all-Regel alles „schluckt“

Deine Audit-Regel:

<rule id="120000" level="3">
  <if_sid>100000</if_sid>
  <description>Firewall audit logs</description>
</rule>

hat keine einschränkenden Bedingungen (keine <field>, kein <match>). Damit matcht sie praktisch jeden Event, der den Parent 100000 erreicht.

Kritisch ist dabei der Level-Unterschied:

  • Spezifische Regeln: Level 1
  • Catch-all: Level 3

Wazuh speichert standardmäßig Alerts erst ab einer gewissen Schwere. Standardmäßig liegt die Alert-Schwelle bei Level 3 (<log_alert_level>3</log_alert_level>).
Heißt: Level-1-Regeln können zwar matchen, aber nicht als Alert erscheinen – während Level 3 als Alert durchgeht.

In der Praxis führt das häufig zu der Wahrnehmung: „Immer triggert 120000.“ Tatsächlich passiert meist beides:

  • Spezifische Rules matchen (Level 1 → kein Alert)
  • Catch-all matcht ebenfalls (Level 3 → Alert sichtbar)

3) Warum „alles auf Level 0“ die falsche Abkürzung ist

Level 0 ist „Ignored/No action“ und wird typischerweise zum Unterdrücken genutzt.
Damit zerstörst du aber deine Korrelation: Viele Konstrukte rund um if_matched_sid setzen voraus, dass die Vorregel sinnvoll matcht (und in der Praxis zumindest >0 ist), sonst fehlt dir die stabile Basis für Zähl-/Eskalationsregeln.


Lösung / Best Practices (konkrete Schritte, Konfigurationen, Reihenfolge, Side-Effects)

Zielbild

  • Spezifische Traffic-Regeln sollen matchen und als Basis für if_matched_sid dienen.
  • Sie sollen nicht automatisch Alerts erzeugen.
  • Eine Catch-all-Regel soll nur dann greifen, wenn keine spezifische Regel greift.
  • Optional: Eine zweite Stufe soll bei Bedarf Alerts erzeugen, aber kontrolliert.

Schritt 1: Basisregeln auf Level 2 statt Level 1 setzen

Wenn du das Default-Setup nutzt (Alert-Schwelle 3), sind Level-2-Regeln weiterhin nicht alertend, aber „stärker“ als Level 1 für die interne Priorisierung und als solide Grundlage für Korrelation.

Die Default-Alertschwelle (log_alert_level=3) ist dokumentiert; ebenso, dass per Default erst ab Level 3 Alerts ausgelöst/gespeichert werden.

Schritt 2: Catch-all zweistufig bauen (Stage 1 nicht alertend, Stage 2 optional alertend)

Die entscheidende Idee: Catch-all zuerst nicht alertend, und falls du wirklich auditierbare Alerts willst, baust du eine zweite Stufe, die du später gezielt aktivieren/steuern kannst.

Hier ist eine praxistaugliche Version (direkt einsetzbar):

<group name="mikrotik,">

  <!-- Base rule -->
  <rule id="100000" level="0">
    <decoded_as>firewall-base</decoded_as>
    <description>Logs Firewall</description>
  </rule>

  <!-- LAN -> WAN* (countable, but not alerting if log_alert_level is 3) -->
  <rule id="100002" level="2">
    <if_sid>100000</if_sid>
    <field name="in">LAN</field>
    <field name="out">WAN|WAN2|WAN3</field>
    <description>Outbound stream detected: $(in) to $(out).</description>
  </rule>

  <!-- LAN -> LAN -->
  <rule id="100003" level="2">
    <if_sid>100000</if_sid>
    <field name="in">LAN</field>
    <field name="out">LAN</field>
    <description>Internal traffic detected at the antenna.</description>
  </rule>

  <!-- WAN* -> LAN -->
  <rule id="100004" level="2">
    <if_sid>100000</if_sid>
    <field name="in">WAN|WAN2|WAN3</field>
    <field name="out">LAN</field>
    <description>External traffic detected at the antenna.</description>
  </rule>

  <!-- Stage 1: non-alerting catch-all (still matchable for chaining) -->
  <rule id="120000" level="1">
    <if_sid>100000</if_sid>
    <description>Firewall audit logs (catch-all, non-alerting)</description>
    <group>audit,</group>
  </rule>

  <!-- Stage 2: optional alerting catch-all -->
  <rule id="120001" level="3">
    <if_sid>120000</if_sid>
    <description>Firewall audit logs (catch-all, alerting)</description>
    <group>audit,</group>
  </rule>

</group>

Warum das funktioniert:

  • Die spezifischen Regeln (100002–100004) sind Level 2 → sie bleiben bei Default-Alertschwelle 3 leise, sind aber als „Basis-Events“ sauber nutzbar.
  • 120000 ist der Catch-all als Level 1 → ebenfalls leise, aber als „Stage 1“ für Ketten nutzbar.
  • 120001 erzeugt Alerts (Level 3) nur, wenn 120000 matched – und du kannst ihn jederzeit anpassen, z. B. mit weiteren Bedingungen oder Gruppen, um die Audit-Flut zu kontrollieren.

Schritt 3: Korrelation mit if_matched_sid sauber auf Level-2-Regeln aufsetzen

Dein Portscan-Beispiel kann so bleiben – es setzt auf 100004 auf und feuert erst bei Frequency/Timeframe, was genau dem Zweck entspricht. Die Syntax und der Einsatz von if_matched_sid sind in der Rules-Syntax dokumentiert.

Side-Effects und Betriebsregeln

  • Wenn du <log_alert_level> im ossec.conf verändert hast (z. B. auf 2), dann würden Level-2-Regeln plötzlich Alerts erzeugen. Prüfe daher die Alert-Schwelle auf dem Manager.
  • Level 0 sollte nicht als „Zählregel“ missbraucht werden; es ist primär zum Ignorieren gedacht.

Lessons Learned / Best Practices (präventive Maßnahmen, Betrieb, Skalierung)

  • Nie mit Rule-ID argumentieren: IDs sind nur Identifikatoren, keine Priorität.
  • Baue Regeln immer als Hierarchie: decoded_as → Parent (Basis) → spezifische Child-Rules → Catch-all als letztes Sicherheitsnetz.
  • Nutze das Level-Konzept strategisch:
    • Level 1–2: „zählbar, aber leise“ (bei Default log_alert_level=3)
    • Level ≥3: „alertend“
    • Level 0: „ignorieren“
  • Korrelation (if_matched_sid + frequency/timeframe) braucht eine stabile, wiedererkennbare Basisregel – idealerweise nicht Level 0.
  • Dokumentiere in Runbooks immer die Alert-Schwelle (log_alert_level) – sie ist der häufigste Grund, warum „Regeln scheinbar nicht triggern“.

Fazit (knappe Zusammenfassung mit Mehrwert)

Wenn eine Catch-all-Regel deine spezifischen Regeln „überfährt“, ist fast nie die Rule-ID schuld – sondern eine Kombination aus fehlenden Bedingungen, Level/Severity und der Alert-Schwelle des Managers. Mit Level-2-Basisregeln (für Korrelation ohne Alert-Flut) und einer zweistufigen Catch-all-Strategie bekommst du beides: saubere Abdeckung aller Mikrotik-Firewall-Logs und belastbare if_matched_sid-Korrelation, ohne das SIEM mit Low-Value-Alerts zu überladen.


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/C07CCCCGHHP/p1769030097566099