Centrally-managed fail2ban + WAF + filtering DNS, with a hub/agent architecture.
One hub (central manager) and one lightweight agent per protected host. Agents enforce locally (WAF reverse proxy, nftables firewall, connection-flood detect, fail2ban-style log-watch, optional filtering DNS) and report to the hub. The hub owns the managed policy (WAF rules, DNS ACLs, log jails, firewall whitelist, thresholds) and pushes it to agents on check-in. The hub does no local enforcement.
Hub state (users, MFA, config, agent roster, blocklists) is persisted in PostgreSQL 17 so it survives restarts and is easy to migrate.
cd deploy/hub export DDOS_STOPPER_HUB_ADMIN_TOKEN=$(openssl rand -hex 24) # machine/API token (automation) export DDOS_STOPPER_ENROLL_TOKEN=$(openssl rand -hex 24) # agent enrollment secret export DDOS_STOPPER_ADMIN_USER=admin export DDOS_STOPPER_ADMIN_PASSWORD='choose-a-strong-one' docker compose up -d --build # starts postgres:17 + the hub # open http://<host>:9090/ and sign in as the admin user
The compose file runs postgres:17 with a healthcheck and a named volume; the hub waits for it and applies its schema on first run. The DATABASE_URL is set for you. State persists across docker compose down/up.
On each protected host, run an agent pointed at the hub with the same enrollment token:
cd deploy/agent DDOS_STOPPER_HUB_URL=http://<hub>:9090 DDOS_STOPPER_ENROLL_TOKEN=<enroll> \ docker compose up -d --build # or on a systemd host: sudo ./install.sh --agent --hub http://<hub>:9090 --token <enroll>
The agent registers with a stable id (hostname + machine-id) and appears in the console Overview within a few seconds. Renaming or restarting never re-enrolls it.
/var/log read-only so the log-watch (fail2ban) jails can read host logs.The console login supports three methods:
A machine/API token (hub.token) always acts as admin for automation.
The Profile tab manages your own account:
| Tab | What you edit |
|---|---|
| WAF Rules | Regex rules (name, on/off, target: path/query/body/header/user-agent/method, pattern) + auto-block criteria (hits, window, rate-limit, ban-vs-403). |
| DNS | Protocol/port monitor toggles, per-type thresholds, tunneling toggle, allowed client CIDRs, allow/block domain lists. |
| Log Jails | fail2ban jails: name, log paths, filter regex (with an (?P<ip>…) group), findtime, maxretry, bantime. |
| Firewall | Whitelist CIDRs (never banned) + default ban TTL. |
Every config tab has a Scope selector: Global (all agents) or a specific agent (a per-agent override). Edit, then Save config. Agents fetch and apply it live on their next check-in (typically ≤10s) — no restart. A per-agent override replaces global for that agent (badge: override).
The Global Blocklist tab lets an operator ban an IP (or CIDR) once at the hub; it's delivered to every agent and enforced in each agent's nftables. Remove it to un-block fleet-wide on the next check-in. Global bans show a red global badge in each agent's blocklist and can't be locally unblocked (remove them at the hub).
Admins manage users on the Users tab. Roles:
| Role | Can |
|---|---|
| viewer | read everything |
| operator | + push blocks & config, edit the global blocklist |
| admin | + manage users & settings |
The Overview shows fleet KPIs (agents online/offline, total blocked, WAF blocks, DNS sinkholes, detect events), the agents list (status, pillars, counters), and — on click — a per-agent detail with its live blocklist (per-row unblock), recent activity feed, and a manual-block form. Event tags: WAF, DNS, JAIL (log-watch), AUTO (detect/conn-flood), BLOCK/UNBLK (manual/global).
← back to the console · see also docs/USER_GUIDE.md and docs/PROTECTIONS_TEST_REPORT.md in the repository.