Fail2Ban SSH Ban Integration with n8n Webhooks

Document Owner: IT / Network

Scope: Linux servers running OpenSSH where Fail2Ban enforces bans and notifies n8n via webhook.

Goal: Permanently ban brute-force SSH IPs locally (bantime = -1) and send events to n8n for enrichment and alerting.


Table of Contents

  1. How It Works
  2. Architecture
  3. Prerequisites
  4. SOP 1 — Install Fail2Ban
  5. SOP 2 — Create n8n Webhook Action
  6. SOP 3 — Configure SSH Jail (Permanent Ban + Multi-Action)
  7. SOP 4 — Validate and Test
  8. SOP 5 — Operations (Monitoring and Health Checks)
  9. SOP 6 — Manual Unban
  10. SOP 7 — Incident Recovery (Accidental Self-Ban)
  11. SOP 8 — Secure the Webhook (Production Standard)
  12. SOP 9 — Change Control

How It Works

Every SSH server on the internet faces constant brute-force login attempts. Fail2Ban monitors your SSH log files for repeated authentication failures and automatically blocks offending IP addresses at the firewall level. In this configuration, bans are permanent (bantime = -1), meaning an attacker does not get a second chance.

On its own, Fail2Ban only protects the individual server it runs on. This setup extends it by adding a webhook action that sends ban and unban events to an n8n workflow. n8n acts as the centralized brain — it can enrich each event with threat intelligence (such as IP geolocation or abuse reports), send notifications to Slack or email, and correlate attacks across multiple servers. The result is a two-layer system: Fail2Ban handles real-time local enforcement, and n8n handles visibility and alerting across your entire fleet.

The webhook is designed to be failure-safe. If n8n is unreachable, the curl command times out after 8 seconds and exits silently. The firewall ban still applies regardless. Enforcement never depends on the webhook succeeding.


Architecture

Fail2Ban serves as the local enforcement layer, while n8n provides centralized alerting and intelligence. The data flows as follows:

  • sshd writes authentication failures to /var/log/auth.log (or journald, depending on your distribution).
  • Fail2Ban detects brute-force patterns based on the jail’s maxretry and findtime thresholds, then applies a local firewall ban.
  • n8n receives webhook events and can enrich them with IP intelligence, send notifications to Slack, and correlate bans across multiple servers.
sshd logs → Fail2Ban jail → firewall ban (%(action_)s)
                         ↘︎ webhook notify → n8n workflow

Prerequisites

Before starting, confirm the following are in place:

  • Ubuntu/Debian server (or a compatible distribution)
  • OpenSSH installed and running
  • SSH authentication log available at /var/log/auth.log
  • Outbound HTTPS traffic allowed to your n8n domain
  • An n8n webhook endpoint created and set to accept POST requests

SOP 1 — Install Fail2Ban

Procedure

  1. Install the package and enable the service:
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban

Validation

Confirm that Fail2Ban is running and responsive:

systemctl is-active fail2ban
sudo fail2ban-client ping

You should see active and pong in the output, respectively.


SOP 2 — Create n8n Webhook Action

This step creates a custom Fail2Ban action that sends a webhook to n8n whenever an IP is banned or unbanned. The action does not replace the firewall ban — it runs alongside it.

Procedure

  1. Create the action definition file:
sudo nano /etc/fail2ban/action.d/n8n-webhook.conf
  1. Paste the following configuration:
[Definition]

# NOTE:
# - actionban/actionunban are all we need for webhook notifications.
# - actionstart/actionstop/actioncheck are intentionally omitted
#   because webhooks do not require lifecycle setup.

actionban = curl -sS -m 8 -X POST "<n8n_url>" \
  -H "Content-Type: application/json" \
  -d '{"event":"fail2ban_ban","jail":"<name>","ip":"<ip>","fq_hostname":"<fq_hostname>","failures":"<failures>","time":"<time>","token":"<token>"}' \
  >/dev/null 2>&1 || true

actionunban = curl -sS -m 8 -X POST "<n8n_url>" \
  -H "Content-Type: application/json" \
  -d '{"event":"fail2ban_unban","jail":"<name>","ip":"<ip>","fq_hostname":"<fq_hostname>","time":"<time>","token":"<token>"}' \
  >/dev/null 2>&1 || true

[Init]
n8n_url =
token =
fq_hostname =

Design Notes

  • No actionstart/stop/check: Webhooks do not require lifecycle hooks. Only ban and unban events matter for alerting.
  • Timeout (-m 8): Limits the curl request to 8 seconds so Fail2Ban does not hang if n8n is slow or unreachable.
  • Failure safety (|| true): Ensures that a webhook failure never prevents the firewall ban from being applied. The ban always succeeds, even if the notification does not.

SOP 3 — Configure SSH Jail (Permanent Ban + Multi-Action)

This jail tells Fail2Ban to watch for SSH brute-force attempts, permanently ban offending IPs, and notify n8n at the same time.

Procedure

  1. Edit the jail configuration:
sudo nano /etc/fail2ban/jail.local
  1. Add or replace the SSH jail with the following:
[sshd]
enabled = true
port = 22
filter = sshd
logpath = /var/log/auth.log
backend = auto

maxretry = 5
findtime = 600
bantime = -1

# Multi-action:
# 1) %(action_)s = default firewall ban action
# 2) n8n-webhook = notify n8n
action = %(action_)s
         n8n-webhook[n8n_url="https://YOUR_N8N_DOMAIN/webhook/fail2ban", token="YOUR_TOKEN", fq_hostname="YOURSERVER.example.com"]

Replace the three placeholder values with your actual n8n webhook URL, authentication token, and server hostname.

Key Settings Explained

  • maxretry = 5: An IP is banned after 5 failed login attempts.
  • findtime = 600: Those 5 failures must occur within 600 seconds (10 minutes).
  • bantime = -1: Bans are permanent. The IP stays blocked until manually unbanned (see SOP 6).
  • Multi-action: Two actions run on every ban — the default firewall rule and the n8n webhook notification.

Critical Token Warning

Avoid using the # character in your token. Fail2Ban’s configuration parser treats # as the start of a comment, which will silently truncate your token value.

  • Safe: SecureToken_ABC123
  • If you must use #: Escape it with a backslash (e.g., abc\#123)

Apply the Changes

sudo systemctl restart fail2ban

SOP 4 — Validate and Test

Check the SSH jail status

sudo fail2ban-client status sshd

You should see the jail listed as active with the correct filter and action count.

Review Fail2Ban logs

sudo tail -n 200 /var/log/fail2ban.log

Look for any configuration errors or warnings. A clean startup means the jail loaded correctly.

Trigger a test ban

To test the full pipeline without locking yourself out:

  1. Use a separate source IP — not your admin IP.
  2. Attempt several failed SSH logins from that IP to exceed the maxretry threshold.
  3. Confirm the ban appears in fail2ban-client status sshd.

Verify the n8n webhook

  • Open your n8n workflow and confirm the webhook executed.
  • Check that the payload includes event, ip, fq_hostname, and jail.

SOP 5 — Operations (Monitoring and Health Checks)

Daily health checks

Run the following commands to verify that Fail2Ban is active and the SSH jail is functioning:

systemctl is-active fail2ban
sudo fail2ban-client status sshd

Investigate suspicious spikes

If you notice a sudden increase in bans, review the most recent activity:

sudo tail -n 200 /var/log/fail2ban.log
sudo grep "Ban" /var/log/fail2ban.log | tail -n 50

Expected behavior

  • Fail2Ban remains active across reboots.
  • Banned IPs accumulate gradually over time.
  • No repeated curl or webhook failures appear in the logs.

SOP 6 — Manual Unban

Because bantime = -1 makes bans permanent, you will need to unban IPs manually when necessary.

List currently banned IPs

sudo fail2ban-client status sshd

Unban a specific IP

sudo fail2ban-client set sshd unbanip 1.2.3.4

Replace 1.2.3.4 with the actual IP address you want to unban. This also triggers the actionunban webhook, so n8n will be notified of the unban.


SOP 7 — Incident Recovery (Accidental Self-Ban)

If you accidentally lock yourself out by triggering the ban threshold from your own IP, follow these steps.

Recovery steps

  1. Access the server through an alternative method — cloud provider console, physical access, or out-of-band management.
  2. Unban your IP:
sudo fail2ban-client set sshd unbanip YOUR.PUBLIC.IP.ADDRESS

Prevention: Whitelist your admin IP

If your admin IP address is static, you can prevent accidental self-bans by adding it to the ignoreip list in your jail configuration:

[sshd]
ignoreip = 127.0.0.1/8 YOUR.PUBLIC.IP.ADDRESS

Do not add dynamic IPs to ignoreip — if your IP changes, the old address would remain whitelisted and could be reassigned to someone else.


SOP 8 — Secure the Webhook (Production Standard)

The webhook endpoint should be hardened before deploying to production. At minimum, implement the following:

  • Validate the token: The n8n workflow should check the token field as its first step and reject requests with an invalid or missing token by returning a 401 or 403 response.
  • Rate-limit the endpoint: Apply rate limiting at your reverse proxy (Nginx, Traefik, or Cloudflare) to prevent abuse.
  • Restrict source IPs: Optionally, limit inbound access to the webhook URL to only the IP addresses of your servers.

SOP 9 — Change Control

Always follow this process when modifying the Fail2Ban configuration.

Before making changes

Create a timestamped backup of the entire configuration directory:

sudo cp -a /etc/fail2ban /etc/fail2ban.bak.$(date +%F)

After making changes

Restart the service and verify that the jail is still healthy:

sudo systemctl restart fail2ban
sudo fail2ban-client status sshd
sudo tail -n 100 /var/log/fail2ban.log

Document every change: Record the date and time, what was changed, why it was changed, and the expected impact. Keep this record alongside your configuration backups.


End of SOP.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Secret Link