Skip to content

v0.3.0

Latest

Choose a tag to compare

@LaurenceJJones LaurenceJJones released this 19 Jan 11:31
· 4 commits to main since this release
39bfe92

Breaking Changes

As outlined in the 0.1.0 release, every minor version before 1.0.0 may contain breaking changes.
Please review the sections below carefully before upgrading to avoid service interruptions.

1) SPOE execution now uses spoe-groups (explicit send-spoe-group)

What changed

SPOE execution is now driven by HAProxy spoe-groups and explicit calls to:

  • http-request send-spoe-group crowdsec <group-name>

This replaces relying on SPOE “event hooks” in the SPOE configuration.

Why this changed

This gives operators full control over when SPOE messages are sent, and allows you to set/override the source IP before the request is evaluated by the SPOA.

Who is impacted

All users upgrading to v0.3.0.

Required migration

  1. Update your HAProxy config to explicitly call the relevant SPOE group(s) in your frontend(s).

  2. Standardize on the upstream crowdsec.cfg and stop relying on custom SPOE config logic (for example forcing req.hdr_ip() usage).

Example

frontend test
    mode http
    bind *:9090

    filter spoe engine crowdsec config /etc/haproxy/crowdsec.cfg

    # Ensure HAProxy sees the correct client IP before calling the SPOE group
    http-request set-src hdr_ip(X-Real-IP) if { req.hdr(X-Real-IP) -m found }

    # Explicitly trigger the SPOE group at the right point in the request lifecycle
    http-request send-spoe-group crowdsec crowdsec-http-body if body_within_limit || !{ req.body_size -m found }

Operational note

You now control SPOE evaluation placement explicitly. If you previously relied on event hooks, ensure you add send-spoe-group to every relevant frontend and at the appropriate stage(s).


2) Captcha now requires a persistent signing_key (minimum 32 bytes)

What changed

Captcha moved away from purely server-side in-memory session state and now relies on signed tokens (JWT). As a result, a signing_key is now required.

Why this changed

Previously, restarting the SPOA invalidated in-memory captcha state, forcing users to re-complete captcha regardless of TTLs. Signed tokens allow captcha state to remain verifiable across restarts.

Who is impacted

All users with captcha enabled.

Required migration

Add signing_key to your captcha configuration (minimum 32 bytes):

hosts:
  - host: "*.example.com"
    captcha:
      site_key: "123"
      secret_key: "456"
      provider: "hcaptcha"
      timeout: 10          # HTTP client timeout in seconds (default: 5)
      pending_ttl: "30m"   # TTL for pending captcha tokens (default: 30m)
      passed_ttl: "24h"    # TTL for passed captcha tokens (default: 24h)
      signing_key: "your-32-byte-minimum-secret-key-here"  # REQUIRED in 0.3.0
  - host: "*"
    captcha:
      fallback_remediation: allow

Change redirection logic to use %[url] instead of %[var(txn.crowdsec.redirect)]

http-request redirect code 302 location %[url] if { var(txn.crowdsec.remediation) -m str "allow" } { var(txn.crowdsec.redirect) -m found }

note we found an issue for some users where without defining the following settings, the validation requests were being rejected

defaults
    option http-buffer-request

Multi-SPOA / HA setups

If you run multiple SPOA instances serving the same domains, use the same signing_key across all deployments so tokens validate consistently.

Key generation

openssl rand -hex 32

New Features

AppSec (WAF evaluation via SPOA)

The SPOA bouncer can now forward requests to CrowdSec AppSec for WAF evaluation, enabling HAProxy to provide:

  • IP-based remediation (ban/captcha/allow)
  • Request filtering via AppSec rules (WAF)

Docs:

Enable AppSec forwarding in the SPOA bouncer

After following the quickstart, configure the SPOA bouncer with a global AppSec endpoint and optional per-host overrides:

# Global AppSec URL (optional)
api_key: 12345
appsec_url: http://127.0.0.1:7422

hosts:
  - host: "*"
    appsec:
      always_send: false  # Only validate if not already banned/captcha'd
      # url: http://custom-appsec:7422  # Optional per-host override
      # api_key: custom-key             # Optional per-host override

Behavior note

With always_send: false, AppSec evaluation is skipped when a request already has a higher-priority remediation outcome (for example, ban or captcha). This reduces unnecessary WAF calls and latency.


AppSec limitations and required HAProxy settings

Because HAProxy is optimized for high-throughput proxying, request body inspection through SPOE/SPOP has hard constraints.

Body access is limited by tune.bufsize

You have limited access to request bodies via tune.bufsize. The value can only go up to 65536 (64KB), due to an underlying library limitation:

global
    log stdout format raw local0
    tune.bufsize 65536  # 64KB - increased for WAF body inspection

Request buffering must be enabled

To allow body inspection/forwarding, enable request buffering in defaults or the relevant frontend:

defaults
    option http-buffer-request

Choosing the correct SPOE group

Two SPOE groups are provided to make performance vs inspection explicit:

  • crowdsec-http-body

    • Sends request body (when available/within limits)
    • Required if you use captcha (captcha validation requires body-capable handling)
  • crowdsec-http-no-body

    • Avoids body forwarding for lower overhead
    • Suitable for “phase 1” WAF checks and IP remediation when you do not need body inspection

Recommended layered approach

If you want full-depth inspection:

  • Use HAProxy SPOA for IP remediation and phase 1 WAF signals (often crowdsec-http-no-body)
  • Use a downstream component (for example, the Nginx remediation component) for full body inspection and deeper WAF enforcement

This keeps HAProxy fast while enabling deeper inspection where it is most effective.


What’s Changed