chore: clean up stale references and README
- jail.local: fix comment pointing to deleted docker-compose.cloudflare.yml - .gitignore: remove .env entry (no env file in use) - README: full rewrite to match current state (no .env, no webhook, correct file structure, inline compose config, CF setup instructions) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,9 +1,6 @@
|
|||||||
# Dependencies
|
# Dependencies
|
||||||
dashboard/node_modules/
|
dashboard/node_modules/
|
||||||
|
|
||||||
# Environment (contains secrets)
|
|
||||||
.env
|
|
||||||
|
|
||||||
# Runtime data
|
# Runtime data
|
||||||
data/
|
data/
|
||||||
*.json
|
*.json
|
||||||
|
|||||||
303
README.md
303
README.md
@@ -1,125 +1,63 @@
|
|||||||
# F2B Control Center
|
# F2B Control Center
|
||||||
|
|
||||||
A batteries-included Fail2Ban distribution for [Nginx Proxy Manager](https://nginxproxymanager.com/) environments, packaged as a single Docker container.
|
Fail2Ban + Nginx Proxy Manager in a single Docker container, with a web dashboard for monitoring and managing bans.
|
||||||
|
|
||||||
Combines Fail2Ban (active blocking), a web dashboard (monitoring + management), and pre-built detection rules tailored for NPM reverse proxy logs — all with a minimal setup process.
|
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.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Live ban feed** — real-time stream of Fail2Ban ban events
|
|
||||||
- **Jail management** — view, ban, unban, and whitelist IPs across all jails
|
|
||||||
- **Nginx log scanner** — identify suspicious IPs from access logs before they trigger automatic rules
|
|
||||||
- **AbuseIPDB integration** — look up IP reputation scores; auto-ban by threshold
|
|
||||||
- **Ban history** — persistent record of all banned IPs with ban counts and timestamps
|
|
||||||
- **Pre-built filters** for NPM logs: bad bots, HTTP error spamming, exploit probing
|
|
||||||
- **Webhook support** — POST ban events to any HTTP endpoint (Discord, Slack, n8n)
|
|
||||||
- **Cloudflare sync hook** — optional script to sync whitelist with CF firewall rules
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Docker Engine 20.10+
|
- Docker + Docker Compose
|
||||||
- Docker Compose v2+
|
- Linux host with iptables support
|
||||||
- Nginx Proxy Manager running and producing access logs
|
- `xt_string` kernel module on the host (`modprobe xt_string`)
|
||||||
- Linux host with iptables support (for active banning)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
**1. Clone the repository**
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone http://YOUR_GITEA_HOST/YOUR_USER/f2b-control-center.git
|
git clone https://git.thisisfake.lol/mykey/f2b-control-center
|
||||||
cd f2b-control-center
|
cd f2b-control-center
|
||||||
```
|
```
|
||||||
|
|
||||||
**2. Configure your environment**
|
Edit `docker-compose.yml` — at minimum review `SUBNETS_TO_IGNORE`. Then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit `.env` and set at minimum:
|
Dashboard at `http://YOUR_HOST:4000`
|
||||||
|
|
||||||
```env
|
On first start the container installs the default Fail2Ban config into a persistent volume and begins monitoring NPM logs immediately.
|
||||||
# Path to your NPM log directory on the host
|
|
||||||
NPM_LOG_DIR=/home/docker/NGINX/data/logs
|
|
||||||
|
|
||||||
# AbuseIPDB API key (optional but strongly recommended)
|
|
||||||
ABUSEIPDB_API_KEY=your_key_here
|
|
||||||
```
|
|
||||||
|
|
||||||
**3. Start the container**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
**4. Open the dashboard**
|
|
||||||
|
|
||||||
Navigate to `http://YOUR_HOST:4000` in your browser.
|
|
||||||
|
|
||||||
On first start, the container will install the default Fail2Ban configuration into a persistent Docker volume. Fail2Ban will begin monitoring your NPM logs immediately.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Configuration Reference
|
## Configuration
|
||||||
|
|
||||||
All settings are controlled via `.env` and the `docker-compose.yml` environment section.
|
All settings are in `docker-compose.yml`. Required fields are uncommented. Optional features are commented out — uncomment and fill in to enable.
|
||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `NPM_LOG_DIR` | `/opt/npm/data/logs` | Host path to NPM access log directory |
|
| `PORT` | `4000` | Dashboard port |
|
||||||
| `DASHBOARD_PORT` | `4000` | Port the dashboard listens on |
|
| `SUBNETS_TO_IGNORE` | RFC1918 ranges | CIDRs excluded from scanning and banning |
|
||||||
| `ABUSEIPDB_API_KEY` | _(empty)_ | AbuseIPDB API key — enables threat scoring |
|
| `ABUSEIPDB_API_KEY` | _(optional)_ | Enables threat scoring and auto-ban |
|
||||||
| `AUTOBAN_THRESHOLD` | `75` | Abuse score threshold for auto-ban (0–100) |
|
| `CF_EMAIL` | _(optional)_ | Cloudflare account email — enables WAF banning |
|
||||||
| `DEFAULT_LOOKBACK_DAYS` | `3` | Default scan window for nginx logs |
|
| `CF_APIKEY` | _(optional)_ | Cloudflare Global API Key |
|
||||||
| `SUBNETS_TO_IGNORE` | `10.0.0.0/8,...` | Comma-separated CIDRs to ignore in scans |
|
|
||||||
| `WEBHOOK_URL` | _(empty)_ | HTTP endpoint to notify on manual bans |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Network Mode
|
## Jails
|
||||||
|
|
||||||
The container runs with `network_mode: host` by default.
|
| Jail | Trigger | 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 |
|
||||||
|
|
||||||
This is intentional. For Fail2Ban's iptables rules to block traffic arriving at the host before it reaches NPM, the container must share the host's network namespace. Without this, iptables rules created inside the container operate in an isolated network namespace and do not affect inbound traffic.
|
To customise jails:
|
||||||
|
|
||||||
**If you only want the dashboard** (monitoring without active iptables blocking), you can switch to bridge mode:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# In docker-compose.yml — comment out network_mode and uncomment:
|
|
||||||
ports:
|
|
||||||
- "${DASHBOARD_PORT:-4000}:4000"
|
|
||||||
cap_add:
|
|
||||||
- NET_ADMIN
|
|
||||||
- NET_RAW
|
|
||||||
```
|
|
||||||
|
|
||||||
In bridge mode, bans will be created in the container's namespace but will not block host-level traffic.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Fail2Ban Configuration
|
|
||||||
|
|
||||||
### Jails
|
|
||||||
|
|
||||||
The default jails installed by the container are:
|
|
||||||
|
|
||||||
| Jail | Filter | Purpose | Default: bantime / findtime / maxretry |
|
|
||||||
|---|---|---|---|
|
|
||||||
| `badbot` | `badbot.conf` | Blocks known scanner/exploit UAs | 24h / 10m / 3 |
|
|
||||||
| `http-errors` | `http-errors.conf` | Blocks high 4xx/5xx error rates | 1h / 5m / 15 |
|
|
||||||
| `npm-probe` | `npm-probe.conf` | Blocks exploit path probes | 48h / 30m / 3 |
|
|
||||||
| `manual-bans` | _(none)_ | Permanent manual bans via dashboard | permanent |
|
|
||||||
|
|
||||||
### Customising jails
|
|
||||||
|
|
||||||
The Fail2Ban config is persisted in the `f2b-config` Docker volume. To edit it:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker exec -it f2b-control-center bash
|
docker exec -it f2b-control-center bash
|
||||||
@@ -127,30 +65,37 @@ vi /etc/fail2ban/jail.local
|
|||||||
fail2ban-client reload
|
fail2ban-client reload
|
||||||
```
|
```
|
||||||
|
|
||||||
Or mount a local directory instead of the volume for easier editing:
|
---
|
||||||
|
|
||||||
```yaml
|
## How Banning Works
|
||||||
# In docker-compose.yml:
|
|
||||||
volumes:
|
|
||||||
- ./my-fail2ban-config:/etc/fail2ban
|
|
||||||
```
|
|
||||||
|
|
||||||
### Log format
|
The container uses `network_mode: host` so iptables rules affect the host network stack. Two rules are inserted per ban:
|
||||||
|
|
||||||
The default filters expect NPM access logs that include the real client IP in a `[Client X.X.X.X]` field at the end of each line. This format is produced when NPM is behind Cloudflare or another upstream proxy that forwards the real IP via `X-Real-IP` or `X-Forwarded-For`.
|
- **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
|
||||||
**If your NPM logs have the client IP at the start of each line** (standard nginx combined format), edit the `failregex` lines in each filter file:
|
|
||||||
|
|
||||||
|
Requires `xt_string` on the host:
|
||||||
```bash
|
```bash
|
||||||
# Example: switch http-errors filter to standard nginx format
|
modprobe xt_string
|
||||||
docker exec -it f2b-control-center bash
|
# to persist across reboots:
|
||||||
vi /etc/fail2ban/filter.d/http-errors.conf
|
echo xt_string >> /etc/modules
|
||||||
# Comment out Primary, uncomment Alternative lines
|
|
||||||
fail2ban-client reload
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Test a filter against your actual logs:
|
---
|
||||||
|
|
||||||
|
## Cloudflare WAF
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Get your Global API Key at: https://dash.cloudflare.com/profile/api-tokens
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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`.
|
||||||
|
|
||||||
|
Test a filter against your logs:
|
||||||
```bash
|
```bash
|
||||||
docker exec f2b-control-center \
|
docker exec f2b-control-center \
|
||||||
fail2ban-regex /nginx-logs/proxy-host-1_access.log \
|
fail2ban-regex /nginx-logs/proxy-host-1_access.log \
|
||||||
@@ -159,163 +104,35 @@ docker exec f2b-control-center \
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Optional Integrations
|
## Useful Commands
|
||||||
|
|
||||||
### AbuseIPDB
|
|
||||||
|
|
||||||
Set `ABUSEIPDB_API_KEY` in `.env`. Free keys are available at [abuseipdb.com](https://www.abuseipdb.com/).
|
|
||||||
|
|
||||||
Once configured:
|
|
||||||
- All ban cards show an abuse confidence score
|
|
||||||
- Use **[THREAT]** on any card to look up a specific IP
|
|
||||||
- Use **[FORCE ABUSE]** to background-check all currently banned IPs
|
|
||||||
- Use **AUTO-BAN** to scan logs and automatically ban IPs above the score threshold
|
|
||||||
|
|
||||||
### Webhook
|
|
||||||
|
|
||||||
Set `WEBHOOK_URL` in `.env` to receive a POST request on every manual ban action:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"action": "ban",
|
|
||||||
"ip": "1.2.3.4",
|
|
||||||
"jail": "manual-bans",
|
|
||||||
"ts": "2026-02-20T14:00:00.000Z"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For Fail2Ban-triggered bans (from filters), use the `webhook` action in `jail.local` — see `fail2ban/action.d/webhook.conf` for setup instructions.
|
|
||||||
|
|
||||||
### Cloudflare whitelist sync
|
|
||||||
|
|
||||||
Set `CF_SYNC` in `.env` to the path of a script inside the container. The script will be executed (fire-and-forget) whenever a whitelist change is made:
|
|
||||||
|
|
||||||
```env
|
|
||||||
CF_SYNC=/usr/local/bin/cloudflare-whitelist-sync.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Mount the script into the container:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
volumes:
|
|
||||||
- ./cloudflare-whitelist-sync.sh:/usr/local/bin/cloudflare-whitelist-sync.sh:ro
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Managing the Container
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View logs
|
# View logs
|
||||||
docker-compose logs -f
|
docker compose logs -f
|
||||||
|
|
||||||
# Restart
|
# Reload fail2ban config
|
||||||
docker-compose restart
|
|
||||||
|
|
||||||
# Update (rebuild image after pulling changes)
|
|
||||||
docker-compose pull
|
|
||||||
docker-compose build --no-cache
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
# Reload fail2ban config without restart
|
|
||||||
docker exec f2b-control-center fail2ban-client reload
|
docker exec f2b-control-center fail2ban-client reload
|
||||||
|
|
||||||
# View current ban status
|
# Check jail status
|
||||||
docker exec f2b-control-center fail2ban-client status
|
docker exec f2b-control-center fail2ban-client status
|
||||||
|
|
||||||
# Manually ban an IP
|
# 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 banip 1.2.3.4
|
||||||
|
|
||||||
# Manually unban an IP
|
|
||||||
docker exec f2b-control-center fail2ban-client set manual-bans unbanip 1.2.3.4
|
docker exec f2b-control-center fail2ban-client set manual-bans unbanip 1.2.3.4
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Persistent Data
|
## Volumes
|
||||||
|
|
||||||
| Volume | Mount | Contents |
|
| Volume | Path | Contents |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `f2b-data` | `/data` | `ban-history.json` — ban tracking database |
|
| `f2b-data` | `/data` | Ban history database |
|
||||||
| `f2b-config` | `/etc/fail2ban` | Jail config, filter files (survives image updates) |
|
| `f2b-config` | `/etc/fail2ban` | Jail and filter config (survives image updates) |
|
||||||
| _(bind mount)_ | `/nginx-logs` | Your NPM log directory (read-only) |
|
| bind mount | `/nginx-logs` | NPM log directory (read-only) |
|
||||||
|
|
||||||
To back up or inspect volumes:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm -v f2b-data:/data busybox tar czf - /data > f2b-data-backup.tar.gz
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
**Dashboard shows no bans / "Error: ..."**
|
|
||||||
|
|
||||||
Fail2Ban may still be starting up. Check:
|
|
||||||
```bash
|
|
||||||
docker-compose logs f2b-control-center | grep fail2ban
|
|
||||||
docker exec f2b-control-center fail2ban-client ping
|
|
||||||
```
|
|
||||||
|
|
||||||
**Bans are created but IPs are not actually blocked**
|
|
||||||
|
|
||||||
Ensure `network_mode: host` is set in `docker-compose.yml`. In bridge mode, iptables rules are isolated to the container's network namespace and won't block host-level traffic.
|
|
||||||
|
|
||||||
**Log scan returns no results**
|
|
||||||
|
|
||||||
- Verify `NPM_LOG_DIR` in `.env` points to the correct directory
|
|
||||||
- Check the volume is mounted: `docker exec f2b-control-center ls /nginx-logs/`
|
|
||||||
- Confirm log files match the pattern `proxy-host-*_access.log`
|
|
||||||
- Check that logs contain `[Client X.X.X.X]` entries (see Log Format section)
|
|
||||||
|
|
||||||
**"iptables: command not found" or permission errors**
|
|
||||||
|
|
||||||
The container requires `network_mode: host` (or `cap_add: [NET_ADMIN, NET_RAW]` for bridge mode). Verify your `docker-compose.yml` configuration.
|
|
||||||
|
|
||||||
**Fail2Ban filter not matching**
|
|
||||||
|
|
||||||
Test the filter directly:
|
|
||||||
```bash
|
|
||||||
docker exec f2b-control-center \
|
|
||||||
fail2ban-regex /nginx-logs/proxy-host-1_access.log \
|
|
||||||
/etc/fail2ban/filter.d/http-errors.conf --print-all-matched
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
f2b-control-center/
|
|
||||||
├── Dockerfile # Single-container image (fail2ban + node + supervisor)
|
|
||||||
├── docker-compose.yml # Service definition
|
|
||||||
├── .env.example # Configuration template
|
|
||||||
├── entrypoint.sh # First-run init + supervisor start
|
|
||||||
├── healthcheck.sh # Docker health check
|
|
||||||
├── supervisor.conf # Process management (fail2ban + dashboard)
|
|
||||||
├── LICENSE
|
|
||||||
├── README.md
|
|
||||||
├── dashboard/
|
|
||||||
│ ├── package.json
|
|
||||||
│ ├── server.js # Express API + log scanning
|
|
||||||
│ └── public/
|
|
||||||
│ ├── index.html # Dashboard UI
|
|
||||||
│ ├── style.css # Terminal-style theme
|
|
||||||
│ └── favicon.svg
|
|
||||||
└── fail2ban/
|
|
||||||
├── jail.local # Jail configuration (installed on first run)
|
|
||||||
├── filter.d/
|
|
||||||
│ ├── badbot.conf # Bad bot UA detection
|
|
||||||
│ ├── http-errors.conf # HTTP error spamming
|
|
||||||
│ ├── npm-probe.conf # Exploit path probing
|
|
||||||
│ └── manual-bans.conf # Empty filter (manual bans only)
|
|
||||||
└── action.d/
|
|
||||||
└── webhook.conf # Optional: HTTP webhook on ban/unban
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT — see [LICENSE](LICENSE).
|
MIT
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
# ── F2B Control Center — jail configuration (standard) ───────────────────────
|
# ── F2B Control Center — jail configuration ───────────────────────────────────
|
||||||
#
|
|
||||||
# Installed to /etc/fail2ban/jail.local on first container start.
|
# Installed to /etc/fail2ban/jail.local on first container start.
|
||||||
# Persisted in the f2b-config Docker volume — survives image updates.
|
# Persisted in the f2b-config Docker volume — survives image updates.
|
||||||
#
|
#
|
||||||
# WHITELIST: managed by the dashboard. The ignoreip line below is written by
|
# CLOUDFLARE: set CF_EMAIL + CF_APIKEY in docker-compose.yml to enable WAF banning.
|
||||||
# the entrypoint (from SUBNETS_TO_IGNORE) and updated live by the dashboard
|
|
||||||
# without restarting fail2ban.
|
|
||||||
#
|
|
||||||
# CLOUDFLARE: to also block IPs at the CF level, use docker-compose.cloudflare.yml
|
|
||||||
# instead of docker-compose.yml. It installs jail.cloudflare.local which
|
|
||||||
# adds the cloudflare action to every jail automatically.
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
|
|||||||
Reference in New Issue
Block a user