Fail2ban is the most widely deployed open-source intrusion prevention tool on Linux servers — it scans log files for patterns of brute-force attempts, bad bots, and abuse, then automatically bans offending IPs via firewall rules. When Fail2ban stops running, your SSH port, mail server, and web applications lose their automated protection. Vigilmon lets you verify the daemon stays alive and its jails are active, so you know the moment protection lapses.
What You'll Set Up
- HTTP monitor for a lightweight Fail2ban health endpoint exposed via a simple script
- Keyword check confirming active jail status
- Heartbeat monitor for the Fail2ban systemd service
- SSL certificate alerts for HTTPS services protected by Fail2ban
Prerequisites
- Fail2ban installed and running (
systemctl status fail2ban) - A web server (nginx or Apache) or systemd available for exposing a health endpoint
- A free Vigilmon account
Step 1: Create a Fail2ban Health Endpoint
Fail2ban has no built-in HTTP interface, so you expose a lightweight endpoint using a shell script and your existing web server. Create a CGI-style health script:
# /usr/local/bin/fail2ban-health.sh
#!/bin/bash
STATUS=$(fail2ban-client status 2>&1)
if echo "$STATUS" | grep -q "Number of jail:"; then
echo "Content-Type: text/plain"
echo ""
echo "fail2ban-ok"
else
echo "Content-Type: text/plain"
echo ""
echo "fail2ban-error"
exit 1
fi
Make it executable:
chmod +x /usr/local/bin/fail2ban-health.sh
If you use nginx, expose it via ngx_http_stub_status_module on an internal port, or simply wrap it in a small Python script:
# /usr/local/bin/fail2ban-healthd.py
#!/usr/bin/env python3
from http.server import HTTPServer, BaseHTTPRequestHandler
import subprocess
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
try:
result = subprocess.run(
['fail2ban-client', 'status'],
capture_output=True, text=True, timeout=5
)
if 'Number of jail:' in result.stdout:
self.send_response(200)
self.end_headers()
self.wfile.write(b'fail2ban-ok')
else:
self.send_response(503)
self.end_headers()
self.wfile.write(b'fail2ban-error')
except Exception:
self.send_response(503)
self.end_headers()
self.wfile.write(b'fail2ban-error')
def log_message(self, *args):
pass
HTTPServer(('127.0.0.1', 9191), Handler).serve_forever()
Run it as a systemd service on 127.0.0.1:9191, then proxy it externally via your web server with IP allowlisting so only Vigilmon's probe IPs can reach it.
Step 2: Monitor the Fail2ban Daemon
With the health endpoint running, add it to Vigilmon:
- Log in to vigilmon.online and click Add Monitor.
- Set Type to
HTTP / HTTPS. - Enter the URL:
http://YOUR_HOST:9191/(or the proxied public URL). - Set Check interval to
2 minutes. - Under Advanced, set Expected body contains to
fail2ban-ok. - Set Expected HTTP status to
200. - Click Save.
A failure here means the Fail2ban daemon has crashed or fail2ban-client cannot communicate with it — your SSH jail is no longer active.
Step 3: Monitor Jail Status via Keyword Check
Beyond daemon availability, you want to confirm specific jails are active. Extend the health script to report jail names:
# modify the do_GET handler to check a specific jail
result = subprocess.run(
['fail2ban-client', 'status', 'sshd'],
capture_output=True, text=True, timeout=5
)
if 'Status for the jail: sshd' in result.stdout:
self.wfile.write(b'sshd-jail-active')
In Vigilmon, add a second monitor:
- Add Monitor →
HTTP / HTTPS. - URL: your health endpoint.
- Set Expected body contains to
sshd-jail-active. - Name it Fail2ban sshd jail.
- Click Save.
Repeat for any other critical jails (e.g. nginx-http-auth, postfix). This confirms the specific protection rules are loaded, not just that the daemon is alive.
Step 4: Heartbeat Monitor for the Systemd Service
The heartbeat approach is the most reliable check for systemd-managed daemons. Add a cron job that pings Vigilmon's heartbeat URL only when Fail2ban is healthy:
# /etc/cron.d/fail2ban-heartbeat
* * * * * root /usr/bin/systemctl is-active --quiet fail2ban && \
/usr/bin/curl -fsS "https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID" > /dev/null 2>&1
In Vigilmon, set up the heartbeat:
- Click Add Monitor → Heartbeat.
- Name it Fail2ban daemon heartbeat.
- Set Expected interval to
2 minutes. - Set Grace period to
3 minutes. - Copy the generated heartbeat URL and paste it into the cron command above.
- Click Save.
If Fail2ban stops (systemctl is-active returns non-zero), the cron command short-circuits and the heartbeat is never sent. Vigilmon alerts you after the grace period expires.
Step 5: SSL Certificate Alerts for Protected Services
Fail2ban often protects HTTPS services — if those services have expiring certificates, automated banning of failed connections becomes irrelevant when the entire service is down for TLS errors. Monitor every HTTPS service behind Fail2ban:
- Open (or create) an HTTP/HTTPS monitor for your protected service (e.g.
https://mail.yourdomain.comorhttps://app.yourdomain.com). - Enable Monitor SSL certificate in the SSL section.
- Set Alert when certificate expires in less than
14 days. - Click Save.
With a two-week warning window, you have time to investigate renewal failures before users see certificate errors — and before Fail2ban starts banning legitimate connection attempts that fail due to TLS handshake errors.
Summary
| Monitor | Target | What It Catches |
|---|---|---|
| Daemon health | :9191/ | Fail2ban crash, socket error |
| Jail status | :9191/ keyword | Specific jail not loaded |
| Heartbeat | cron + systemctl | Daemon silently stopped |
| SSL certificate | Each HTTPS service | Let's Encrypt renewal failure |
Fail2ban's value is entirely dependent on it running continuously. A crashed daemon means your firewall rules go unenforced — SSH and web services become exposed to brute-force attacks within minutes. With Vigilmon monitoring the daemon health, jail status, and the services it protects, you'll know the moment protection lapses.