C
CrowdSec2mo ago
Spratt

haproxy spoa bouncer

HI all, I am trying to use haproxy spoa bouncer but the issue I am seeing is that my webiste is behind cloudflare, I've managed to pass the real IPs to both haproxy logs and down the line to nginx but the bouncer is still only seeing cloudflare's IPs. I've tried a few things using ai unfortunately nothing really helped My crowdsec.cfg:
spoe-agent crowdsec-agent
messages crowdsec-ip crowdsec-http
option var-prefix crowdsec
option set-on-error error
timeout hello 100ms
timeout idle 30s
timeout processing 500ms
use-backend crowdsec-spoa
log global

## Minimal test - just pass src directly
spoe-message crowdsec-ip
args src-ip=src
event on-frontend-http-request

spoe-message crowdsec-http
args remediation=var(txn.crowdsec.remediation) host=hdr(Host) method=method path=path
event on-frontend-http-request
spoe-agent crowdsec-agent
messages crowdsec-ip crowdsec-http
option var-prefix crowdsec
option set-on-error error
timeout hello 100ms
timeout idle 30s
timeout processing 500ms
use-backend crowdsec-spoa
log global

## Minimal test - just pass src directly
spoe-message crowdsec-ip
args src-ip=src
event on-frontend-http-request

spoe-message crowdsec-http
args remediation=var(txn.crowdsec.remediation) host=hdr(Host) method=method path=path
event on-frontend-http-request
My haproxy.cfg frontend: ```frontend web_traffic_in mode http bind 10...:443 ssl crt /etc/ssl/ alpn h2,http/1.1 # ACL to identify Cloudflare traffic (needs to be tcp-request compatible) acl from_cf src -f /etc/haproxy/CF_ips.list
# FIRST: Replace source IP with real client IP http-request set-src req.hdr(CF-Connecting-IP) if from_cf
# Also set the header for other components http-request set-header CF-Connecting-IP %[var(sess.real_ip)] # Set source for logging http-request set-src var(sess.real_ip) # CrowdSec SPOE filter filter spoe engine crowdsec config /etc/haproxy/crowdsec.cfg http-request set-header X-CrowdSec-Remediation %[var(txn.crowdsec.remediation)] http-request lua.crowdsec_handle if { var(txn.crowdsec.remediation) -m found } # Add headers option forwardfor http-request add-header X-Forwarded-Proto https if { ssl_fc } # ACLs for routing acl **
hdr(host) -i **
# Use ACLs to route requests use_backend
17 Replies
CrowdSec
CrowdSec2mo ago
Important Information
This post has been marked as resolved. If this is a mistake please press the red button below or type /unresolve
© Created By WhyAydan for CrowdSec ❤️
Loz
Loz2mo ago
Yes, we figured having an upstream proxy will cause issue especially since our "out of box" uses on-tcp-request which wont happen at the right layer. So I guess even passing:
spoe-message crowdsec-ip
args src-ip=hdr(CF-Connecting-IP)
event on-frontend-http-request
spoe-message crowdsec-ip
args src-ip=hdr(CF-Connecting-IP)
event on-frontend-http-request
wont work? and thinking now it might not parse to the correct type
Spratt
SprattOP2mo ago
@iiamloz your original response before edited ended up returning this:
time="2025-10-09T12:56:24+01:00" level=debug msg="received header bytes: datalength: 30 | verb: get, | module: hosts | submodule: "
time="2025-10-09T12:56:24+01:00" level=debug msg="data: [webtest.mainlinemenswear.co.uk]"
time="2025-10-09T12:56:24+01:00" level=debug msg="calling command get:hosts with data: [mysite.co.uk] and permissions 0"
time="2025-10-09T12:56:24+01:00" level=debug msg="command get:hosts returned &{Host: Captcha:{Provider: SecretKey: SiteKey: FallbackRemediation: CookieGenerator:{SignCookies:<nil> Secure: HTTPOnly:<nil> Secret: Name: logger:<nil>} Sessions:{sessions:map[] SessionIdleTimeout: SessionMaxTime: SessionGarbageSeconds:0 parsedIdleTimeout:0 parsedMaxTime:0 logger:<nil> mu:{w:{_:{} mu:{state:0 sema:0}} writerSem:0 readerSem:0 readerCount:{_:{} v:0} readerWait:{_:{} v:0}}} logger:<nil> client:<nil> Cancel:<nil>} Ban:{ContactUsURL: logger:<nil>} AppSec:{AlwaysSend:false logger:<nil>} LogLevel:<nil> logger:<nil>}"
time="2025-10-09T12:56:24+01:00" level=debug msg="received header bytes: datalength: 30 | verb: get, | module: hosts | submodule: "
time="2025-10-09T12:56:24+01:00" level=debug msg="data: [webtest.mainlinemenswear.co.uk]"
time="2025-10-09T12:56:24+01:00" level=debug msg="calling command get:hosts with data: [mysite.co.uk] and permissions 0"
time="2025-10-09T12:56:24+01:00" level=debug msg="command get:hosts returned &{Host: Captcha:{Provider: SecretKey: SiteKey: FallbackRemediation: CookieGenerator:{SignCookies:<nil> Secure: HTTPOnly:<nil> Secret: Name: logger:<nil>} Sessions:{sessions:map[] SessionIdleTimeout: SessionMaxTime: SessionGarbageSeconds:0 parsedIdleTimeout:0 parsedMaxTime:0 logger:<nil> mu:{w:{_:{} mu:{state:0 sema:0}} writerSem:0 readerSem:0 readerCount:{_:{} v:0} readerWait:{_:{} v:0}}} logger:<nil> client:<nil> Cancel:<nil>} Ban:{ContactUsURL: logger:<nil>} AppSec:{AlwaysSend:false logger:<nil>} LogLevel:<nil> logger:<nil>}"
the edited reponse crashes haproxy on restart :
2025-10-09T12:57:25.447706+01:00 lb-2 haproxy[449211]: [ALERT] (449211) : config : parsing [/etc/haproxy/crowdsec.cfg:15] : error detected while parsing an 'event on-frontend-http-request' condition : unknown fetch method 'from_cf' in ACL expression 'from_cf'.
2025-10-09T12:57:25.447706+01:00 lb-2 haproxy[449211]: [ALERT] (449211) : config : parsing [/etc/haproxy/crowdsec.cfg:15] : error detected while parsing an 'event on-frontend-http-request' condition : unknown fetch method 'from_cf' in ACL expression 'from_cf'.
Loz
Loz2mo ago
ahh okay, didnt know if the ACL you defined was global or not you can remove the if case the "working" logs dont seem to show what ip it is checking?
Spratt
SprattOP2mo ago
WIth the crowdsec.cfg I provided the logs show this
No description
Spratt
SprattOP2mo ago
if I try your suggestion without the if case it doesn't return any IP logs yes
Loz
Loz2mo ago
😕
Spratt
SprattOP2mo ago
Just to confirm that I checked again:
No description
Loz
Loz2mo ago
Okay @Spratt try
spoe-message crowdsec-ip
args src-ip=req.hdr_ip(CF-Connecting-IP)
event on-frontend-http-request
spoe-message crowdsec-ip
args src-ip=req.hdr_ip(CF-Connecting-IP)
event on-frontend-http-request
Spratt
SprattOP2mo ago
It works
Loz
Loz2mo ago
but you may want to move or reuse the acl adding a
event on-frontend-http-request if { src -f /etc/haproxy/CF_ips.list }
event on-frontend-http-request if { src -f /etc/haproxy/CF_ips.list }
to prevent spoofing if not firewalled
Spratt
SprattOP2mo ago
good Idea. Thanks a lot for helping
CrowdSec
CrowdSec2mo ago
Resolving haproxy spoa bouncer This has now been resolved. If you think this is a mistake please run /unresolve
Loz
Loz2mo ago
GitHub
Add example configuration for HAProxy behind upstream proxy by Laur...
fix: #13 This adds example configurations demonstrating how to extract real client IP when HAProxy is deployed behind an upstream proxy (nginx, Cloudflare, etc.). Key changes: docker-compose.proxy...
Loz
Loz2mo ago
however, I still see issues @Spratt we may have to split it to two message crowdsec-tcp and crowdsec-http and need to do the ip check in crowdsec-http
Spratt
SprattOP2mo ago
I see how that could be an issue
Loz
Loz2mo ago
going to create a pr now, that means you can drop the crowdsec-ip message and just do crowdsec-http as long as you pass the src-ip variable it will do the check. Okay created a seperate PR and update the examples to be based on the new code once merged 😅

Did you find this page helpful?