Tools · Endpoint visibility · 05 / 05
Wazuh: watch configuration files and write a local rule
Turn on Wazuh file integrity monitoring for a lab server’s configuration folders, test a custom rule with wazuh-logtest, and read the resulting alert in alerts.json.
Agent, manager, rules
A Wazuh agent on each host collects logs, file integrity events, and inventory, and sends them to a manager. The manager decodes each event, matches it against rules, and writes alerts above a level threshold. Custom rules live in local_rules.xml and use IDs from 100000 upward so upgrades never overwrite them.
File integrity monitoring
The syscheck block in the agent’s ossec.conf lists folders to watch. realtime uses inotify on Linux for immediate events; whodata adds the user and process that made a change where auditd is available.
<syscheck> <frequency>43200</frequency> <directories check_all="yes" realtime="yes" report_changes="yes">/etc/ssh,/etc/sudoers.d</directories> <directories check_all="yes" whodata="yes">/etc/systemd/system</directories> <ignore type="sregex">.swplt;/ignore> </syscheck> sudo systemctl restart wazuh-agent
A local rule, tested before deployment
This rule raises the level of any integrity change under /etc/sudoers.d. It builds on the stock syscheck rules for modified (550), deleted (553), and added (554) files. wazuh-logtest replays a sample event and shows which rule fired, so the rule can be checked without waiting for a real change.
<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="local,syscheck,">
<rule id="100210" level="12">
<if_sid>550,553,554</if_sid>
<field name="file">^/etc/sudoers.d/</field>
<description>sudoers.d changed on $(agent.name): $(file)</description>
<mitre><id>T1548.003</id></mitre>
</rule>
</group>
sudo /var/ossec/bin/wazuh-logtestWorked case: reading the alert
An admin adds a file to /etc/sudoers.d on lab-web-02. Within seconds the manager writes an alert. whodata shows the change came from a sudo session by the admin account, during a change window recorded in the ticket, so the alert is closed with the ticket number. The same rule would have paged on a change outside a window or from an unexpected account.
jq 'select(.rule.id=="100210") | {time: .timestamp, agent: .agent.name, file: .syscheck.path, user: .syscheck.audit.user.name, process: .syscheck.audit.process.name}' /var/ossec/logs/alerts/alerts.json
{"time":"2026-11-02T10:41:07.512+0000","agent":"lab-web-02",
"file":"/etc/sudoers.d/deploy","user":"ops-amir","process":"/usr/bin/tee"}Diagram
A file change becomes a Wazuh alert
- Watch
syscheck monitors chosen folders.
<directories realtime="yes">/etc/sudoers.d</directories>
- Decode
The manager turns the event into fields.
syscheck.path, syscheck.audit.user.name
- Match
A local rule raises the level for this path.
<rule id="100210" level="12"><if_sid>550,553,554</if_sid>
- Review
Compare the change with the change window.
jq 'select(.rule.id=="100210")' alerts.json
Check yourself
1. Why do custom Wazuh rules use IDs of 100000 and above?
2. What does whodata add to an integrity event?
3. How can a new rule be checked without waiting for a real event?