feat(security): give back to CrowdSec and harden the CTI budget
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 29s
CI / tests-integration (push) Successful in 1m34s
CI / tests-unit (push) Successful in 1m36s
CI / tests-ui (push) Successful in 2m22s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m53s
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 29s
CI / tests-integration (push) Successful in 1m34s
CI / tests-unit (push) Successful in 1m36s
CI / tests-ui (push) Successful in 2m22s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m53s
- Bound the in-process verdict cache (FIFO eviction at 2000 entries) so a
flood of distinct bucket-tripping IPs cannot grow it without limit.
- Record block metadata (reputation, score, behaviors, category, TTL) in
antiddos:block:meta:{ip}, surfaced as the reason in the admin block list;
unban now also clears the metadata and report locks.
- Track daily CTI enrichment usage in Redis (crowdsec:usage:{date}); warn
once at 80% and pause lookups until tomorrow at CROWDSEC_CTI_DAILY_QUOTA
(default 10000, 0 = unlimited) so a via-spread DDoS cannot burn the plan.
- Add opt-in signal push to the CrowdSec community (CAPI watcher): stable
auto-generated 48-char machine_id/password pair persisted in Redis (or via
env), one-time registration, cached JWT login, optional Console enrollment,
and POST /v3/signals with a ban decision, deduped per IP. Never throws and
reports last status to the admin panel with a verify action.
- Admin page: quota usage bar, reporting status/verify channel, and CrowdSec
block reasons in the active-blocks list.
This commit is contained in:
1 parent
ee25545b7f
commit
5e4fc9ab59
8 files changed
+1323
-37
No files matched your search
+25
@@ -177,6 +177,31 @@ const schema = z
|
||||
.int()
|
||||
.positive()
|
||||
.default(86_400),
|
||||
// Daily CTI enrichment quota guard (freemium plan ≈ 10k lookups/day).
|
||||
// The gate stops consulting the API once the counter for today exceeds
|
||||
// it, so a spread DDoS can never silently burn the whole quota; 0
|
||||
// disables the guard.
|
||||
CROWDSEC_CTI_DAILY_QUOTA: z.coerce.number().int().min(0).default(10_000),
|
||||
// Share our own detections back into the CrowdSec community blocklist
|
||||
// (signal push over the Central API). Opt-in: flipping this on publicly
|
||||
// shares blocked IPs + behaviors, so it defaults to off.
|
||||
CROWDSEC_REPORT_ENABLED: z
|
||||
.string()
|
||||
.optional()
|
||||
.transform((value) => value === "true" || value === "1"),
|
||||
// Central API (CAPI) base endpoint; overridden for tests/staging.
|
||||
CROWDSEC_CAPI_BASE_URL: z
|
||||
.string()
|
||||
.url()
|
||||
.default("https://api.crowdsec.net/v3"),
|
||||
// Watcher credentials for signal push. When omitted, a stable pair is
|
||||
// generated once and persisted in Redis (48-char alnum machine id,
|
||||
// per the CAPI schema).
|
||||
CROWDSEC_REPORT_MACHINE_ID: z.string().optional(),
|
||||
CROWDSEC_REPORT_PASSWORD: z.string().optional(),
|
||||
// Optional attachment key from the CrowdSec Console — links our
|
||||
// watcher to your account so pushed signals show up there.
|
||||
CROWDSEC_REPORT_ENROLL_KEY: z.string().optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.NODE_ENV !== "production") return;
|
||||
|
||||
Reference in new issue
Block a user