HAProxy
Bot protection for HAProxy 2.4+ using SPOE and Lua hooks.
Prerequisites
- Secret key from your dashboard
- HAProxy 2.4+ compiled with Lua 5.3 and SPOE support
Installation
Download centinel-haproxy.zip and extract the three files to your HAProxy configuration directory:
curl -O https://docs.centinelanalytica.com/downloads/centinel-haproxy.zip
unzip centinel-haproxy.zip -d /etc/haproxy/lua/
mv /etc/haproxy/lua/spoe-centinel.conf /etc/haproxy/The zip contains:
centinel-spoe.lua— Lua hooks for request routing, response headers, and block pagesjson.lua— JSON encoder/decoder used by the Lua hooksspoe-centinel.conf— SPOE filter configuration
global
lua-prepend-path /etc/haproxy/lua/?.lua
lua-load /etc/haproxy/lua/centinel-spoe.lua
frontend http-in
bind *:80
filter spoe engine centinel config /etc/haproxy/spoe-centinel.conf
http-request lua.centinel-request-hook
http-response lua.centinel-response-hook
use_backend centinel-block if { var(txn.centinel_status) -m str blocked }
default_backend servers
backend servers
server app1 127.0.0.1:8080 check
backend centinel-block
http-request use-service lua.centinel-block-service
backend spoe-centinel
mode tcp
server spoa validator.centinelanalytica.com:9000 ssl verify required ca-file /etc/ssl/certs/ca-certificates.crtFail-open by design
If the SPOA is unreachable or doesn't respond within timeout processing (500ms default), HAProxy lets the request through. A down SPOA never blocks traffic.
export CENTINEL_SECRET_KEY="sk_live_your_key_here"
haproxy -f /etc/haproxy/haproxy.cfg -sf $(cat /var/run/haproxy.pid)For systemd:
[Service]
Environment="CENTINEL_SECRET_KEY=sk_live_your_key_here"How it works
HAProxy's SPOE filter sends request metadata to the hosted SPOA over SPOP. The SPOA validates the request and returns a decision. Lua hooks then route the request:
- allow / not_matched — request goes to your backend. Response headers and cookies from the validator get applied on the way out.
- block / redirect — request hits the
centinel-block-serviceLua applet, which serves the block page or challenge HTML.
Static assets (images, fonts, CSS, JS, media files) are excluded by an ACL in spoe-centinel.conf and never reach the SPOA.
Advanced configuration
Timeouts are configured in spoe-centinel.conf:
spoe-agent centinel-agent
timeout hello 500ms
timeout idle 30s
timeout processing 500ms| Timeout | Default | Description |
|---|---|---|
timeout processing | 500ms | Max wait for the SPOA response. If exceeded, request is allowed (fail-open). Increase if you see frequent timeouts. |
timeout hello | 500ms | TLS + SPOP handshake timeout. Only matters on first connection — subsequent requests reuse the persistent connection. |
timeout idle | 30s | Idle connection timeout before HAProxy drops the SPOA connection. |
Static files are excluded by an ACL in spoe-centinel.conf line 3. These requests skip the SPOE pipeline entirely.
Default exclusions: .avi, .avif, .bmp, .css, .eot, .flac, .flv, .gif, .gz, .ico, .jpeg, .jpg, .js, .less, .map, .mka, .mkv, .mov, .mp3, .mp4, .mpeg, .mpg, .ogg, .ogm, .opus, .otf, .png, .svg, .svgz, .swf, .ttf, .wav, .webm, .webp, .woff, .woff2.
Edit the path_reg ACL in spoe-centinel.conf to add or remove extensions.
To skip bot protection on specific paths, add ACLs in your haproxy.cfg frontend before the SPOE filter:
frontend http-in
bind *:80
acl no_centinel path_beg /health /metrics /ready
acl no_centinel path_beg /internal/
filter spoe engine centinel config /etc/haproxy/spoe-centinel.conf
http-request lua.centinel-request-hook unless no_centinel
http-response lua.centinel-response-hook unless no_centinel
use_backend centinel-block if { var(txn.centinel_status) -m str blocked }
default_backend serversRequests matching no_centinel bypass the Lua hooks. The SPOE filter still runs but with no matching event it does nothing.
Configuration reference
Environment variables
| Variable | Description |
|---|---|
CENTINEL_SECRET_KEY | API key from the dashboard. Required. |
HAProxy config directives
| Directive | Purpose |
|---|---|
lua-prepend-path | Path to Lua module directory |
lua-load | Loads the Centinel Lua hooks at startup |
filter spoe | Activates the SPOE filter with spoe-centinel.conf |
http-request lua.centinel-request-hook | Routes requests based on SPOA decision |
http-response lua.centinel-response-hook | Applies response headers/cookies on allow |
use_backend centinel-block | Routes blocked requests to the block service |
backend spoe-centinel | TLS+TCP backend pointing to the hosted SPOA |
Changelog
- 1.1.0 — TLS for SPOA connection, hardened block handling, trimmed static file ACL.
- 1.0.0 — Initial release.