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,26 +1,28 @@
name: selfhostedops/crs-route-exclusions
# ep13: schaltet die CRS-Coraza-Engine (SecLang) fuer genau die legitimen
# Anwendungs-Routen ab, auf denen CRS sonst Fehlalarme wirft. ctl:ruleEngine=Off
# betrifft nur die SecLang-Regeln (CRS + base-config), die nativen vpatch- und
# generic-Regeln von CrowdSec laufen weiter, auch auf diesen Routen.
# ep13: entfernt je legitimer Route NUR die CRS-Anomalie-Blockregeln (949110
# inbound, 959100 outbound), sodass das generische CRS dort nicht mehr sperrt.
# Die Coraza-Engine bleibt an (ruleEngine On), deshalb laufen die nativen
# CrowdSec-vpatch-Regeln auf diesen Routen weiter (bekannte CVEs bleiben
# geschuetzt). NICHT ctl:ruleEngine=Off benutzen, das schaltet auch vpatch ab.
# Die Regel muss VOR crowdsecurity/crs stehen (Phase 1).
seclang_rules:
# Forgejo git smart-HTTP (clone/push), binaerer Pack-Body
- |
SecRule REQUEST_HEADERS:Host "@beginsWith git." "id:900101,phase:1,pass,nolog,chain,msg:'ep13 exclude git smart-http'"
SecRule REQUEST_URI "@rx (git-upload-pack|git-receive-pack|/info/refs\?service=)" "ctl:ruleEngine=Off"
# Forgejo REST-API: Issues, Datei-Inhalte (Nutzer schreiben Code/SQL/.env)
SecRule REQUEST_HEADERS:Host "@beginsWith git." "id:900101,phase:1,pass,nolog,chain,msg:'ep13 crs-off git smart-http'"
SecRule REQUEST_URI "@rx (git-upload-pack|git-receive-pack|/info/refs\?service=)" "ctl:ruleRemoveById=949110,ctl:ruleRemoveById=959100"
# Forgejo REST-API: Issues, Datei-Inhalte
- |
SecRule REQUEST_HEADERS:Host "@beginsWith git." "id:900102,phase:1,pass,nolog,chain,msg:'ep13 exclude forgejo api'"
SecRule REQUEST_URI "@beginsWith /api/v1/" "ctl:ruleEngine=Off"
# Outline Dokument-API (Codebloecke)
SecRule REQUEST_HEADERS:Host "@beginsWith git." "id:900102,phase:1,pass,nolog,chain,msg:'ep13 crs-off forgejo api'"
SecRule REQUEST_URI "@beginsWith /api/v1/" "ctl:ruleRemoveById=949110,ctl:ruleRemoveById=959100"
# Outline Dokument-API
- |
SecRule REQUEST_HEADERS:Host "@beginsWith wiki." "id:900103,phase:1,pass,nolog,chain,msg:'ep13 exclude outline api'"
SecRule REQUEST_URI "@beginsWith /api/" "ctl:ruleEngine=Off"
# ntfy Publish (POST /<topic>, freier Text)
SecRule REQUEST_HEADERS:Host "@beginsWith wiki." "id:900103,phase:1,pass,nolog,chain,msg:'ep13 crs-off outline api'"
SecRule REQUEST_URI "@beginsWith /api/" "ctl:ruleRemoveById=949110,ctl:ruleRemoveById=959100"
# ntfy Publish (POST /<topic>)
- |
SecRule REQUEST_HEADERS:Host "@beginsWith ntfy." "id:900104,phase:1,pass,nolog,chain,msg:'ep13 exclude ntfy publish'"
SecRule REQUEST_METHOD "@streq POST" "ctl:ruleEngine=Off"
# n8n Webhook-Pfade (beliebiges JSON)
SecRule REQUEST_HEADERS:Host "@beginsWith ntfy." "id:900104,phase:1,pass,nolog,chain,msg:'ep13 crs-off ntfy publish'"
SecRule REQUEST_METHOD "@streq POST" "ctl:ruleRemoveById=949110,ctl:ruleRemoveById=959100"
# n8n Webhook-Pfade
- |
SecRule REQUEST_HEADERS:Host "@beginsWith n8n." "id:900105,phase:1,pass,nolog,chain,msg:'ep13 exclude n8n webhook'"
SecRule REQUEST_URI "@beginsWith /webhook" "ctl:ruleEngine=Off"
SecRule REQUEST_HEADERS:Host "@beginsWith n8n." "id:900105,phase:1,pass,nolog,chain,msg:'ep13 crs-off n8n webhook'"
SecRule REQUEST_URI "@beginsWith /webhook" "ctl:ruleRemoveById=949110,ctl:ruleRemoveById=959100"