Fail2Ban
Fail2ban is a log-monitoring service that detects suspicious behaviour, such as repeated authentication failures, and automatically blocks offending IP addresses.
Apprise integrates with Fail2ban as a notification action, allowing you to receive alerts through any of Apprise’s supported services, including email, push notifications, chat platforms, and webhooks.
This guide assumes:
- A fresh Fail2ban installation
- systemd is available
- A single host configuration
- sshd is the first service being monitored
Distribution-specific paths and advanced jail configurations are intentionally deferred.
Prerequisites
Section titled “Prerequisites”Before you begin, ensure the following are installed:
- Fail2ban
- Apprise (CLI)
- Apprise API is optional (if configuration is centralized)
Verify both are available:
fail2ban-client --versionapprise --versionInstalling Fail2ban
Section titled “Installing Fail2ban”On most systems, Fail2ban is available via the system package manager.
Enable and start the service:
sudo systemctl enable fail2bansudo systemctl start fail2banVerify it is running:
sudo systemctl status fail2banConfiguring Apprise
Section titled “Configuring Apprise”Be sure to be comfortable with Apprise Configuration Files and know which service(s) you plan on using. We will choose to assign the tag fail2ban to all of the end points we wish to be notified if an event occurs:
# Define our fail2ban configuration tag, and assign it to a Discord webhook# as an example for the purpose of this guide. But you can use any# service you want.fail2ban=discord://4174216298/JHMHI8qBe7bk2ZwO5U711o3dV_jsConfiguring Apprise for Fail2ban
Section titled “Configuring Apprise for Fail2ban”Add the following to /etc/fail2ban/jail.local
[DEFAULT]
# ignoreip acts as a safelist; requests coming from these IPs are immune to# Fail2Ban monitoring. Each entry is separated by a space (' ').# You only want to specify safe access points:# - 127.0.0.1/8 : Localhost (this PC); you don't want Fail2Ban banning internal# requests.. this one is safe to add. the `/8` is safe to leave# on the end of the IP for this entry.# - YOUR_IP/32 : If you are accessing your server running Fail2Ban remotely, then# replace the 'YOUR_IP/32' entry below with 'your actual IP address'.# make sure to add `/32` if an IPv4 address or `/128` if it is an# IPv6 address.ignoreip = 127.0.0.1/8 YOUR_IP/32
# how far back to lookfindtime = 5m
# ban if we trigger on 4 failed authenticate within the findtimemaxretry = 4
# ban time; how long do we restrict this user from our system for?bantime = 1d
## Now we define our Apprise Action## - Read from /etc/fail2ban/apprise.conf# - Only notify end points tagged with 'fail2ban'#action = apprise[config="/etc/fail2ban/apprise.conf", args="--tag fail2ban"]
## Now we will define our jails#
[sshd]enabled = trueport = sshlogpath = %(sshd_log)sbackend = %(sshd_backend)s
# Optionally over-ride our defaults abovemaxretry = 5findtime = 10mbantime = 1h
# The entry below is not necessary because it is defined in [DEFAULT] above,# but this is to show that you could also define another entry here as well# and assign it a different set of tags.action = apprise[config="/etc/fail2ban/apprise.conf", args="--tag fail2ban"]Restart Fail2ban:
sudo systemctl restart fail2banVerify the jail is active:
sudo fail2ban-client status sshdTesting Your Setup
Section titled “Testing Your Setup”Trigger a test ban by exceeding authentication attempts, or simulate manually:
sudo fail2ban-client set sshd banip 203.0.113.10You should receive an Apprise notification immediately.
Protecting Apprise API
Section titled “Protecting Apprise API”Fail2ban can also monitor the nginx access log in front of Apprise API and ban clients that repeatedly fail authentication, ignore rate limits, or probe unsupported routes.
Apprise API uses ordinary nginx access-log lines in both regular and strict mode. Strict mode additionally limits authentication traffic and accepts only known routes. It returns standard statuses that a reverse proxy can relay and log:
401means credentials were missing or invalid.404means the route does not exist.405means the route exists but does not accept that HTTP method.429means nginx temporarily rate-limited the client.
Do not treat every 403 as an authentication failure. Apprise can return 403 when valid credentials belong to an account that is not permitted to perform the requested operation.
Choose the Access Log
Section titled “Choose the Access Log”When a host nginx reverse proxy fronts the container, give the Apprise virtual host its own log:
access_log /var/log/nginx/apprise_access.log combined;This is the simplest arrangement because the outer log already contains the connecting client’s address.
If the container is exposed directly, you can instead bind-mount /var/log/apprise and add both destinations to /etc/nginx/server-override.conf:
access_log /dev/stdout combined if=$apprise_access_loggable;access_log /var/log/apprise/access.log combined if=$apprise_access_loggable;Ensure the directory is writable by the container’s PUID, then use /var/log/apprise/access.log in the jail examples below.
Create the Filters
Section titled “Create the Filters”Create /etc/fail2ban/filter.d/nginx-apprise-auth.conf:
[Definition]# Missing or invalid credentials. A valid but unauthorized user receives 403.failregex = ^<HOST> - \S+ \[[^]]+\] "[^"]*" 401 \d+ignoreregex =Create /etc/fail2ban/filter.d/nginx-apprise-rate-limit.conf:
[Definition]# The client continued after nginx began returning Too Many Requests.failregex = ^<HOST> - \S+ \[[^]]+\] "[^"]*" 429 \d+ignoreregex =Create /etc/fail2ban/filter.d/nginx-apprise-probe.conf:
[Definition]# Repeated malformed requests, missing routes, unsupported methods, oversized# requests, long URIs, or requests sent to the wrong virtual host.failregex = ^<HOST> - \S+ \[[^]]+\] "[^"]*" (?:400|404|405|413|414|421|431) \d+ignoreregex =If you already have a generic nginx 429 filter, add the Apprise access log to that jail instead of creating another copy.
Create the Jails
Section titled “Create the Jails”Add the following to /etc/fail2ban/jail.local. These values are cautious starting points; tune them after observing normal traffic:
[nginx-apprise-auth]enabled = truefilter = nginx-apprise-authport = http,httpslogpath = /var/log/nginx/apprise_access.logfindtime = 10mmaxretry = 6bantime = 1h
[nginx-apprise-rate-limit]enabled = truefilter = nginx-apprise-rate-limitport = http,httpslogpath = /var/log/nginx/apprise_access.logfindtime = 5mmaxretry = 10bantime = 1h
[nginx-apprise-probe]enabled = truefilter = nginx-apprise-probeport = http,httpslogpath = /var/log/nginx/apprise_access.logfindtime = 10mmaxretry = 10bantime = 1dPreserve the Real Client Address
Section titled “Preserve the Real Client Address”Fail2ban must never ban the reverse proxy in place of the client. The host proxy should replace, rather than append to, the forwarded address:
proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $remote_addr;The Apprise container also needs the real address for its own strict-mode rate limits. Trust only the exact proxy or Docker gateway in /etc/nginx/server-override.conf:
set_real_ip_from 172.17.0.1; # Replace with the exact trusted proxy address.real_ip_header X-Forwarded-For;real_ip_recursive on;Do not place an entire Docker or private network in set_real_ip_from unless every device on that network is trusted.
Validate Before Restarting
Section titled “Validate Before Restarting”Test each filter against real log entries:
sudo fail2ban-regex /var/log/nginx/apprise_access.log \ /etc/fail2ban/filter.d/nginx-apprise-auth.confsudo fail2ban-regex /var/log/nginx/apprise_access.log \ /etc/fail2ban/filter.d/nginx-apprise-rate-limit.confsudo fail2ban-regex /var/log/nginx/apprise_access.log \ /etc/fail2ban/filter.d/nginx-apprise-probe.confThen validate and reload Fail2ban:
sudo fail2ban-client -tsudo systemctl restart fail2bansudo fail2ban-client status nginx-apprise-authsudo fail2ban-client status nginx-apprise-rate-limitsudo fail2ban-client status nginx-apprise-probeTroubleshooting
Section titled “Troubleshooting”View Fail2ban logs:
journalctl -u fail2banIncrease verbosity for Apprise actions (by adding -vv and test to see what might be going on:
apprise -vv --tag fail2ban --config /etc/fail2ban/apprise.conf \ --body "fail2ban trigger test"Confirm Apprise URLs independently before debugging Fail2ban.
Useful Tips
Section titled “Useful Tips”Fail2ban is amazing, but it can be tedious to use at times. Sometimes it helps to have a few handy notes on the side to troubleshoot with:
# See all jails:fail2ban-client status
# Look into the status of one of the specific jailsfail2ban-client status sshd
# quick and dirty way to unban an IP (regardless of which jail banned it)fail2ban-client unban 1.2.3.4 Questions or Feedback?
Technical Issues
Having trouble with the code? Open an issue on GitHub: