docs: rewrite README and add user guide

- Full README rewrite: screenshot placeholders, credits, project origin note
- New USERGUIDE.md: plain-English walkthrough for non-technical users
- Documents whitelist vs exempt distinction, all dashboard actions, scan workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 18:59:23 +00:00
parent 60bb6abe4f
commit 239e2df8c1
2 changed files with 261 additions and 50 deletions

152
README.md
View File

@@ -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.
<!-- SCREENSHOT: Main dashboard — full grid of ban cards visible, mix of BADBOT/NPM-PROBE/HTTP-ERRORS jail badges, AbuseIPDB scores showing on several cards, dark terminal UI -->
---
## 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.
<!-- SCREENSHOT: Filter tab row — all tab buttons visible ([ALL][BADBOT][NPM][HTTP-ERRORS][PRISON][WHITELIST][EXEMPT][SCAN]), one tab active/highlighted -->
<!-- SCREENSHOT: Scan results panel — several suspect IP cards visible with [BAN], [EXEMPT], [THREAT], [RECORDS] buttons; one card showing an AbuseIPDB score -->
<!-- SCREENSHOT: Live log stream — scrolling fail2ban log output showing ban events in real time, terminal-style -->
---
## 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).

159
USERGUIDE.md Normal file
View File

@@ -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.
<!-- SCREENSHOT: Full dashboard on first load — ban grid visible with several cards, filter tabs across the top, action bar below the tabs, dark retro-terminal theme -->
---
## The Ban Grid
The main view is a grid of cards — one card per banned IP address.
<!-- SCREENSHOT: Close-up of 2-3 ban cards side by side — showing IP, jail badge (e.g. BADBOT), ban expiry time, AbuseIPDB score, and action buttons -->
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 0100 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
<!-- SCREENSHOT: The filter tab row at full width — [ALL] [BADBOT] [NPM] [HTTP-ERRORS] [PRISON] [WHITELIST] [EXEMPT] [SCAN] — with one tab highlighted/active -->
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
<!-- SCREENSHOT: The action bar — IP address input field on the left, dropdown selector showing options (ARREST / PAROLE / WHITELIST / SEARCH), GO button on the right -->
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
<!-- SCREENSHOT: Scan results — 4-5 suspect IP cards visible, each with [BAN], [EXEMPT], [THREAT], [RECORDS] buttons; one card showing a high AbuseIPDB score in red -->
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
<!-- SCREENSHOT: Live log stream panel — scrolling text of fail2ban log output showing detection events and ban notices, monospace font, dark background, newest entry at bottom -->
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)
<!-- SCREENSHOT: Per-IP log viewer — showing a filtered list of HTTP requests from one IP: method, path, status code, timestamp, one or two suspicious paths highlighted -->
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.