From 1cf70f9b581ae4e53efc0408ad9b8822e1acbf99 Mon Sep 17 00:00:00 2001 From: MajorLinux Date: Thu, 2 Apr 2026 16:04:13 -0400 Subject: [PATCH] Add wiki article: Fail2ban WordPress login brute force jail Access-log-based filter for wp-login.php brute force detection without requiring the WP fail2ban plugin. Documents the backend=polling gotcha on Ubuntu 24.04 and manual banning workflow. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../security/fail2ban-wordpress-login-jail.md | 131 ++++++++++++++++++ SUMMARY.md | 1 + 2 files changed, 132 insertions(+) create mode 100644 02-selfhosting/security/fail2ban-wordpress-login-jail.md diff --git a/02-selfhosting/security/fail2ban-wordpress-login-jail.md b/02-selfhosting/security/fail2ban-wordpress-login-jail.md new file mode 100644 index 0000000..df2d908 --- /dev/null +++ b/02-selfhosting/security/fail2ban-wordpress-login-jail.md @@ -0,0 +1,131 @@ +--- +title: "Fail2ban Custom Jail: WordPress Login Brute Force" +domain: selfhosting +category: security +tags: [fail2ban, wordpress, apache, security, brute-force] +status: published +created: 2026-04-02 +updated: 2026-04-02 +--- +# Fail2ban Custom Jail: WordPress Login Brute Force + +## The Problem + +WordPress login brute force attacks are extremely common. Bots hammer `/wp-login.php` with POST requests, cycling through common credentials. The default Fail2ban `apache-auth` jail doesn't catch these because WordPress returns **HTTP 200** on failed logins — not 401 — so nothing appears as an authentication failure in the Apache error log. + +There are pre-packaged filters (`wordpress-hard.conf`, `wordpress-soft.conf`) that ship with some Fail2ban installations, but these require the **[WP fail2ban](https://wordpress.org/plugins/wp-fail2ban/)** WordPress plugin to be installed. That plugin writes login failures to syslog, which the filters then match. Without the plugin, those filters do nothing. + +## The Solution + +Create a lightweight filter that reads the **Apache access log** and matches repeated POST requests to `wp-login.php` directly. No WordPress plugin needed. + +### Step 1 — Create the filter + +Create `/etc/fail2ban/filter.d/wordpress-login.conf`: + +```ini +# Fail2Ban filter for WordPress login brute force +# Matches POST requests to wp-login.php in Apache access log + +[Definition] + +failregex = ^ .* "POST /wp-login\.php + +ignoreregex = +``` + +### Step 2 — Add the jail + +Add to `/etc/fail2ban/jail.local`: + +```ini +[wordpress-login] +enabled = true +port = http,https +filter = wordpress-login +logpath = /var/log/apache2/access.log +maxretry = 5 +findtime = 60 +bantime = 30d +backend = polling +``` + +**5 attempts in 60 seconds** is tight enough to catch bots (which fire hundreds of requests per minute) while giving a real human a reasonable margin for typos. + +> **Critical: `backend = polling` is required** on Ubuntu 24.04 and other systemd-based distros where `backend = auto` defaults to `systemd`. Without it, Fail2ban ignores `logpath` and reads from journald, which Apache doesn't write to. The jail silently monitors nothing. See [[fail2ban-apache-404-scanner-jail]] for more detail on this gotcha. + +### Step 3 — Test the regex + +```bash +fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wordpress-login.conf +``` + +In a real-world test against an active brute force (3 IPs, ~1,700 hits each), this matched **5,178 lines**. + +### Step 4 — Reload and verify + +```bash +systemctl restart fail2ban +fail2ban-client status wordpress-login +``` + +### Manually banning known attackers + +If you've already identified brute-force IPs from the logs, ban them immediately rather than waiting for new hits: + +```bash +# Find top offenders +grep "POST /wp-login.php" /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10 + +# Ban them +fail2ban-client set wordpress-login banip +``` + +## Why Default Jails Miss This + +| Jail | Log Source | What It Matches | Why It Misses | +|---|---|---|---| +| `apache-auth` | error log | 401 authentication failures | WordPress returns 200, not 401 | +| `wordpress-hard` | syslog | WP fail2ban plugin messages | Requires plugin installation | +| `wordpress-soft` | syslog | WP fail2ban plugin messages | Requires plugin installation | +| **`wordpress-login`** | **access log** | **POST to wp-login.php** | **No plugin needed** | + +## Optional: Extend to XML-RPC + +WordPress's `xmlrpc.php` is another common brute-force target. To cover both, update the filter: + +```ini +failregex = ^ .* "POST /wp-login\.php + ^ .* "POST /xmlrpc\.php +``` + +## Quick Diagnostic Commands + +```bash +# Test filter against current access log +fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wordpress-login.conf + +# Check jail status and banned IPs +fail2ban-client status wordpress-login + +# Verify the jail is reading the correct file +fail2ban-client get wordpress-login logpath + +# Count wp-login POSTs in today's log +grep "POST /wp-login.php" /var/log/apache2/access.log | wc -l + +# Watch bans in real time +tail -f /var/log/fail2ban.log | grep wordpress-login +``` + +## Key Notes + +- This filter works with both Apache **combined** and **common** log formats. +- Make sure your Tailscale subnet (`100.64.0.0/10`) is in the `ignoreip` list under `[DEFAULT]` so legitimate admin access isn't banned. +- The `recidive` jail (if enabled) will escalate repeat offenders — three 30-day bans within a day triggers a 90-day block. +- Complements the [[fail2ban-apache-404-scanner-jail|Apache 404 Scanner Jail]] for full access-log coverage. + +## See Also + +- [[fail2ban-apache-404-scanner-jail]] — catches vulnerability scanners via 404 floods +- [[tuning-netdata-web-log-alerts]] — suppress false Netdata alerts from normal HTTP traffic diff --git a/SUMMARY.md b/SUMMARY.md index 39e717e..e9b7042 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -31,6 +31,7 @@ * [Linux Server Hardening Checklist](02-selfhosting/security/linux-server-hardening-checklist.md) * [Standardizing unattended-upgrades with Ansible](02-selfhosting/security/ansible-unattended-upgrades-fleet.md) * [Fail2ban Custom Jail: Apache 404 Scanner Detection](02-selfhosting/security/fail2ban-apache-404-scanner-jail.md) + * [Fail2ban Custom Jail: WordPress Login Brute Force](02-selfhosting/security/fail2ban-wordpress-login-jail.md) * [SELinux: Fixing Fail2ban grep execmem Denial](02-selfhosting/security/selinux-fail2ban-execmem-fix.md) * [UFW Firewall Management](02-selfhosting/security/ufw-firewall-management.md) * [Open Source & Alternatives](03-opensource/index.md)