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)
|
## Production Deployment (PM2)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
Reference in new issue
Block a user