diff --git a/README.md b/README.md index e907e85..d45fcd0 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,122 @@ # F2B Control Center -Fail2Ban + Nginx Proxy Manager in a single Docker container, with a web dashboard for monitoring and managing bans. +> Fail2Ban management dashboard for Nginx Proxy Manager — bans scanners, bots, and exploit probers automatically, with a browser UI to monitor and control everything. -Pre-built filters detect bad bots, HTTP error spamming, and exploit probing against NPM logs. Bans are enforced via iptables (DOCKER-USER chain + INPUT), with optional Cloudflare WAF banning on top. + + +--- + +## What It Does + +F2B Control Center runs alongside Nginx Proxy Manager as a single Docker container. It watches your NPM access logs in real time and automatically bans IPs that are: + +- Using known scanner or hacking-tool user-agents (masscan, sqlmap, zgrab, etc.) +- Probing for common exploits — WordPress logins, `.env` files, admin panels, PHP shells +- Hammering your services with repeated HTTP errors + +Bans are enforced at the **iptables level** — blocked IPs can't reach anything on the host, not just one service. Optionally, bans also push to **Cloudflare WAF** to block traffic before it even hits your server. + +The dashboard gives you a live view of every active ban, lets you manually ban/unban IPs, scan logs for suspicious activity, check threat reputation via AbuseIPDB, and manage your trusted IP whitelist — all from the browser. --- ## Requirements - Docker + Docker Compose -- Linux host with iptables support -- `xt_string` kernel module on the host (`modprobe xt_string`) +- Linux host with iptables/nftables support +- Nginx Proxy Manager (or any reverse proxy writing NPM-format access logs) +- `xt_string` kernel module loaded on the host: + ```bash + modprobe xt_string + echo xt_string >> /etc/modules # persist across reboots + ``` --- ## Quick Start ```bash -git clone https://git.thisisfake.lol/mykey/f2b-control-center +git clone https://github.com/YOUR_USERNAME/f2b-control-center cd f2b-control-center ``` -Edit `docker-compose.yml` — at minimum review `SUBNETS_TO_IGNORE`. Then: +Open `docker-compose.yml` and set `SUBNETS_TO_IGNORE` to your local network ranges — these IPs will never be scanned or banned. Everything else works out of the box. ```bash docker compose up -d ``` -Dashboard at `http://YOUR_HOST:4000` +Dashboard is at `http://YOUR_HOST:4000`. -On first start the container installs the default Fail2Ban config into a persistent volume and begins monitoring NPM logs immediately. +On first start the container populates a persistent config volume from the bundled defaults and begins monitoring NPM logs immediately. No manual fail2ban setup required. --- ## Configuration -All settings are in `docker-compose.yml`. Required fields are uncommented. Optional features are commented out — uncomment and fill in to enable. +All settings live in `docker-compose.yml`. Required fields are uncommented. Optional integrations are commented out — uncomment and fill in to enable them. | Variable | Default | Description | |---|---|---| | `PORT` | `4000` | Dashboard port | -| `SUBNETS_TO_IGNORE` | RFC1918 ranges | CIDRs excluded from scanning and banning | -| `ABUSEIPDB_API_KEY` | _(optional)_ | Enables threat scoring and auto-ban | -| `CF_EMAIL` | _(optional)_ | Cloudflare account email — enables WAF banning | -| `CF_APIKEY` | _(optional)_ | Cloudflare Global API Key | +| `SUBNETS_TO_IGNORE` | RFC1918 ranges | Comma-separated CIDRs excluded from scanning and banning | +| `ABUSEIPDB_API_KEY` | *(optional)* | Enables AbuseIPDB threat scores and report submission | +| `CF_EMAIL` | *(optional)* | Cloudflare account email — enables WAF banning | +| `CF_APIKEY` | *(optional)* | Cloudflare Global API Key | --- ## Jails -| Jail | Trigger | Ban time | +| Jail | What triggers it | Default ban time | |---|---|---| -| `badbot` | Known scanner/exploit user-agents | 24h | -| `http-errors` | 15+ 4xx/5xx errors in 5 min | 1h | -| `npm-probe` | Exploit path probing (.env, wp-login, etc.) | 48h | -| `manual-bans` | Manual dashboard or CLI bans | Permanent | -| `recidive` | Repeat offenders (disabled by default) | 7d | +| `badbot` | Known scanner/exploit user-agents | 24 hours | +| `http-errors` | 15+ 4xx/5xx errors in 5 minutes | 1 hour | +| `npm-probe` | Exploit path probing (.env, wp-login, admin panels, etc.) | 48 hours | +| `manual-bans` | Manually banned via dashboard or CLI | Permanent | +| `recidive` | Repeat offenders — 3 bans within 24h | 7 days *(disabled by default)* | -To customise jails: - -```bash -docker exec -it f2b-control-center bash -vi /etc/fail2ban/jail.local -fail2ban-client reload -``` +Jail thresholds (ban time, retry count, window) are tunable in the `fail2ban/jail.local` config, which persists in the `f2b-config` Docker volume across image updates. You can also edit it live through the dashboard's Jail Manager. --- ## How Banning Works -The container uses `network_mode: host` so iptables rules affect the host network stack. Two rules are inserted per ban: +The container runs with `network_mode: host` and `NET_ADMIN`/`NET_RAW` capabilities so it can write iptables rules directly on the host network stack. -- **DOCKER-USER** — drops forwarded packets where the `X-Forwarded-For` header matches the banned IP (catches traffic routed through Cloudflare/CDN) -- **INPUT** — drops direct connections from the banned IP +Each ban inserts two rules: -Requires `xt_string` on the host: -```bash -modprobe xt_string -# to persist across reboots: -echo xt_string >> /etc/modules -``` +1. **DOCKER-USER chain** — drops forwarded traffic where the `X-Forwarded-For` header matches the banned IP. Catches traffic routed through Cloudflare or other CDNs. +2. **INPUT chain** — drops direct connections from the banned IP. ---- +Bans survive container restarts (fail2ban reloads its state from disk). They do not survive host reboots unless you also enable the `recidive` jail or use persistent iptables (e.g., `iptables-persistent`). -## Cloudflare WAF +### Cloudflare WAF (optional) -Uncomment and fill in `CF_EMAIL` and `CF_APIKEY` in `docker-compose.yml`. The container detects these on first start and installs the Cloudflare action alongside iptables — no other changes needed. +Uncomment `CF_EMAIL` and `CF_APIKEY` in `docker-compose.yml`. The container detects these on startup and activates Cloudflare banning automatically — no other config needed. Every ban also creates a Cloudflare firewall rule, blocking the IP at the edge before it reaches your server. Get your Global API Key at: https://dash.cloudflare.com/profile/api-tokens --- +## Dashboard + +See **[USERGUIDE.md](USERGUIDE.md)** for a plain-English walkthrough of every dashboard feature. + + + + + + + +--- + ## Log Format -Filters expect NPM logs with the real client IP in a `[Client X.X.X.X]` field — the format NPM produces when behind Cloudflare or any proxy that forwards `X-Forwarded-For`. +Filters expect NPM access logs with the real client IP in a `[Client X.X.X.X]` field. This is the format NPM produces when sitting behind Cloudflare or any proxy that forwards `X-Forwarded-For`. + +To verify your filters are working: -Test a filter against your logs: ```bash docker exec f2b-control-center \ fail2ban-regex /nginx-logs/proxy-host-1_access.log \ @@ -104,35 +125,66 @@ docker exec f2b-control-center \ --- -## Useful Commands +## Volumes + +| Volume | Container path | Contents | +|---|---|---| +| `f2b-data` | `/data` | Ban history, exemptions list | +| `f2b-config` | `/etc/fail2ban` | Jail and filter config (persists across image rebuilds) | +| bind mount | `/nginx-logs` | NPM log directory (read-only) | + +To reset config to defaults: `docker volume rm f2b-control-center_f2b-config` then restart. + +--- + +## CLI Reference ```bash -# View logs +# Stream container logs docker compose logs -f -# Reload fail2ban config +# Reload fail2ban after manual config edits docker exec f2b-control-center fail2ban-client reload # Check jail status docker exec f2b-control-center fail2ban-client status +docker exec f2b-control-center fail2ban-client status badbot # Manual ban / unban docker exec f2b-control-center fail2ban-client set manual-bans banip 1.2.3.4 docker exec f2b-control-center fail2ban-client set manual-bans unbanip 1.2.3.4 + +# Test a filter against your logs +docker exec f2b-control-center \ + fail2ban-regex /nginx-logs/proxy-host-1_access.log \ + /etc/fail2ban/filter.d/npm-probe.conf ``` --- -## Volumes +## Credits -| Volume | Path | Contents | -|---|---|---| -| `f2b-data` | `/data` | Ban history database | -| `f2b-config` | `/etc/fail2ban` | Jail and filter config (survives image updates) | -| bind mount | `/nginx-logs` | NPM log directory (read-only) | +This project is built on and integrates the following open-source tools and services: + +| | | +|---|---| +| [Fail2Ban](https://github.com/fail2ban/fail2ban) | IP ban engine and jail framework | +| [Nginx Proxy Manager](https://nginxproxymanager.com) — jc21 | The reverse proxy this was designed for | +| [AbuseIPDB](https://www.abuseipdb.com) | Threat intelligence API and reporting | +| [Express.js](https://expressjs.com) | Dashboard API server | +| [ipaddr.js](https://github.com/whitequark/ipaddr.js) | IP and CIDR address parsing | +| [VT323](https://fonts.google.com/specimen/VT323) | Retro terminal UI font (Google Fonts) | +| [supervisord](http://supervisord.org) | Multi-process management inside the container | +| [Docker](https://www.docker.com) | Container runtime | + +### About This Project + +This started as a personal setup I built to protect my own self-hosted services. The filters, iptables actions, and log parsing logic came out of real-world use on my own infrastructure. + +The migration from personal scripts into a clean, shareable Docker project — packaging, entrypoint initialization, the dashboard UI, and this documentation — was done with significant help from [Claude](https://claude.ai) (Anthropic). If you're curious what AI-assisted development looks like in practice, this project is a fairly honest example. --- ## License -MIT +MIT — see [LICENSE](LICENSE). diff --git a/USERGUIDE.md b/USERGUIDE.md new file mode 100644 index 0000000..299247b --- /dev/null +++ b/USERGUIDE.md @@ -0,0 +1,159 @@ +# F2B Control Center — User Guide + +A plain-English walkthrough of the dashboard. No technical background required. + +--- + +## What It Does For You + +F2B Control Center watches the traffic hitting your self-hosted services and automatically bans IP addresses that are behaving badly — bots scanning for security holes, scrapers hammering your error pages, tools probing for WordPress vulnerabilities on a site that isn't even WordPress. + +When something gets banned, it's blocked at the server level. The banned IP can't reach any of your services, not just the one it was probing. + +The dashboard gives you a live view of what's happening, and lets you take action on anything you see. + +--- + +## Opening the Dashboard + +Navigate to `http://YOUR_SERVER_IP:4000` in your browser. + + + +--- + +## The Ban Grid + +The main view is a grid of cards — one card per banned IP address. + + + +Each card shows: + +- **The IP address** that was banned +- **Why it was banned** — shown as a badge (see Jails section below) +- **When the ban expires** — or "permanent" for manual bans +- **Threat score** — a 0–100 number from AbuseIPDB showing how widely this IP is known to be malicious *(only shown if AbuseIPDB is configured)* + +--- + +## Jails — Why Was This IP Banned? + +Every ban comes from a "jail" — a rule set that detected bad behaviour: + +| Badge | What it caught | +|---|---| +| `[BADBOT]` | The IP's browser/tool identified itself as a known scanner or hacking tool (masscan, sqlmap, Nikto, etc.) | +| `[NPM-PROBE]` | The IP tried to access classic exploit targets — WordPress logins, `.env` files, PHP shells, admin panels — paths that have no business being requested on your server | +| `[HTTP-ERRORS]` | The IP generated a flood of 4xx or 5xx error responses in a short time window — typical scanning or brute-force behaviour | +| `[PRISON]` | You manually banned this IP | +| `[WHITELIST]` | This IP is fully trusted and will never be banned or flagged | +| `[EXEMPT]` | You've reviewed this IP and dismissed it — it won't appear in scan results, but the system is still watching it | + +--- + +## Filter Tabs + + + +The row of buttons across the top filters the grid: + +- Click **`[ALL]`** to see every banned IP at once +- Click any jail name to show only that group (e.g. `[BADBOT]` to see only bot traffic) +- Click **`[SCAN]`** to run the log scanner *(see Scan section below)* + +--- + +## Actions — What You Can Do With a Banned IP + +Each card has buttons for acting on that IP: + +| Button | What it does | +|---|---| +| `[RECORDS]` | Opens the access log for that IP — every request it ever made to your server | +| `[PAROLE]` | Removes all bans for this IP and lets it through again | +| `[ARREST]` | Manually bans this IP permanently (useful from scan results) | +| `[THREAT]` | Looks up this IP on AbuseIPDB — shows its global reputation and recent abuse reports | +| `[WHITELIST]` | Marks this IP as fully trusted — fail2ban will never ban it again | +| `[EXEMPT]` | Dismisses this IP from scan results without trusting it — it stays monitored | +| `[REMOVE]` | Removes an IP from the whitelist or exempt list | + +--- + +## The Action Bar + + + +The bar just below the filter tabs lets you act on any IP by address — even ones that aren't currently banned: + +1. Type or paste an IP address +2. Choose an action from the dropdown +3. Press **`[GO]`** + +Actions available here: + +- **`[ARREST]`** — ban immediately, permanently +- **`[PAROLE]`** — remove all bans +- **`[WHITELIST]`** — trust this IP permanently +- **`[SEARCH]`** — highlight cards matching this IP in the current grid view + +--- + +## Scan + + + +The Scan reads your NPM access logs and surfaces IP addresses that are behaving suspiciously but haven't been banned yet. Click **`[SCAN]`** in the filter tabs to run it. + +It looks for IPs that are: +- Generating lots of errors +- Hitting known exploit paths +- Using scanner user-agents + +From each scan result card you can: +- **`[BAN]`** — ban it right now +- **`[EXEMPT]`** — mark as reviewed; hide from future scans (monitoring continues) +- **`[THREAT]`** — check its reputation on AbuseIPDB +- **`[RECORDS]`** — see everything it requested + +The scan is the best place to start when you first set things up. Run it after a day or two of traffic to get a sense of what's been probing your services. + +--- + +## Whitelist vs. Exempt + +These are two different levels of "leave this IP alone": + +**Whitelist** is for IPs you fully trust — your own devices, uptime monitors, your office network. Fail2ban will never ban them. They won't appear in scans or the ban grid. + +**Exempt** is for IPs you've looked at and decided aren't worth worrying about right now. They disappear from scan results so they don't keep coming up, but fail2ban is still watching them. If they start misbehaving later they'll get banned normally. + +When in doubt about an IP you've reviewed: use Exempt. Reserve Whitelist for things you're certain about. + +--- + +## Live Log + + + +The live log shows fail2ban's activity as it happens — every detection, ban, and unban in real time. Useful for confirming that a ban landed, watching a scan in progress, or just seeing what's hitting your server right now. + +--- + +## Access Log Viewer (Records) + + + +Click **`[RECORDS]`** on any ban card or scan result to open a filtered view of the NPM access log for that specific IP. You'll see every request it made — what paths it hit, what status codes it got back, and when. + +This is useful for understanding *why* something was banned, or deciding whether to parole an IP that might have been caught by a threshold incorrectly. + +--- + +## Tips + +- **First time setup**: Run a Scan after your server has been running for a day or two. It'll show you what's already been poking around. +- **False positives**: If a legitimate service keeps getting banned, check its access log with `[RECORDS]` to understand why. Then either `[WHITELIST]` it (if you fully trust it) or raise the `maxretry` threshold in the jail config. +- **Recidive jail**: Disabled by default. Enable it after your other jails have been running for a while — it gives repeat offenders a 7-day ban automatically. +- **Manual bans are permanent**: Bans placed via `[ARREST]` or the action bar don't expire. Use `[PAROLE]` to lift them. +- **Cloudflare users**: With `CF_EMAIL` and `CF_APIKEY` set, every iptables ban also creates a Cloudflare firewall rule — the IP gets blocked at the edge, before it reaches your server at all.