gitea 920b69cfca feat: plug-and-play refactor — docker-npm action, CF support, whitelist live-update
- Replace iptables-allports with docker-npm action (DOCKER-USER + xt_string
  X-Forwarded-For matching + INPUT chain) matching user's working setup
- Add telegram_notif.sh (deployed to /data/action.d/ at first run, user-editable)
- Add cloudflare.conf action; jail.cloudflare.local enabled via CF compose file
- Two compose files: docker-compose.yml (standard) and docker-compose.cloudflare.yml
- entrypoint: modprobe xt_string, DOCKER-USER chain check, CF jail auto-selection,
  telegram_notif.sh deployment to persistent volume on first run
- Fix whitelist live-update: addignoreip/delignoreip called alongside jail.local write
- Hardcode AUTOBAN_THR=75 and DEFAULT_DAYS=3 (remove env vars)
- Include Nginx Proxy Manager in both compose files with shared log bind mount
- Rewrite filters for actual NPM log format ([Client <HOST>] real IP extraction)
- Add DATA_DIR, Telegram, CF API key fields to .env.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 15:08:06 +00:00

F2B Control Center

A batteries-included Fail2Ban distribution for Nginx Proxy Manager environments, packaged as a single Docker container.

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.


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

  • Docker Engine 20.10+
  • Docker Compose v2+
  • Nginx Proxy Manager running and producing access logs
  • Linux host with iptables support (for active banning)

Quick Start

1. Clone the repository

git clone http://YOUR_GITEA_HOST/YOUR_USER/f2b-control-center.git
cd f2b-control-center

2. Configure your environment

cp .env.example .env

Edit .env and set at minimum:

# 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

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

All settings are controlled via .env and the docker-compose.yml environment section.

Variable Default Description
NPM_LOG_DIR /opt/npm/data/logs Host path to NPM access log directory
DASHBOARD_PORT 4000 Port the dashboard listens on
ABUSEIPDB_API_KEY (empty) AbuseIPDB API key — enables threat scoring
AUTOBAN_THRESHOLD 75 Abuse score threshold for auto-ban (0100)
DEFAULT_LOOKBACK_DAYS 3 Default scan window for nginx logs
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

The container runs with network_mode: host by default.

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.

If you only want the dashboard (monitoring without active iptables blocking), you can switch to bridge mode:

# 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:

docker exec -it f2b-control-center bash
vi /etc/fail2ban/jail.local
fail2ban-client reload

Or mount a local directory instead of the volume for easier editing:

# In docker-compose.yml:
volumes:
  - ./my-fail2ban-config:/etc/fail2ban

Log format

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.

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:

# Example: switch http-errors filter to standard nginx format
docker exec -it f2b-control-center bash
vi /etc/fail2ban/filter.d/http-errors.conf
# Comment out Primary, uncomment Alternative lines
fail2ban-client reload

Test a filter against your actual logs:

docker exec f2b-control-center \
  fail2ban-regex /nginx-logs/proxy-host-1_access.log \
  /etc/fail2ban/filter.d/http-errors.conf

Optional Integrations

AbuseIPDB

Set ABUSEIPDB_API_KEY in .env. Free keys are available at 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:

{
  "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:

CF_SYNC=/usr/local/bin/cloudflare-whitelist-sync.sh

Mount the script into the container:

volumes:
  - ./cloudflare-whitelist-sync.sh:/usr/local/bin/cloudflare-whitelist-sync.sh:ro

Managing the Container

# View logs
docker-compose logs -f

# Restart
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

# View current ban status
docker exec f2b-control-center fail2ban-client status

# Manually ban an IP
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

Persistent Data

Volume Mount Contents
f2b-data /data ban-history.json — ban tracking database
f2b-config /etc/fail2ban Jail config, filter files (survives image updates)
(bind mount) /nginx-logs Your NPM log directory (read-only)

To back up or inspect volumes:

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:

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:

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

MIT — see LICENSE.

Description
No description provided
Readme MIT 844 KiB
Languages
JavaScript 34%
HTML 30.1%
CSS 22.2%
Shell 7.7%
Dockerfile 6%