Commit Graph
5 Commits
Author SHA1 Message Date
shadowdaoandClaude Opus 5.5 cc0652e0c0 fix(waf): 400 duplicate Content-Type; JSON depth limit 64 (round 1)
- hap_listener.tpl (Coraza block): `http-request deny deny_status 400 if
  { req.fhdr_cnt(content-type) gt 1 }`. With two Content-Type lines Coraza
  evaluates every value (text/xml on either line selects its XML processor,
  which accepts a form body without error) while Apache merges the lines
  and PHP parses the body as a form. Measured with Coraza v3.7.0 + PHP 8.3:
  `application/x-www-form-urlencoded` + `text/xml` put the payload in
  $_POST uninspected. Live: two lines -> 400 over HTTP/1.1 and HTTP/2; one
  comma-joined line (browser XHR double-set) is unaffected.
- coraza-spoa/config.yaml: SecRequestBodyJsonDepthLimit 64 (was 1024 from
  the recommended file). Coraza's JSON flattening is quadratic in depth:
  depth 300 (601 B) = 297 ms, depth 1000 (2 KB) = 3.1 s, past the 100 ms
  fail-open timeout. Over the limit it is a 0.1 ms parse error, which WHP's
  enforce-mode rule denies on a complete body (live: depth 65/300/1000 ->
  403, depth 64 and a depth-9 global-styles body -> 200). Legitimate depth
  measured on WordPress core, Gutenberg, WooCommerce and Elementor JSON:
  max 8.
- test-waf-body-inspection.py: 17 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:21:49 -07:00
shadowdaoandClaude Opus 5.5 4f21bb2f76 feat(waf): make Coraza see request bodies; stamp complete/partial; 2026.10.2
Measured on haproxy 3.0.11 + coraza-spoa v0.7.1 + CRS 4.25.0 (local
haproxy-manager + coraza-spoa + echo backend):

- send-spoe-group fired at header time, so req.body held only what arrived
  with the headers. A 933180 payload whose body came in a second send(),
  2s late, or after Expect: 100-continue passed uninspected; 8 parallel
  14 KB payload-first POSTs: 6/40 blocked before, 40/40 after.
- Add `option http-buffer-request` (Coraza block only). Uploads are not
  held: one buffer is inspected, the rest streams (50 MB upload: sha-ok,
  same timing before/after).
- Stamp X-WHP-WAF-Body: complete|partial for Coraza (client copies
  stripped first, stripped again before the backend). Content-Length:
  exact compare; chunked/no-CL: body+headers < 12 KB => complete. WHP's
  generated rules use it to deny body-parse errors (200002/200003) only on
  complete bodies.
- Pin tune.bufsize 16384 (= default) when Coraza is on. Coraza costs
  ~3.3 ms/KB of body and the 100 ms SPOE timeout fails open: at 32 KB a
  payload at byte 0 of a 30 KB base64 body bypassed. SPOP frames > 65535
  are refused by the agent library.
- coraza-spoa/config.yaml: SecRequestBodyLimit 131072 / InMemory 131072 /
  ProcessPartial (Reject 413s even under DetectionOnly).
- Access log gains wafb= (complete|partial) and wafe= (SPOE error =
  request passed uninspected).
- overrides.conf: document (comment only) that the 920440 promotion turns
  the engine On for every URL with a file extension.
- scripts/test-waf-body-inspection.py (16 tests), wired into the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 10:49:27 -07:00
shadowdaoandClaude Sonnet 4.6 99dfe98aaf coraza: pre-CRS Include for runtime per-host exemptions (load-order fix)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:55:51 -07:00
shadowdao de221a1326 coraza: add second Include for runtime-managed local-overrides.conf 2026-05-14 06:51:24 -07:00
shadowdaoandClaude Opus 4.7 9b329fa52b PR 1/3: add coraza-spoa sidecar image
Self-contained sidecar that runs Coraza-SPOA v0.7.1 (latest upstream as of
2026-05-08, with OWASP CRS bundled in the binary). HAProxy will consult it
per-request via SPOE in PR 2; for now this PR ships the image only.

Defines:
- coraza-spoa/Dockerfile       — multi-stage build (golang:1.25 -> distroless),
                                 pinned to v0.7.1, ARG-overridable
- coraza-spoa/config.yaml      — single application "haproxy", JSON audit log
                                 to /var/log/coraza/audit.log, SecRuleEngine
                                 DetectionOnly globally
- coraza-spoa/overrides.conf   — day-one enforce list: scanner UAs (913xxx),
                                 RCE shell injection (932100-932160),
                                 webshell paths (933170-933200), targeted LFI
                                 (930120), Log4Shell/JNDI (944100-944300).
                                 Rationale per-range documented inline.
                                 Detect-only for XSS/SQLi/protocol (high FP
                                 on WP/WooCommerce/Divi customer mix).
- coraza-spoa/README.md        — deployment shape, audit log location, pin
                                 upgrade procedure, false-positive tuning.
- .gitea/workflows/build-push-coraza.yaml — Gitea Action triggered on
                                 coraza-spoa/** changes, publishes
                                 repo.anhonesthost.net/cloud-hosting-platform/
                                 coraza-spoa:latest. Path-scoped so it
                                 doesn't fire on every haproxy-manager push.

No changes to haproxy-manager-base itself in this PR — the existing image
stays bit-identical, used standalone in home networks and other projects
without dependency on this sidecar. PR 2 will add the OPT-IN template
plumbing that lets haproxy-manager call out to this agent when an env var
is set.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 16:28:44 -07:00