Gatekeeper inqaba-security/gatekeeper
v1.x

Wazuh

The Wazuh sink appends events as JSON lines to a file that the Wazuh agent tails — Wazuh's recommended pattern for custom application logs. Every ECS field is available to the manager's JSON decoder, so writing rules requires no custom decoder work.

Laravel configuration#

SIEM_SINKS=wazuh
SIEM_WAZUH_PATH=/var/log/gatekeeper/events.json

The sink creates the directory (0750) and file (0640) if missing. Make sure the path is:

  • writable by your PHP / queue worker user, and
  • readable by the wazuh agent user (add it to your app group, or relax the permissions config key).
Key Env Default
sinks.wazuh.path SIEM_WAZUH_PATH storage/logs/siem/events.json
sinks.wazuh.permissions 0640

Agent configuration#

Add a localfile block to the agent's ossec.conf:

<localfile>
  <log_format>json</log_format>
  <location>/var/log/gatekeeper/events.json</location>
</localfile>

Restart the agent and verify events flow with php artisan siem:test --sink=wazuh.

Manager rules#

Match on any ECS field. A practical starter ruleset:

<group name="laravel,gatekeeper,">

  <!-- Base rule: everything from Gatekeeper -->
  <rule id="100100" level="3">
    <decoded_as>json</decoded_as>
    <field name="event.module">gatekeeper</field>
    <description>Gatekeeper: $(event.action) from $(source.ip)</description>
  </rule>

  <!-- Honeypot hits: high-confidence attack traffic -->
  <rule id="100101" level="10">
    <if_sid>100100</if_sid>
    <field name="event.action">honeypot_route_hit</field>
    <description>Gatekeeper honeypot hit from $(source.ip): $(url.path)</description>
    <mitre><id>T1595</id></mitre>
  </rule>

  <!-- Brute force -->
  <rule id="100102" level="12">
    <if_sid>100100</if_sid>
    <field name="event.action">brute_force_detected</field>
    <description>Brute force against $(service.name) from $(source.ip)</description>
    <mitre><id>T1110</id></mitre>
  </rule>

  <!-- IDOR / enumeration -->
  <rule id="100103" level="10">
    <if_sid>100100</if_sid>
    <field name="event.action">resource_enumeration_detected</field>
    <description>Resource enumeration on $(service.name) from $(source.ip)</description>
    <mitre><id>T1119</id></mitre>
  </rule>

  <!-- Correlation: failed logins followed by a success from the same IP -->
  <rule id="100110" level="12" frequency="1" timeframe="300">
    <if_sid>100100</if_sid>
    <field name="event.action">login_succeeded</field>
    <if_matched_sid>100102</if_matched_sid>
    <same_field>source.ip</same_field>
    <description>Successful login from $(source.ip) shortly after brute force detection</description>
    <mitre><id>T1110</id><id>T1078</id></mitre>
  </rule>

</group>

The correlation rule at the end is the payoff of application-layer telemetry: "brute force then a successful login from the same IP" is a compromised-account signal that infrastructure logs alone can't produce.

Log rotation#

Rotate with copytruncate so the agent keeps its file handle:

/var/log/gatekeeper/events.json {
    daily
    rotate 14
    compress
    missingok
    notifempty
    copytruncate
}

Alternative: syslog to the Wazuh manager#

If running an agent isn't possible (serverless, containers without sidecars), point the syslog sink at the Wazuh manager's <remote> syslog listener instead.