AlistoIR Blog Topic

Detecting Fail2Ban Bans in Wazuh SIEM Using Custom Decoders and Rules

June 16, 2026 · By Oliver Roca
A practical step-by-step guide for collecting Fail2Ban events, building a custom decoder, creating Wazuh rules, and validating the alert pipeline end to end.

A practical step-by-step guide for collecting Fail2Ban events, building a custom decoder, creating Wazuh rules, and validating the alert pipeline end to end.

Audience: Wazuh admins, SOC analysts, blue teams, and Linux defenders

Fail2Ban is great at blocking repeated abusive activity such as SSH brute-force attempts, but many teams stop at the ban itself and never forward that security signal into their SIEM. That means the blocking happens on the server, but the SOC does not get clean visibility, correlation, or alerting inside Wazuh.

In this tutorial, we will connect Fail2Ban to Wazuh the right way so that:

  • Wazuh detects when an IP is banned
  • Wazuh detects when an IP is unbanned
  • SSH-related bans can be treated with higher severity

What We Will Build

At the end of this tutorial, your flow will look like this:

  1. Fail2Ban bans an IP on a Linux server
  2. The event is written to a Wazuh-collected log source
  3. Wazuh decodes the event
  4. Wazuh matches a custom rule
  5. The alert appears in the Wazuh dashboard and alert logs

Lab Example

This tutorial assumes:

  • a Linux server running Fail2Ban and the Wazuh agent (Check the link below for the

Step-by-Step Fail2Ban setup/ configuration)

  • a Wazuh manager where custom decoders and rules will be added
  • SSH is protected by the sshd jail

Why Fail2Ban Alerts Sometimes Do Not Show in Wazuh

Before jumping into the setup, here is the most important lesson.

A common reason Fail2Ban rules do not work in Wazuh is a log format mismatch.

For example, Fail2Ban may log into:

``` text /var/log/fail2ban.log


with lines like:

``` text
2026-06-12 11:37:00,197 fail2ban.actions [24092]: NOTICE [sshd] Ban 203.0.113.55

That format is not the same as standard syslog. If your Wazuh decoder depends on program_name such as fail2ban.actions, but Wazuh is reading the file as regular syslog, the pre-decoder may not extract fields the way you expect. The result is simple: the custom rule never fires.

The cleaner approach is to make Fail2Ban log in a syslog-compatible destination that Wazuh already collects.

Step 1: Configure Fail2Ban to Log in a Wazuh-Friendly Format

On the monitored Linux server, open:

nano /etc/fail2ban/fail2ban.conf

Find this setting:

logtarget = /var/log/fail2ban.log

Change it to:

logtarget = SYSLOG

Then restart Fail2Ban:

sudo systemctl restart fail2ban
sudo fail2ban-client status

After the restart, confirm that Fail2Ban entries are now visible in syslog:

sudo grep fail2ban /var/log/syslog | tail -n 20

You should see lines similar to:

2026-06-12T13:02:24.914836+08:00 alistoir-prod fail2ban.actions[176447]: NOTICE [sshd] Ban 203.0.113.55
2026-06-12T13:02:27.047146+08:00 alistoir-prod fail2ban.actions[176447]: NOTICE [sshd] Unban 203.0.113.55

Step 2: Make Sure the Wazuh Agent Collects the Right Log Source

On the monitored server, your Wazuh agent should collect either:

  • journald
  • /var/log/syslog
  • or both

A common working setup looks like this:

<localfile>
<log_format>journald</log_format>
<location>journald</location>
</localfile>

and/or:

<localfile>
<log_format>syslog</log_format>
<location>/var/log/syslog</location>
</localfile>

If you manage agents centrally, this can also be placed in shared agent.conf.

After updating the agent config, restart the Wazuh agent:

sudo systemctl restart wazuh-agent

Step 3: Create the Fail2Ban Decoder on the Wazuh Manager

On the Wazuh manager, create:

nano /var/ossec/etc/decoders/fail2ban.xml

Use the following decoder:

<decoder name="fail2ban-base">
<program_name>^fail2ban.actions|^fail2ban.filter</program_name>
</decoder>

<decoder name="fail2ban-ban">
<parent>fail2ban-base</parent>
<use_own_name>true</use_own_name>
<prematch type="pcre2">^NOTICE\s+\[[^\]]+\]\s+Ban\s+</prematch>
<regex type="pcre2">^\s*NOTICE\s+\[([^\]]+)\]\s+Ban\s+((?:\d{1,3}\.){3}\d{1,3}|[A-Fa-f0-9:]+)\s*$</regex>
<order>jail,srcip</order>
</decoder>

<decoder name="fail2ban-unban">
<parent>fail2ban-base</parent>
<use_own_name>true</use_own_name>
<prematch type="pcre2">^NOTICE\s+\[[^\]]+\]\s+Unban\s+</prematch>
<regex type="pcre2">^\s*NOTICE\s+\[([^\]]+)\]\s+Unban\s+((?:\d{1,3}\.){3}\d{1,3}|[A-Fa-f0-9:]+)\s*$</regex>
<order>jail,srcip</order>
</decoder>

What This Decoder Does

  • fail2ban-base matches events where the process name is fail2ban.actions or fail2ban.filter
  • fail2ban-ban extracts the jail name and source IP for ban events
  • fail2ban-unban extracts the jail name and source IP for unban events
image2

Step 4: Create the Wazuh Rules

On the Wazuh manager, create:

nano /var/ossec/etc/rules/fail2ban.xml

Use these rules:

<group name="fail2ban,security,">
<rule id="110500" level="8">
<decoded_as>fail2ban-ban</decoded_as>
<description>Fail2ban banned an IP address.</description>
<group>fail2ban,ban,authentication_failures,</group>
</rule>

<rule id="110503" level="12">
<if_sid>110500</if_sid>
<field name="jail">^sshd$</field>
<description>Fail2ban blocked a source IP in the SSH service jail.</description>
<group>fail2ban,ban,sshd,bruteforce,authentication_failures,</group>
</rule>

<rule id="110501" level="5">
<decoded_as>fail2ban-unban</decoded_as>
<description>Fail2ban unbanned an IP address.</description>
<group>fail2ban,unban,authentication_failures,</group>
</rule>

<rule id="110502" level="10" frequency="5" timeframe="300" ignore="300">
<if_matched_sid>110500</if_matched_sid>
<description>Multiple Fail2ban bans detected within 5 minutes.</description>
<group>fail2ban,multiple_bans,bruteforce,authentication_failures,</group>
</rule>

<rule id="110504" level="14" frequency="5" timeframe="300" ignore="300">
<if_matched_sid>110503</if_matched_sid>
<description>Multiple SSH-related Fail2ban bans detected within 5 minutes.</description>
<group>fail2ban,multiple_bans,sshd,bruteforce,authentication_failures,</group>
</rule>
</group>

Why These Rules Are Useful

  • 110500 catches any Fail2Ban ban event
  • 110503 raises severity when the jail is sshd
  • 110501 tracks unban events
  • 110502 and 110504 help identify bursts of abuse

This gives you both visibility and prioritization.

image3

Step 5: Restart the Wazuh Manager

After adding the decoder and rules, restart the manager:

sudo systemctl restart wazuh-manager

Or, depending on your environment:

sudo /var/ossec/bin/wazuh-control restart

Step 6: Test the Setup Safely

A safe way to test without waiting for a real attacker is to use a documentation IP address.

Run:

sudo fail2ban-client set sshd banip 203.0.113.55
sleep 2
sudo fail2ban-client set sshd unbanip 203.0.113.55

Then check the Wazuh manager alerts:

sudo grep -Rni '203.0.113.55' /var/ossec/logs/alerts/alerts.log /var/ossec/logs/alerts/alerts.json

If everything is working, you should see matches similar to:

Rule: 110503 (level 12) -> 'Fail2ban blocked a source IP in the SSH service jail.'
Src IP: 203.0.113.55

and:

Rule: 110501 (level 5) -> 'Fail2ban unbanned an IP address.'
Src IP: 203.0.113.55
image4

Step 8: Validate the Fail2Ban Alert in AlistoIR

After confirming that Wazuh successfully detects the Fail2Ban ban event, the next step is to validate that the alert is also visible inside AlistoIR.

This is important because Wazuh confirms that the security event was detected, while AlistoIR helps the SOC team investigate, document, enrich, and respond to the incident in a structured way.

In this example, the Fail2Ban event was generated when the sshd jail blocked the source IP address 203.0.113.55. Wazuh matched the event using the custom Fail2Ban decoder and rule, and then the alert was forwarded to AlistoIR for investigation.

Expected AlistoIR Output

Inside the AlistoIR dashboard, the Fail2Ban alert should appear in the alert queue or incident investigation view.

The alert should show important details such as:

  • Alert title or rule description
  • Severity level
  • Source IP address
  • Affected Linux server or Wazuh agent
  • Wazuh rule ID
  • Jail name, such as sshd
  • Raw event or related log evidence
  • Timestamp of the ban event

Example alert details:

Alert Name: Fail2ban blocked a source IP in the SSH service jail
Rule ID: 110503
Severity: High
Source IP: 203.0.113.55
Jail: sshd
Event Type: Fail2Ban Ban
Status: New / Queued
Source: Wazuh

AlistoIR Dashboard Screenshot

image5 image6

Figure: AlistoIR dashboard showing the Fail2Ban alert generated from a Wazuh custom rule.

The screenshot clearly shows that the Fail2Ban alert was successfully ingested into AlistoIR. This helps prove that the full detection pipeline is working from Linux server logs, to Wazuh decoding and rules, and finally to AlistoIR alert visibility.

What to Check in AlistoIR

When reviewing the alert in AlistoIR, validate the following:

  1. The alert title matches the Wazuh rule description.
  2. The source IP is correctly extracted.
  3. The affected host or agent name is visible.
  4. The severity is mapped properly.
  5. The raw log or evidence is available for review.
  6. The alert can be investigated, documented, and escalated if needed.

If the alert is visible in Wazuh but not in AlistoIR, check the Wazuh-to-AlistoIR forwarding configuration, the API key, the endpoint URL, and the ingestion logs.

Final Validation Flow

Once everything is working, the complete flow should look like this:

validation flow

This confirms that Fail2Ban is not only blocking attacks locally but also producing useful security visibility for the SOC team through Wazuh and AlistoIR.

Troubleshooting Tips

If the rules still do not fire, check these items first:

1. Fail2Ban is logging to the wrong place

If Fail2Ban is still writing only to /var/log/fail2ban.log, your decoder may not match as expected.

2. Wazuh is not collecting the correct source

Make sure the agent is actually collecting journald or /var/log/syslog.

3. The process name is different

Your decoder expects:

  • fail2ban.actions
  • fail2ban.filter

If your distribution logs a different process name, update the decoder accordingly.

4. Duplicate alerts appear

If you collect both journald and /var/log/syslog, you may see the same event twice. This is normal if both sources contain the same message. If you want cleaner alerts, choose one main source for Fail2Ban visibility.

Recommended Production Tips

  • Keep custom decoders and rules clearly named and documented
  • Use a dedicated ID range for your organization
  • Raise severity for service-specific jails like sshd
  • Test with known safe IPs before relying on the alert in production

Final Thoughts

Fail2Ban is already doing useful defensive work on many Linux servers, but integrating it into Wazuh gives your team much better visibility. Once the logs are normalized correctly and the decoder is aligned with the actual log format, Wazuh can turn simple local bans into actionable SIEM alerts.

The biggest takeaway is this: always verify the real log format first. Most Fail2Ban-to-Wazuh issues are not caused by the rule logic itself, but by the log source and parsing path.

If you build the pipeline carefully, the result is simple, reliable, and very effective for SSH brute-force monitoring.

References