feat(waf): reference HAProxy + SPOE config for Coraza (M1-02) - #163
Merged
Merged
Conversation
- add scaffold haproxy.cfg with single frontend, spoe filter - add scaffold coraza.cfg with spoe-agent coraza-agent, timeouts config, spoe-message on frontend http request - add configs/haproxy/README.md documenting request flow and SPOE variables - remove obsolete deploy/docker scaffolds superseded by configs/haproxy/ Refs #105 Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Adds a hand-written, reference HAProxy + SPOE configuration to integrate HAProxy with the Coraza SPOA (M1-02), intended to serve as the seed for the upcoming Jinja2 config generator in M2.
Changes:
- Added
configs/haproxy/haproxy.cfgreference HAProxy config with SPOE filter + vhost routing and deny-on-txn.coraza.action=deny. - Added
configs/haproxy/coraza.cfgSPOE engine/message definition to send request metadata to Coraza SPOA. - Added
configs/haproxy/README.mddocumenting request flow, SPOE variables/args, validation steps; removed obsoletedeploy/docker/*scaffolding configs.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| deploy/docker/haproxy/haproxy.cfg | Deleted old HAProxy scaffold config (now superseded by configs/haproxy/). |
| deploy/docker/coraza/coraza.conf | Deleted old Coraza scaffold config (now superseded by new config structure). |
| configs/haproxy/haproxy.cfg | New reference HAProxy config: frontend, ACL, SPOE filter, deny logic, app + SPOA backends. |
| configs/haproxy/coraza.cfg | New SPOE agent/message config for Coraza integration. |
| configs/haproxy/README.md | New documentation for the reference config, flow, variables, validation, and issue links. |
Comment on lines
+47
to
+52
| | Variable | Meaning | | ||
| |----------------------|--------------------------------------------------------| | ||
| | `txn.coraza.action` | Decision string (`deny` blocks; anything else allows) | | ||
| | `txn.coraza.score` | Anomaly score from the rule set, propagated as header | | ||
| | `txn.coraza.id` | Transaction id correlated with HAProxy's `unique-id` | | ||
|
|
There was a problem hiding this comment.
After reviewing the file, all three tables in configs/haproxy/README.md already use single | row starts — there are no || double-pipe prefixes in the file. The automated reviewer comment appears to have been a false positive. No changes are needed.
Comment on lines
+62
to
+75
| | Argument | HAProxy fetch | Notes | | ||
| |------------|------------------|----------------------------------------| | ||
| | `app` | `str(default)` | Which Coraza application bundle to use | | ||
| | `id` | `unique-id` | Same value as the `X-Request-ID` header| | ||
| | `src-ip` | `src` | Client IP | | ||
| | `src-port` | `src_port` | Client TCP port | | ||
| | `dst-ip` | `dst` | HAProxy bind IP | | ||
| | `dst-port` | `dst_port` | HAProxy bind port | | ||
| | `method` | `method` | HTTP method | | ||
| | `path` | `path` | Request path without query | | ||
| | `query` | `query` | Raw query string | | ||
| | `version` | `req.ver` | HTTP version | | ||
| | `headers` | `req.hdrs` | All request headers, framed for SPOE | | ||
| | `body` | `req.body` | Request body (subject to SPOA limits) | |
Comment on lines
+9
to
+11
| # This file is the hand-written seed that the M2 Jinja2 template | ||
| # generator will later reproduce from the policy database. | ||
|
|
Comment on lines
+34
to
+39
| # Sent once per HTTP request, before the backend selection. The | ||
| # argument names on the right hand side are HAProxy fetches; the | ||
| # names on the left hand side are what the Coraza SPOA expects. | ||
| spoe-message coraza-req | ||
| args app=str(default) id=unique-id src-ip=src src-port=src_port dst-ip=dst dst-port=dst_port method=method path=path query=query version=req.ver headers=req.hdrs body=req.body | ||
| event on-frontend-http-request |
Comment on lines
+11
to
+15
| | File | Purpose | | ||
| |--------------|----------------------------------------------------------| | ||
| | `haproxy.cfg`| Frontend, vhost ACL, SPOE filter, backends | | ||
| | `coraza.cfg` | SPOE engine + message definition for the Coraza SPOA | | ||
| | `README.md` | This document | |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot stopped work on behalf of
bihius due to an error
April 25, 2026 10:10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the hand-written reference HAProxy configuration that M1-02 calls for, so HAProxy can consult the Coraza SPOA before forwarding requests. This file becomes the seed the M2 Jinja2 template generator (#110) must reproduce.
configs/haproxy/haproxy.cfg— single:80frontend,host_appACL forapp.local, SPOE filterengine coraza,txn.coraza.action == "deny"→ 403,be_appbackend forbackend:8000,mode tcpcoraza-spoabackend forcoraza:9000.init-addr last,libc,nonesohaproxy -cworks outside Docker Compose.configs/haproxy/coraza.cfg—[coraza]SPOE engine:spoe-agent coraza-agent(var-prefixcoraza,set-on-error error, hello/idle/processing timeouts,use-backend coraza-spoa) andspoe-message coraza-reqonon-frontend-http-requestcarrying app/id/src/dst/method/path/query/version/headers/body.configs/haproxy/README.md— request-flow diagram,txn.coraza.*variables, every SPOE message argument with its HAProxy fetch, fail-open behaviour (linked to Handle Coraza failures in degraded mode #80), local + Docker validation commands, and links to ADR-007 / M1-03 — Coraza SPOA and OWASP CRS 4.x bundle #106 / M1-04 — Full-stack docker-compose with HAProxy and Coraza #107 / M1-05 — End-to-end smoke test (benign allowed, SQLi blocked) #108 / M2-01 — Jinja2 templates for haproxy.cfg and CRS configs #110.deploy/docker/haproxy/haproxy.cfganddeploy/docker/coraza/coraza.confscaffolds, which are superseded byconfigs/haproxy/. Compose mounts still pointing at the old paths will be repointed in M1-04 — Full-stack docker-compose with HAProxy and Coraza #107.Refs #105. Full DoD ("benign request forwarded, SQLi blocked end-to-end") depends on #106 (CRS bundle), #107 (compose wiring), and #108 (smoke test).
Test plan
docker run --rm -v "$PWD/configs/haproxy:/usr/local/etc/haproxy:ro" haproxy:3.0-alpine haproxy -W -c -f /usr/local/etc/haproxy/haproxy.cfgexits 0 with no warnings/alerts (onlyNOTICElines aboutbackend/corazaDNS, expected outside Compose).