15. Optimizing Wazuh Ruleset
PT FPT Metrodata Indonesia (FMI) is a joint venture between FPT IS and Metrodata Electronics, focusing on providing Cybersecurity-as-a-Service—including SOC, managed security, professional services, consulting, and threat intelligence—to support Indonesia’s rapidly growing digital economy. FMI is expanding into AI and cloud GPU services to deliver innovative protection and solutions for enterprises. Learn more at https://fmisec.com.
Wazuh is a free and open source security monitoring platform that protects IT assets against threats that target the core pillars of cybersecurity. It is a unified XDR and SIEM that helps you monitor and promptly respond to security incidents across workloads in the cloud and on-premises environments.
The Wazuh architecture includes the Wazuh agent installed on servers or devices, and Wazuh central components (Wazuh Server, Wazuh Indexer, Wazuh Dashboard). The Wazuh agents collect system events (logs, Sysmon, etc.) and send them to the Wazuh Server for analysis. The Wazuh server performs pre-decoding and decoding of logs, then matches them against the set of rules to generate alerts and forwards them to the Wazuh indexer for indexing and storage. These alerts can then be queried on the Wazuh dashboard. The Wazuh log analysis engine uses decoders to extract important fields from logs (e.g., program name, user, IP), and then applies rules to detect suspicious patterns. Each rule has an ID and a level indicating the severity. Rules can also be grouped and linked with MITRE ATT&CK for standardized alerting.
False Positives and Low Performance
Two common problems in Wazuh when analyzing logs are false positives and slow processing. False positives occur when rules are too broad or triggering conditions are loose, leading to many irrelevant alerts. This overloads security teams. Wazuh categorizes alert levels from 0 to 15, where level 0 is used to ignore irrelevant events, reducing false positives.
Performance issues arise when the ruleset is too large or contains complex regex, causing CPU and memory overhead for the analysis engine. Using excessive regex for filtering increases processing time. Optimization techniques include frequency/timeframe attributes to group similar events, CDB lists for whitelists/blacklists, and sibling decoders to simplify parsing. Properly configuring conditions like if_group or if_sid improves specificity and system performance.
Rules Optimization Techniques
Overwriting and Editing Default Rules
Wazuh allows overwriting default rules by copying them into local_rules.xml and adding overwrite="yes", then modifying attributes such as level or group. For example, when Wazuh detects an SSH log like "invalid user", the default rule 5700 only has level = 5, so it might be ignored.
You want to raise the alert level to 10 and add it to the "bruteforce" group:
<group name="syslog,sshd,">
<rule id="5710" level="10" overwrite="yes">
<if_sid>5700</if_sid>
<match>illegal user|invalid user</match>
<description>sshd: Attempt to login using a non-existent user</description>
</rule>
</group>
Creating Custom Rules
Custom rules in Wazuh allow users to define specific conditions or patterns in log data that are relevant to their unique environment, applications, or security requirements. The default rules cover many events, but custom rules refine detections, add exceptions, and extend coverage to your unique needs.
For small tweaks such as a few new rules, adjusted alert levels, or updated descriptions, edit local_rules.xml. For larger or more structured changes like creating multiple rule groups for different services or projects, create a new XML file in the /var/ossec/etc/rules/ directory. When creating custom rules, set the rule IDs in the 100000–120000 range to avoid conflicts.
Using Frequency and Timeframe
The frequency and timeframe attributes are used to control how many times a rule must match within a timeframe to trigger an alert, reducing alert flooding that could overwhelm security analysts.
For example, the rule below is triggered when a user authentication fails.
<rule id="100010" level="8">
<if_group>invalid_login</if_group>
<description>User authentication failed</description>
<group>access_control,authentication_failed</group>
</rule>
The rule below is triggered when rule ID 100010 above is matched 5 times within the last two minutes. Rule ID 100011 may indicate a possible brute-force attempt on a user account.
<rule id="100011" level="10" timeframe="120" frequency="5">
<if_matched_sid>100010</if_matched_sid>
<same_srcuser />
<description>Multiple login failures were triggered by the same user$.</description>
<group>access_control,authentication_failed</group>
</rule>
Using Decoders and Sibling Decoders
Decoders extract fields from logs. For small changes, such as adding one or two custom decoders, you should add them directly to the file local_decoder.xml or creating new decoder files in the /var/ossec/etc/decoders/ directory for changes on a larger scale:

Example:
<decoder name="example">
<program_name>^example</program_name>
</decoder>
<decoder name="example">
<parent>example</parent>
<regex>User '(\w+)' logged from '(\d+\.\d+\.\d+\.\d+)'</regex>
<order>user, srcip</order>
</decoder>
Sibling decoders break complex logs into smaller decoders, improving parsing accuracy and performance.
Example:
Suppose you have the following custom application log:
192.168.1.10 - - [11/Oct/2025:10:20:33 +0700] "GET /index.html HTTP/1.1" 200 512
You want extract field IP, HTTP method, URL, status code, và bytes sent.
If you using regex, it would make the decoder long and error-prone, so we split it using Sibling Decoders.
<!-- Main decoder to identify Apache logs -->
<decoder name="apache_access">
<program_name>apache</program_name>
<regex>(\S+)</regex>
<order>srcip</order>
</decoder>
<!-- Child decoder 1: extract HTTP method and URL -->
<decoder name="apache_request">
<parent>apache_access</parent>
<regex>"(\S+)\s(\S+)\sHTTP/\d\.\d"</regex>
<order>http_method,url</order>
</decoder>
<!-- Child decoder 2: extract status code and bytes -->
<decoder name="apache_response">
<parent>apache_request</parent>
<regex>"\s(\d{3})\s(\d+)$"</regex>
<order>status,bytes</order>
</decoder>
Using the decoders above, the following fields are extracted from the log. After that, we will have result like this:
srcip: 192.168.1.10
http_method: GET
url: /index.html
status: 200
bytes: 512
Pre-filtering Logs per System
Filter logs at the source to reduce noise. For Windows, use Event Channel Query to collect only relevant events. For Sysmon, include only events of interest in XML configuration. On Linux, focus on important log files and ignore high-volume debug logs.
Using CDB Lists for Whitelist/Blacklist
Wazuh is able to check if a field extracted during the decoding phase is in a CDB list (constant database). The main use case of this feature is to create a white/black list of users, file hashes, IP addresses, or domain names.
Define the CDB list within the <ruleset> block of the /var/ossec/etc/ossec.conf file. For example, to define the CDB list /var/ossec/etc/lists/list-IP, add the relative path etc/lists/list-IP as highlighted below:
<ruleset>
<!-- Default ruleset -->
<decoder_dir>ruleset/decoders</decoder_dir>
<rule_dir>ruleset/rules</rule_dir>
<rule_exclude>0215-policy_rules.xml</rule_exclude>
<list>etc/lists/audit-keys</list>
<list>etc/lists/amazon/aws-eventnames</list>
<list>etc/lists/security-eventchannel</list>
<list>etc/lists/list-IP</list>
<!-- User-defined ruleset -->
<decoder_dir>etc/decoders</decoder_dir>
<rule_dir>etc/rules</rule_dir>
</ruleset>
CDB lists provide fast lookups for whitelists and blacklists. Example:
<rule id="110700" level="10">
<if_group>json</if_group>
<list field="srcip" lookup="address_match_key">etc/lists/List-one</list>
<description>IP blacklisted in LIST ONE</description>
<group>list1,</group>
</rule>
Update lists easily without modifying rules.
Analyzing Logs via wazuh-archives
Check archives to see which logs did not trigger rules. This identifies missing rules or unnecessary rules. Grep archives.json for sample events to validate coverage.
Analyzing logs through wazuh-archives helps optimize Wazuh rules by allowing you to review all raw, unfiltered logs stored by the system. This enables you to:
Identify log patterns that do not have corresponding existing decoders or rules might miss.
Query the logs collected by the Wazuh agent, which is helpful during threat hunting.
Detect false positives or unnecessary triggers in current rules.
Create or refine custom decoders and rules that better match your real-world log data.
Tools to Support Rule Tuning
Wazuh Logtest: test decoders and rules.
Wazuh Dashboard: analyze alerts, visualize patterns.
Wazuh Query Language (WQL): query alerts in detail.
Other tools: regex testing, stats clearing, Elasticsearch queries.
Strategies for Maintaining an Effective Ruleset
Regularly review alerts and threat intelligence.
Use Git for version control of local_rules.xml and local_decoder.xml.
Consider Ruleset as Code (RaC) for CI/CD deployment. https://wazuh.com/blog/wazuh-ruleset-as-code-rac/
Document rules with descriptions and reasons for creation/modification.
Tag rules with MITRE ATT&CK for coverage analysis.





