Episode 13: correct the CRS exclusion mechanism (ruleRemoveById, not ruleEngine)

Follow-up measurements showed the first exclusion approach was too broad.
ctl:ruleEngine=Off disables the whole Coraza engine for the route, which also
turns off the native virtual-patching rules: a .env probe on such a route
returned 404 (no WAF) instead of 403.

The exclusion now uses ctl:ruleRemoveById=949110,959100 (the CRS inbound and
outbound anomaly-score blockers) so the engine stays on and virtual patching
keeps firing on the excluded routes. Measured: .env / PHPUnit / .git/config
still 403 on those routes, generic SQLi/XSS pass, non-excluded hosts keep full
CRS (27 of 41). Rule order relative to the CRS rule no longer matters.

Also measured and documented: RemoveInBandRuleByName in a pre_eval hook does
nothing to CRS (SQLi stayed 403 globally and per route); a full authenticated
Vaultwarden flow (register, login, sync, cipher create) passes under full CRS
on the vault host. README traps and numbers updated accordingly.
This commit is contained in:
Maximilian Grimm 2026-09-16 21:06:32 +02:00
parent 26a7da0495
commit 22300d7a28
3 changed files with 60 additions and 45 deletions

View file

@ -1,9 +1,10 @@
name: selfhostedops/appsec-tuned
default_remediation: ban
# ep13: vpatch + generic (nativ) plus OWASP CRS (SecLang). Eine lokale
# SecLang-Regel schaltet die CRS-Engine je legitimer Route ab (ctl:ruleEngine),
# die native vpatch-Abdeckung bleibt dort erhalten. Die Exclusion-Regel muss
# VOR crowdsecurity/crs stehen, damit ctl in Phase 1 greift.
# ep13: vpatch + generic (nativ) plus OWASP CRS (SecLang). Die lokale Regel
# crs-route-exclusions entfernt je legitimer Route nur die CRS-Anomalie-
# Blockregeln (ctl:ruleRemoveById), die native vpatch-Abdeckung bleibt dort
# erhalten. Reihenfolge zu crowdsecurity/crs ist egal (Phase 1 vor Phase 2),
# der Uebersicht halber steht die Ausnahme oben.
inband_rules:
- crowdsecurity/base-config
- selfhostedops/crs-route-exclusions