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
|
|
|
|
# WHP day-one enforce overrides for coraza-spoa.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Global mode in config.yaml is SecRuleEngine DetectionOnly. The rule ID
|
|
|
|
|
|
# ranges below are promoted to enforcement individually, chosen for very
|
|
|
|
|
|
# low false-positive rate on the kinds of customer traffic seen on WHP
|
|
|
|
|
|
# (WordPress, WooCommerce, Divi page builders).
|
|
|
|
|
|
#
|
|
|
|
|
|
# When bumping the upstream coraza-spoa pin (and thus the bundled CRS):
|
|
|
|
|
|
# 1. Skim the CRS CHANGELOG for new/changed rules in these ID ranges.
|
|
|
|
|
|
# 2. Verify they're still high-confidence before promoting the new image.
|
|
|
|
|
|
# 3. Smoke-test in staging detect-only mode for 24h before flipping enforce.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Per-customer false-positive tuning lives in a future per-customer
|
|
|
|
|
|
# override mechanism; v1 is server-wide.
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 930120 — LFI: explicit traversal to sensitive system files
|
|
|
|
|
|
# (/etc/passwd, /proc/self/, /.ssh/, /etc/shadow, /etc/group, etc.)
|
|
|
|
|
|
# Unambiguous probe pattern; no legitimate site path leads here.
|
|
|
|
|
|
# Note: 930xxx as a whole includes broader traversal patterns that can FP
|
|
|
|
|
|
# on legitimate relative-path file browsers — keep those detect-only.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
SecRuleUpdateActionById 930120 "ctl:ruleEngine=On"
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 932100-932160 — RCE: Unix shell command injection
|
|
|
|
|
|
# Patterns like `; cat /etc/passwd`, `|whoami`, backtick `\`uname\``,
|
|
|
|
|
|
# $(...) substitution, &&/|| chaining with shell builtins.
|
|
|
|
|
|
# Don't appear in normal POST bodies, URL params, or headers. Targeting
|
|
|
|
|
|
# these is unambiguous attempted command execution.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
SecRuleUpdateActionById 932100-932160 "ctl:ruleEngine=On"
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 933170-933200 — PHP Webshell access patterns
|
|
|
|
|
|
# Direct requests to known webshell paths: c99.php, r57.php, b374k.php,
|
|
|
|
|
|
# wso.php, alfa.php, mini.php, etc. Almost universally reconnaissance
|
|
|
|
|
|
# scanning for post-exploitation. Even legitimate WordPress installs
|
|
|
|
|
|
# never serve these paths.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
SecRuleUpdateActionById 933170-933200 "ctl:ruleEngine=On"
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 944100-944300 — Log4Shell / JNDI injection
|
|
|
|
|
|
# `${jndi:ldap://}`, `${jndi:rmi://}`, and obfuscated variants thereof
|
|
|
|
|
|
# in headers, query strings, or bodies. Even our PHP/Node stack isn't
|
|
|
|
|
|
# vulnerable, but blocking at the edge keeps logs clean and protects
|
|
|
|
|
|
# any future Java workloads.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
SecRuleUpdateActionById 944100-944300 "ctl:ruleEngine=On"
|
|
|
|
|
|
|
coraza: promote 920440 + 930130 to enforce list (empirical detect-only data)
After ~30 min of detect-only on whp01 we have actionable data on what
fires against legitimate customer traffic vs. attacker recon. Two rules
demonstrably catch only the latter and earn promotion to the day-one
enforce list:
920440 — URL file extension restricted by policy
Caught 124 events in the sample window, ALL backup/config-file
disclosure probes (`/wp-config.php.old`, `/db_backup.sql`,
`/.env.save`, `/releases.sql` ...) from a single GCP-hosted scanner
hammering joshuaknapp.net. Match patterns: .sql (×62), .bak (×5),
.old (×3), .save (×2), .backup, .dist. No legitimate URL on
WP/WooCommerce/Divi/HPR ends in these.
930130 — Restricted File Access Attempt
Caught 117 events, ALL dotfile/VCS/config-disclosure probes
(`/.env`, `/.env.local`, `/.env.bak`, `/.git/config`, `/config.php`,
`/admin/.env`, `/backend/.env` ...). Spread across joshuaknapp.net,
cgdannyb.com, onlinesupplements.net. Notably, HPR's
`/ccdn.php?filename=/eps/...` legitimate audio-delivery URL does NOT
trigger this rule — verified empirically.
Also documented in the "intentionally detect-only" comment block: 933150
fires on WooCommerce checkout when literal `session_start` appears in
billing form data (alphaoneaminos.com saw 2 such events). That's a
canonical CRS false positive on WooCommerce; left detect-only.
Net effect: existing detect_only deployments stay detect-only (the WHP
apply script bind-mounts an empty overrides over the baked-in file).
When operators next flip a server to enforce, these two extra ranges
activate alongside the original day-one list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:00:21 -07:00
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 920440 — URL file extension restricted by policy
|
|
|
|
|
|
# Catches probes for backup / config / dump files: .bak, .old, .save,
|
|
|
|
|
|
# .swp, .sql, .dist, .backup. Promoted to enforce after empirical
|
|
|
|
|
|
# observation on whp01 (2026-05-12, first ~30 min of detect-only):
|
|
|
|
|
|
# 124 events, all backup-file recon — `/wp-config.php.old`,
|
|
|
|
|
|
# `/db_backup.sql`, `/.env.save`, `/releases.sql`, etc. — from a
|
|
|
|
|
|
# single GCP-hosted scanner. Zero false positives observed; standard
|
|
|
|
|
|
# WP/WooCommerce/Divi/HPR URLs do not end in these extensions.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
SecRuleUpdateActionById 920440 "ctl:ruleEngine=On"
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# 930130 — Restricted File Access Attempt
|
|
|
|
|
|
# Catches dotfile / VCS / config-disclosure probes: .env (and .env.local /
|
|
|
|
|
|
# .env.bak / .env.save variants), .git/config, config.php at root or under
|
|
|
|
|
|
# /admin /backend, etc. Distinct from 930120 (system file paths like
|
|
|
|
|
|
# /etc/passwd); this targets application secret files.
|
|
|
|
|
|
#
|
|
|
|
|
|
# Promoted to enforce on the same observation pass that justified 920440:
|
|
|
|
|
|
# 117 events split across joshuaknapp.net (136), cgdannyb.com (51),
|
|
|
|
|
|
# onlinesupplements.net (23) — all `.env`-class disclosure probes.
|
|
|
|
|
|
# Zero false positives observed. Notably, HPR's `/ccdn.php?filename=...`
|
|
|
|
|
|
# audio delivery path does NOT trigger this rule — verified empirically.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
SecRuleUpdateActionById 930130 "ctl:ruleEngine=On"
|
|
|
|
|
|
|
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
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# Rule families intentionally kept at DETECT-ONLY for v1 — high FP rate
|
|
|
|
|
|
# on customer mix. Promote individually after observation:
|
|
|
|
|
|
#
|
2026-05-13 19:13:22 -07:00
|
|
|
|
# 913xxx (Scanner UAs)— matches legitimate ActivityPub federation
|
|
|
|
|
|
# (Mastodon's "...Bot" UA) and SiteLockSpider (a
|
|
|
|
|
|
# paid customer-security service some sites use).
|
|
|
|
|
|
# Observed on whp01 burn-in 2026-05-13:
|
|
|
|
|
|
# 20/185 hits = ~11% FP rate on HPR + greggfranklin
|
|
|
|
|
|
# + suchascream. Detection adds anomaly score
|
|
|
|
|
|
# either way; enforce upside is low.
|
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
|
|
|
|
# 941xxx (XSS) — Divi rich-text editor saves, TinyMCE submissions
|
|
|
|
|
|
# 942xxx (SQLi) — WP admin queries reflected in params
|
coraza: promote 920440 + 930130 to enforce list (empirical detect-only data)
After ~30 min of detect-only on whp01 we have actionable data on what
fires against legitimate customer traffic vs. attacker recon. Two rules
demonstrably catch only the latter and earn promotion to the day-one
enforce list:
920440 — URL file extension restricted by policy
Caught 124 events in the sample window, ALL backup/config-file
disclosure probes (`/wp-config.php.old`, `/db_backup.sql`,
`/.env.save`, `/releases.sql` ...) from a single GCP-hosted scanner
hammering joshuaknapp.net. Match patterns: .sql (×62), .bak (×5),
.old (×3), .save (×2), .backup, .dist. No legitimate URL on
WP/WooCommerce/Divi/HPR ends in these.
930130 — Restricted File Access Attempt
Caught 117 events, ALL dotfile/VCS/config-disclosure probes
(`/.env`, `/.env.local`, `/.env.bak`, `/.git/config`, `/config.php`,
`/admin/.env`, `/backend/.env` ...). Spread across joshuaknapp.net,
cgdannyb.com, onlinesupplements.net. Notably, HPR's
`/ccdn.php?filename=/eps/...` legitimate audio-delivery URL does NOT
trigger this rule — verified empirically.
Also documented in the "intentionally detect-only" comment block: 933150
fires on WooCommerce checkout when literal `session_start` appears in
billing form data (alphaoneaminos.com saw 2 such events). That's a
canonical CRS false positive on WooCommerce; left detect-only.
Net effect: existing detect_only deployments stay detect-only (the WHP
apply script bind-mounts an empty overrides over the baked-in file).
When operators next flip a server to enforce, these two extra ranges
activate alongside the original day-one list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 18:00:21 -07:00
|
|
|
|
# 920xxx (other) — most 920xxx rules; 920440 specifically promoted above
|
|
|
|
|
|
# 933150 — PHP injection FP on WooCommerce checkout
|
|
|
|
|
|
# (`session_start` literal appearing in billing form data)
|
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
|
|
|
|
# 950xxx-953xxx — Data leakage / backup-file disclosure (mixed FP)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
2026-05-14 06:53:37 -07:00
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
# RESERVED RULE-ID RANGE: 990000000 – 990999999
|
|
|
|
|
|
# WHP's coraza_rule_manager generates per-host-exception rules in this range
|
|
|
|
|
|
# (rule ID = 990000000 + target_rule_id). Do NOT add new rules in this range
|
|
|
|
|
|
# from any other source. When bumping the coraza-spoa pin, check the CRS
|
|
|
|
|
|
# changelog for new rules with 9-digit IDs (rare but possible) and re-namespace
|
|
|
|
|
|
# if collision risk emerges.
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|