docs: add Cloudflare & anti-DDoS setup guide to README
CI / check (push) Successful in 51s
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / tests-ui (push) Failing after 32s
CI / tests-unit (push) Successful in 1m37s
CI / tests-integration (push) Successful in 1m41s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
CI / check (push) Successful in 51s
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / tests-ui (push) Failing after 32s
CI / tests-unit (push) Successful in 1m37s
CI / tests-integration (push) Successful in 1m41s
CI / preflight (push) Skipped
CI / deploy (push) Skipped
This commit is contained in:
1 parent
c56696b230
commit
64edb81ab7
1 file changed
+88
@@ -687,6 +687,94 @@ The CMS caches through `cached()` / `cachedQuery()` (`src/lib/cache.ts`): an in-
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare & Anti-DDoS Protection
|
||||
|
||||
Two layers work together to keep the origin alive during misuse, floods, and
|
||||
repeat-offender attacks:
|
||||
|
||||
1. **App-layer gate** (production only) — per-IP request buckets per category
|
||||
(pages / API / auth), a whole-site safety valve, and escalation tiers that
|
||||
temporarily hard-block offending IPs (`src/lib/ddos-guard.ts`).
|
||||
`/api/health` is always exempt so the Docker liveness probe never trips it.
|
||||
2. **Automatic Cloudflare edge blocks** (optional, works on every Cloudflare
|
||||
plan including Free) — when the gate hard-blocks an IP, the CMS mirrors the
|
||||
block to the zone's **IP Access Rules** (`src/lib/cloudflare-api.ts`), so a
|
||||
repeat offender is dropped at the Cloudflare edge before it reaches your
|
||||
server. Blocks expire together with the gate's tiered block (a 30s sweep
|
||||
removes them; see `src/instrumentation.ts`).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- The domain must actually be **proxied through Cloudflare** (orange cloud) —
|
||||
automatic edge rules can only stop traffic that transits the edge. Restrict
|
||||
direct access to the origin ports (e.g. only allow your server IP(s),
|
||||
Cloudflare IPs, or swap the firewall to 443/80 only) so attackers cannot
|
||||
bypass Cloudflare.
|
||||
- A Cloudflare API token with the following zone permissions:
|
||||
- `Zone > Zone > Read`
|
||||
- `Zone > Firewall > Edit`
|
||||
Create one at **My Profile → API Tokens → Create Token** (custom token).
|
||||
|
||||
### 1. Environment variables
|
||||
|
||||
`.env.example` documents every anti-DDoS / Cloudflare variable. In your real
|
||||
`.env`:
|
||||
|
||||
```dotenv
|
||||
# --- App-layer gate (production only) ---
|
||||
ANTI_DDOS_ENABLED=true
|
||||
ANTI_DDOS_PAGES_LIMIT=300
|
||||
ANTI_DDOS_PAGES_WINDOW_SEC=60
|
||||
ANTI_DDOS_API_LIMIT=600
|
||||
ANTI_DDOS_API_WINDOW_SEC=60
|
||||
ANTI_DDOS_AUTH_LIMIT=20
|
||||
ANTI_DDOS_AUTH_WINDOW_SEC=60
|
||||
ANTI_DDOS_GLOBAL_LIMIT=18000
|
||||
ANTI_DDOS_GLOBAL_WINDOW_SEC=60
|
||||
ANTI_DDOS_GLOBAL_HALT_MS=10000
|
||||
ANTI_DDOS_VIOLATION_WINDOW_SEC=600
|
||||
ANTI_DDOS_MAX_VIOLATIONS=10
|
||||
ANTI_DDOS_BLOCK_TIERS=5:600,20:3600,50:86400
|
||||
|
||||
# --- Cloudflare API (automatic edge blocks) ---
|
||||
CLOUDFLARE_API_TOKEN=<your API token>
|
||||
CLOUDFLARE_ZONE_ID=<your zone id — Dashboard → your domain → Overview>
|
||||
CLOUDFLARE_AUTO_BLOCK_ENABLED=true
|
||||
```
|
||||
|
||||
Credentials live in env only and are never stored in Redis-visible config.
|
||||
Restart the container after changing them.
|
||||
|
||||
### 2. Verify in the admin panel
|
||||
|
||||
Open **DevOps → Anti-DDoS protection**
|
||||
(`/admin/devops/antiddos`, requires `settings.view`) and:
|
||||
|
||||
- Check the **Cloudflare** card shows the request arrived proxied, and the
|
||||
green **API configured** badge.
|
||||
- Click **Verify connection** — it confirms the token can read the zone and
|
||||
shows the zone name.
|
||||
- Keep the **Automatically create Cloudflare edge blocks** toggle on.
|
||||
- The **Cloudflare edge blocks** card lists every auto-created rule with its
|
||||
remaining TTL; **Remove rule** deletes it — and **Unban** lifts both the
|
||||
host-block and the edge rule in one action.
|
||||
|
||||
### 3. Notes
|
||||
|
||||
- **Only proxied traffic** is edge-blocked. The gate verifies a request
|
||||
actually transited Cloudflare (`CF-Ray` / `CDN-Loop`); a spoofable
|
||||
`CF-Connecting-IP` alone is never trusted.
|
||||
- **Free plan**: IP Access Rules support is plan-independent; the limit is
|
||||
~300 rules per zone and the 30s sweep keeps expired rules cleaned up.
|
||||
- **Distributed botnets**: many unique IPs each under the threshold are shed
|
||||
by the whole-site valve (short global halt) rather than banned one-by-one,
|
||||
which avoids collateral damage to shared IPs.
|
||||
- **Volumetric L3/L4 floods** cannot be stopped by application code — for
|
||||
those, keep the domain on Cloudflare's orange cloud so Cloudflare's own
|
||||
always-on DDoS protection absorbs them.
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment (PM2)
|
||||
|
||||
```bash
|
||||
|
||||
Reference in new issue
Block a user