Fail2Ban + n8n Webhook SOP (SSH Permanent Ban)

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/alerting.


Table of Contents

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

Architecture

This design keeps Fail2Ban as the local enforcement layer and uses n8n for centralized alerting/intel.

  • sshd writes auth failures to /var/log/auth.log (or journald).
  • Fail2Ban detects brute-force patterns and applies a local ban.
  • n8n receives webhook events to enrich (IP intel), notify (Slack), and correlate across servers.
sshd logs → Fail2Ban jail → firewall ban (%(action_)s)
                         ↘︎ webhook notify → n8n workflow

Prerequisites

  • Ubuntu/Debian server (or compatible)
  • OpenSSH installed and running
  • Log file exists: /var/log/auth.log
  • Outbound HTTPS allowed to your n8n domain
  • n8n webhook endpoint created (POST)

SOP 1 — Install Fail2Ban

Procedure

  1. Install packages:
sudo apt update
sudo apt install -y fail2ban
sudo systemctl enable --now fail2ban

Validation

systemctl is-active fail2ban
sudo fail2ban-client ping

SOP 2 — Create n8n Webhook Action

Create a custom Fail2Ban action that calls n8n when an IP is banned or unbanned.

Procedure

  1. Create the action definition:
sudo nano /etc/fail2ban/action.d/n8n-webhook.conf

Paste the following:

[Definition]

# NOTE:
# - actionban/actionunban are all we need for webhook notifications.
# - actionstart/actionstop/actioncheck are intentionally omitted.

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 =

Notes

  • Why no actionstart/stop/check? Webhooks don’t require lifecycle setup; only ban/unban events matter.
  • Timeout: -m 8 prevents Fail2Ban from hanging on slow networks.
  • Reliability: || true prevents webhook failures from breaking Fail2Ban operations.

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

Procedure

  1. Edit the jail configuration:
sudo nano /etc/fail2ban/jail.local

Add/replace the SSH jail:

[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"]

Critical Token Warning

Avoid tokens containing # unless you escape it, because # can be treated as a comment delimiter.

  • Good: SecureToken_ABC123
  • If you must keep #: escape it like \# (example: abc\#123)

Apply

sudo systemctl restart fail2ban

SOP 4 — Validate & Test

Check SSH jail status

sudo fail2ban-client status sshd

Tail Fail2Ban logs

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

Test a ban safely

  • Use a separate source IP (not your admin IP)
  • Attempt several failed SSH logins to trigger maxretry

Verify n8n

  • Confirm the webhook executed in n8n
  • Confirm payload includes event, ip, fq_hostname, and jail

SOP 5 — Operations (Monitoring & Health Checks)

Daily quick checks

systemctl is-active fail2ban
sudo fail2ban-client status sshd

Investigate suspicious spikes

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

Expected behavior

  • Fail2Ban stays active
  • Banned IPs accumulate gradually
  • No repeated webhook/curl failures in logs

SOP 6 — Manual Unban

List current bans

sudo fail2ban-client status sshd

Unban a specific IP

sudo fail2ban-client set sshd unbanip 1.2.3.4

SOP 7 — Incident Recovery (Accidental Self-Ban)

Recovery steps

  1. Use console access (cloud console / physical / out-of-band)
  2. Unban your public IP:
sudo fail2ban-client set sshd unbanip YOUR.PUBLIC.IP.ADDRESS

Optional: protect stable admin IP

If your admin IP is stable, add it to ignoreip:

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

SOP 8 — Secure the Webhook (Production Standard)

  • Validate token in n8n as the first step
  • Return 401/403 on invalid token
  • Rate-limit the webhook at reverse proxy (Nginx/Traefik/Cloudflare)
  • Optionally restrict source IPs to only your servers

SOP 9 — Change Control

Backup config before changes

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

After changes

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

Document changes: date/time, what changed, why, and expected impact.


End of SOP.

Comments

Leave a Reply

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

Secret Link