A lightweight Stream Processing Offload Agent (SPOA) that contacts the CrowdSec Local API to fetch decisions in real time while aiming to be highly performant and minimize latency for clients. It manages an in-memory cache of bans and captchas, and directs HAProxy on how to treat each connection without blocking the data path.
crowdsec-tcp and HTTP inspection via crowdsec-http-body / crowdsec-http-no-body to protect both web frontends and raw TCP services.The bouncer is a single binary with three key loops:
go-cs-bouncer maintains a long-lived stream to the CrowdSec Local API and feeds new/deleted decisions into the dataset (pkg/dataset).pkg/spoa) listens on TCP and/or Unix sockets, answers HAProxy messages, and applies host-specific logic such as captchas or ban pages.More detailed request/decision flow diagrams are in ARCHITECTURE.md.
1sequenceDiagram2 participant Client3 participant HAProxy4 participant SPOA as SPOA bouncer5 participant Backend67 Note over Client,HAProxy: Client connects8 Client->>HAProxy: Connect (TCP)910 Note over HAProxy,SPOA: Early decision (session-level)11 HAProxy->>SPOA: SPOE: crowdsec-tcp12 SPOA-->>HAProxy: Set txn.crowdsec.* (remediation baseline)1314 Note over Client,HAProxy: HTTP request (per request)15 Client->>HAProxy: HTTP request16 alt body not sent17 HAProxy->>SPOA: SPOE group: crowdsec-http-no-body18 else body sent (required for captcha POST)19 HAProxy->>SPOA: SPOE group: crowdsec-http-body20 end21 SPOA-->>HAProxy: Set txn.crowdsec.* (final remediation + metadata)2223 alt remediation = allow24 HAProxy->>Backend: Forward request25 Backend-->>Client: Response26 else remediation = captcha or ban27 HAProxy->>HAProxy: Render response page (Lua)28 HAProxy-->>Client: Response29 end
The official documentation covers packaging and upgrade notes. If you want to manually build, see the build section below. A quick happy path:
make build.config/crowdsec-spoa-bouncer.yaml to /etc/crowdsec/bouncers/ and set your CrowdSec LAPI URL and API key.lua/ if you do not already ship them.systemctl start crowdsec-haproxy-spoa-bouncer or run ./crowdsec-spoa-bouncer -c /path/to/config.yaml for local tests.Configuration is YAML. Start from the example in config/crowdsec-spoa-bouncer.yaml and override locally with .yaml.local.
The bouncer supports:
Detailed configuration guides:
pkg/host/README.mdpkg/captcha/README.mdinternal/remediation/ban/README.mdARCHITECTURE.mdAdd the SPOE filter and Lua helpers to your frontend. The config files in config/ and the Lua scripts in lua/ show complete examples; the snippet below highlights the essentials:
1frontend www2 bind :803 unique-id-format %[uuid()]4 unique-id-header X-Unique-ID5 filter spoe engine crowdsec config /etc/haproxy/crowdsec.cfg67 acl body_within_limit req.body_size -m int le 512008 http-request send-spoe-group crowdsec crowdsec-http-body if body_within_limit || !{ req.body_size -m found }9 http-request send-spoe-group crowdsec crowdsec-http-no-body if !body_within_limit { req.body_size -m found }1011 http-request redirect code 302 location %[url] if { var(txn.crowdsec.remediation) -m str "allow" } { var(txn.crowdsec.redirect) -m found }1213 http-request lua.crowdsec_handle if { var(txn.crowdsec.remediation) -m str "captcha" }14 http-request lua.crowdsec_handle if { var(txn.crowdsec.remediation) -m str "ban" }1516 http-after-response set-header Set-Cookie %[var(txn.crowdsec.captcha_cookie)] if { var(txn.crowdsec.captcha_status) -m found } { var(txn.crowdsec.captcha_cookie) -m found }17 http-after-response set-header Set-Cookie %[var(txn.crowdsec.captcha_cookie)] if { var(txn.crowdsec.captcha_cookie) -m found } !{ var(txn.crowdsec.captcha_status) -m found }1819 default_backend app
Use a dedicated SPOE section (crowdsec.cfg) to declare the messages HAProxy sends and which request variables are exported. The provided sample uses:
crowdsec-tcp (event on-client-session) for early, connection-level IP decisionscrowdsec-http-body / crowdsec-http-no-body (sent via SPOE groups with the same names) for per-request HTTP inspection, with conditional body forwardingImportant: captcha validation needs the request body (form-encoded POST). Ensure your frontend sends captcha submissions via the crowdsec-http-body group (see http-request send-spoe-group ... crowdsec-http-body in the examples).
For complete, working examples (including optional request-body forwarding, captcha redirects, and cookie management), see config/haproxy.cfg and config/crowdsec.cfg.
pprof in non-production environments to inspect CPU, heap, or goroutines via standard Go tooling.log_level: trace to watch BART operations and confirm that lists/ranges are loaded as expected.Everything you need for local development is included in the repository:
1git clone https://github.com/crowdsecurity/cs-haproxy-spoa-bouncer.git2cd cs-haproxy-spoa-bouncer3make build # builds the binary in ./crowdsec-spoa-bouncer4make test # runs Go tests
Docker Compose files under docker/ and docker-compose*.yaml spin up HAProxy, the bouncer, and a CrowdSec LAPI for integration testing.
Contributions are welcome—feel free to open an issue or PR:
git checkout -b feature/my-change).make test (and any relevant integration checks) before submitting.MIT – see LICENSE for the full text.