{"id":19923378,"url":"https://github.com/corazawaf/coraza-spoa","last_synced_at":"2026-03-01T11:05:42.351Z","repository":{"id":37886119,"uuid":"470478563","full_name":"corazawaf/coraza-spoa","owner":"corazawaf","description":"A wrapper for integrating the OWASP Coraza WAF with HAProxy's SPOE filters.","archived":false,"fork":false,"pushed_at":"2025-03-24T13:02:33.000Z","size":258,"stargazers_count":98,"open_issues_count":27,"forks_count":22,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-03-24T14:21:42.182Z","etag":null,"topics":["coraza","haproxy","haproxy-spoa","waf","web-application-firewall"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/corazawaf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-16T07:37:41.000Z","updated_at":"2025-03-24T13:01:27.000Z","dependencies_parsed_at":"2024-01-02T13:59:47.727Z","dependency_job_id":"30d4df9b-f743-4636-a864-e58d466d9477","html_url":"https://github.com/corazawaf/coraza-spoa","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corazawaf%2Fcoraza-spoa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corazawaf%2Fcoraza-spoa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corazawaf%2Fcoraza-spoa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/corazawaf%2Fcoraza-spoa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/corazawaf","download_url":"https://codeload.github.com/corazawaf/coraza-spoa/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247198445,"owners_count":20900079,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["coraza","haproxy","haproxy-spoa","waf","web-application-firewall"],"created_at":"2024-11-12T22:14:03.654Z","updated_at":"2026-03-01T11:05:42.345Z","avatar_url":"https://github.com/corazawaf.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"\u003ch1\u003e\n  \u003cimg src=\"https://coraza.io/images/logo_shield_only.png\" align=\"left\" height=\"46px\" alt=\"\"/\u003e\n  \u003cspan\u003eCoraza SPOA - HAProxy Web Application Firewall\u003c/span\u003e\n\u003c/h1\u003e\n\n[![Code Linting](https://github.com/corazawaf/coraza-spoa/actions/workflows/lint.yaml/badge.svg)](https://github.com/corazawaf/coraza-spoa/actions/workflows/lint.yaml)\n[![CodeQL Scanning](https://github.com/corazawaf/coraza-spoa/actions/workflows/codeql.yaml/badge.svg)](https://github.com/corazawaf/coraza-spoa/actions/workflows/codeql.yaml)\n\nCoraza SPOA is a system daemon which brings the Coraza Web Application Firewall (WAF) as a backing service for HAProxy. It is written in Go, Coraza supports ModSecurity SecLang rulesets and is 100% compatible with the OWASP Core Rule Set v4.\n\nHAProxy includes a [Stream Processing Offload Engine](https://www.haproxy.com/blog/extending-haproxy-with-the-stream-processing-offload-engine) [SPOE](https://raw.githubusercontent.com/haproxy/haproxy/master/doc/SPOE.txt) to offload request processing to a Stream Processing Offload Agent (SPOA). Coraza SPOA embeds the [Coraza Engine](https://github.com/corazawaf/coraza), loads the ruleset and filters http requests or application responses which are passed forwarded by HAProxy for inspection.\n\n## Compilation\n\n### Build\n\nThe command `go run mage.go build` will compile the source code and produce the executable file `coraza-spoa` inside the `build/` folder.\n\n## Configuration\n\n## Coraza SPOA\n\nThe example configuration file is [example/coraza-spoa.yaml](https://github.com/corazawaf/coraza-spoa/blob/main/example/coraza-spoa.yaml), you can copy it and modify the related configuration information. You can start the service by running the command:\n\n```\ncoraza-spoa -config /etc/coraza-spoa/coraza-spoa.yaml\n```\n\n## HAProxy SPOE\n\nConfigure HAProxy to exchange messages with the SPOA. The example SPOE configuration file is [coraza.cfg](https://github.com/corazawaf/coraza-spoa/blob/main/example/haproxy/coraza.cfg), you can copy it and modify the related configuration information. Default directory to place the config is `/etc/haproxy/coraza.cfg`.\n\n```ini\n# /etc/haproxy/coraza.cfg\nspoe-agent coraza-agent\n    ...\n    use-backend coraza-spoa\n\nspoe-message coraza-req\n    args app=str(sample_app) id=unique-id src-ip=src ...\n```\n\nThe application name from `config.yaml` must match the `app=` name.\n\nThe backend defined in `use-backend` must match a `haproxy.cfg` backend which directs requests to the SPOA daemon reachable via `127.0.0.1:9000`.\n\nInstead of the hard coded application name `str(sample_app)` you can use some HAProxy variables. For example, frontend name `fe_name`.\n\n## HAProxy\n\nConfigure HAProxy with a frontend, which contains a `filter` statement to forward requests to the SPOA and deny based on the returned action. Also add a backend section, which is referenced by use-backend in `coraza.cfg`.\n\n```haproxy\n# /etc/haproxy/haproxy.cfg\nfrontend web\n    filter spoe engine coraza config /etc/haproxy/coraza.cfg\n    ...\n    http-request deny deny_status 403 hdr waf-block \"request\" if { var(txn.coraza.action) -m str deny }\n    ...\n\nbackend coraza-spoa\n    mode tcp\n    option spop-check\n    server s1 127.0.0.1:9000 check\n```\n\nA comprehensive HAProxy configuration example can be found in [example/haproxy/coraza.cfg](https://github.com/corazawaf/coraza-spoa/blob/main/example/haproxy/coraza.cfg).\n\nBecause, in the SPOE configuration file (coraza.cfg), we declare to use the backend [coraza-spoa](https://github.com/corazawaf/coraza-spoa/blob/main/example/haproxy/coraza.cfg#L13) to communicate with the service, so we need also to define it in the [HAProxy file](https://github.com/corazawaf/coraza-spoa/blob/main/example/haproxy/haproxy.cfg#L50):\n\nIf you intend to access coraza-spoa service from another machine, remember to change the binding networking directives (IPAddressAllow/IPAddressDeny) in [contrib/coraza-spoa.service](https://github.com/corazawaf/coraza-spoa/blob/main/contrib/coraza-spoa.service)\n\n## HAProxy Logging\n\nTo gain full visibility into WAF actions directly from your HAProxy logs, you can use the transaction variables exported by the Coraza-SPOA agent.\n\n### Available Variables\n\nThe agent populates the following variables in the `txn` scope:\n\n* **`txn.coraza.id`**: The unique transaction ID.\n* **`txn.coraza.status`**: The HTTP status code determined by the WAF (e.g., 403).\n* **`txn.coraza.anomaly_score`**: The total inbound anomaly score for the request.\n* **`txn.coraza.rules_hit`**: The total count of triggered attack rules.\n* **`txn.coraza.rule_ids`**: A comma-separated list of triggered Rule IDs (if enabled).\n* **`txn.coraza.error`**: Contains SPOA-related errors if the transaction fails.\n\n### Example Log Formats\n\nYou can incorporate these variables into your `log-format` directive in `haproxy.cfg`.\n\n**1. Standard Score Tracking**\nUse this for general monitoring of threat levels and rule counts:\n\n```haproxy\nlog-format \"%ci:%cp\\ [%t]\\ %ft\\ %b/%s\\ %Th/%Ti/%TR/%Tq/%Tw/%Tc/%Tr/%Tt\\ %ST\\ %B\\ %CC\\ %CS\\ %tsc\\ %ac/%fc/%bc/%sc/%rc\\ %sq/%bq\\ %hr\\ %hs\\ %{+Q}r\\ %[var(txn.coraza.id)]\\ spoa-error:\\ %[var(txn.coraza.error)]\\ waf-hit:\\ %[var(txn.coraza.status)]\\ score:%[var(txn.coraza.anomaly_score)]\\ rules_hit:%[var(txn.coraza.rules_hit)]\"\n```\n\n**2. Extended Debugging (with Rule IDs)**\nUse this if you need to identify exactly which rules were triggered to troubleshoot false positives. \n\n\u003e **Note:** Exporting the specific Rule IDs requires explicit activation in your Coraza configuration.\n```coraza.cfg\nspoe-message coraza-req\n    \n    args app= ... exportRuleIDs=bool(true)\n\nspoe-message coraza-res\n    \n    args app= ... exportRuleIDs=bool(true)\n\n  .....\n```\n```haproxy\nlog-format \"%ci:%cp\\ [%t]\\ %ft\\ %b/%s\\ %Th/%Ti/%TR/%Tq/%Tw/%Tc/%Tr/%Tt\\ %ST\\ %B\\ %CC\\ %CS\\ %tsc\\ %ac/%fc/%bc/%sc/%rc\\ %sq/%bq\\ %hr\\ %hs\\ %{+Q}r\\ %[var(txn.coraza.id)]\\ spoa-error:\\ %[var(txn.coraza.error)]\\ waf-hit:\\ %[var(txn.coraza.status)]\\ rule_ids:\\ %[var(txn.coraza.rule_ids)]\\ rules-hit:\\ %[var(txn.coraza.rules_hit)]\"\n```\n\n### Custom Rules \u0026 ID Ranges Allocation\n\nTo avoid conflicts with the OWASP Core Rule Set (CRS) and to ensure that the SPOA agent exports accurate metrics to HAProxy (`rules_hit` \u0026 `rule_ids`), you must strictly adhere to the following Rule ID ranges for local rules:\n\n* **Infrastructure \u0026 Whitelists (IDs: 100000 - 189999):** Use this range for IP whitelists, disabling specific CRS rules, or tuning (e.g., GeoIP limits). Rules in this range are **intentionally ignored** by the SPOA agent's attack counter to prevent false positives in your HAProxy metrics.\n* **Custom Attack \u0026 Hardening Rules (IDs: 190000 - 199999):** Use this range for actual security blocks and custom hardening rules. Rules in this range are actively monitored. If triggered, they will increment the `rules_hit` counter and their IDs will be exported in the `rule_ids` variable.\n\n## Docker\n\n- Build the coraza-spoa image `cd ./example ; docker compose build`\n- Run haproxy, coraza-spoa and a mock server `docker compose up`\n- Perform a request which gets blocked by the WAF: `curl http://localhost:8080/\\?x\\=/etc/passwd`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorazawaf%2Fcoraza-spoa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcorazawaf%2Fcoraza-spoa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcorazawaf%2Fcoraza-spoa/lists"}