Skip to content

feat(waf): add Coraza SPOA CRS bundle - #164

Merged
bihius merged 4 commits into
mainfrom
feat/m1-03-coraza-spoa-bundle
Apr 25, 2026
Merged

bihius merged 4 commits into
mainfrom
feat/m1-03-coraza-spoa-bundle

Conversation

@bihius

@bihius bihius commented Apr 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add OWASP CRS 4.25.0 as a pinned submodule under configs/coraza/crs with baseline Coraza SPOA configuration.
  • Add deploy/docker/coraza.Dockerfile and make coraza-build for a reproducible Coraza SPOA image.
  • Fix Docker Compose and HAProxy SPOE wiring so the full stack can route requests through Coraza.

Test plan

  • make coraza-build
  • docker-compose -f deploy/docker/docker-compose.yml --env-file deploy/docker/.env up -d --build
  • docker-compose -f deploy/docker/docker-compose.yml --env-file deploy/docker/.env ps showed all five services healthy
  • curl -i -H 'Host: app.local' http://localhost:8080/health returned 200 OK
  • curl -i -H 'Host: app.local' "http://localhost:8080/?id=1%27%20OR%20%271%27=%271" returned 403 Forbidden
  • docker-compose -f deploy/docker/docker-compose.yml --env-file deploy/docker/.env down -v
  • uv run pytest --cov=app
  • uv run mypy app/
  • uv run ruff check app/
  • pnpm run type-check
  • pnpm run lint
  • docker run --rm -v "$PWD/configs/haproxy:/usr/local/etc/haproxy:ro" haproxy:3.0-alpine haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg

Closes #106
Related to #107

Add a pinned OWASP CRS 4.25.0 submodule and baseline Coraza SPOA configuration so the M1 stack can build a reproducible WAF image. Also fix the Compose/HAProxy wiring needed for the full-stack smoke path through Coraza.

Made-with: Cursor
Copilot AI review requested due to automatic review settings April 25, 2026 10:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a reproducible Coraza SPOA + OWASP CRS bundle and wires it into the full-stack Docker Compose setup so HAProxy can route requests through Coraza for request inspection.

Changes:

  • Adds pinned OWASP CRS (as a git submodule) plus baseline Coraza config (coraza.conf, crs-setup.conf, coraza-spoa.yaml).
  • Introduces a reproducible Coraza SPOA image build (deploy/docker/coraza.Dockerfile) and make coraza-build.
  • Updates HAProxy SPOE/SPOP wiring and Docker Compose mounts to route traffic through Coraza.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
deploy/docker/docker-compose.yml Build Coraza image in-compose, adjust HAProxy config mounts, update Coraza healthcheck
deploy/docker/coraza/rules/.gitkeep Removes no-longer-used placeholder rules directory
deploy/docker/coraza.Dockerfile New Dockerfile to bake in Coraza config + CRS bundle
configs/haproxy/haproxy.cfg Updates SPOE wiring (send-spoe-group), anomaly score var name, and SPOP checks
configs/haproxy/coraza.cfg Updates SPOE agent configuration to use groups and adds spoe-group
configs/haproxy/README.md Documents updated SPOE group semantics and anomaly score variable
configs/coraza/crs-setup.conf Adds baseline CRS paranoia/anomaly thresholds for the pinned CRS version
configs/coraza/coraza.conf Adds baseline Coraza directives and includes CRS setup + rules
configs/coraza/coraza-spoa.yaml Adds Coraza SPOA service config used by the Docker image
configs/coraza/README.md Documents pinned versions, defaults, and CRS update workflow
README.md Updates “how to access” instructions and adds WAF smoke-test curl
README.commands.md Adds make coraza-build to command reference
Makefile Adds coraza-build target
.gitmodules Adds CRS submodule definition
.dockerignore Restricts Docker build context for reproducible Coraza image builds
Comments suppressed due to low confidence (2)

configs/haproxy/haproxy.cfg:45

  • http-request send-spoe-group is executed before the Host ACL rejection, so even requests for unrecognised vhosts will be sent to Coraza. This increases WAF load and expands the DoS surface unnecessarily. Consider moving the send after the host_app check or adding a condition (e.g., only send to SPOE when host_app matches).
    filter spoe engine coraza config /usr/local/etc/haproxy/coraza.cfg
    http-request send-spoe-group coraza coraza-req

    # Reference vhost: only requests for app.local are accepted.
    # Anything else is rejected before it hits the backend.
    acl host_app hdr(host) -i app.local app.local:80
    http-request deny deny_status 421 if !host_app

configs/haproxy/coraza.cfg:18

  • The comment example still refers to txn.coraza.score, but the HAProxy config/README now use txn.coraza.anomaly_score. Please update this example to avoid confusion when debugging.
    # All variables produced by the agent are exposed under
    # txn.coraza.*  (e.g. txn.coraza.action, txn.coraza.score).
    option              var-prefix  coraza

Comment on lines 71 to 75
healthcheck:
test: ["CMD", "/coraza-spoa", "--help"]
test: ["CMD", "/bin/busybox", "nc", "-z", "127.0.0.1", "9000"]
interval: 10s
timeout: 5s
retries: 10

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since coraza now has a real port-based healthcheck, HAProxy should depend on coraza being healthy (not just started). Otherwise HAProxy may start accepting traffic before the SPOA is listening, causing SPOE errors / fail-open behavior during startup. Update the haproxy.depends_on.coraza.condition to service_healthy to match the other services.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 372427a — changed condition: service_started to condition: service_healthy for the coraza dependency under haproxy. HAProxy will now wait until the Coraza SPOA passes its netcat healthcheck before starting, preventing SPOE fail-open during startup.

Comment thread configs/coraza/README.md
Comment on lines +7 to +12
## Pinned versions

- Coraza SPOA image: `ghcr.io/corazawaf/coraza-spoa:0.6.1`
- OWASP Core Rule Set: `v4.25.0`, pinned as the `configs/coraza/crs` git
submodule

Copilot AI Apr 25, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because CRS is a git submodule, first-time users who clone without --recurse-submodules (or CI checkouts that don’t init submodules) will end up with an empty configs/coraza/crs/ and a non-functional WAF ruleset. Add a short “clone/init” snippet (e.g., git submodule update --init --recursive) to make the setup reproducible.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 565dfa3 — added a "First-time setup" section to configs/coraza/README.md with the git submodule update --init --recursive snippet, placed prominently before the "Pinned versions" section.

Repository owner deleted a comment from Copilot AI Apr 25, 2026
- add alembic upgrade head before starting Uvicorn
- add Postgres DATABASE_URL to Docker .env.example
- update related Docker command docs

Made-with: Cursor
@bihius
bihius merged commit 516b516 into main Apr 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M1-03 — Coraza SPOA and OWASP CRS 4.x bundle

3 participants