Skip to content

Commit c17be62

Browse files
feat: improve HAProxy buffer config and add debug tooling
- Add tune.bufsize 65536 to haproxy.cfg and haproxy-upstreamproxy.cfg for 64KB body support - Add stats socket for runtime debugging (/tmp/haproxy.sock) - Lower body_within_limit to 50KB to stay safely under SPOE frame limit - Remove redundant max-frame-size from crowdsec.cfg (tune.bufsize handles it) - Add docker-compose.dev.yaml with debug container (tcpdump, socat, curl, etc.) - Move per-message logging from Debug to Trace for quieter Debug mode - Add Debug log for AppSec validation results
1 parent 374fde2 commit c17be62

File tree

5 files changed

+78
-8
lines changed

5 files changed

+78
-8
lines changed

config/crowdsec.cfg

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ spoe-agent crowdsec-agent
99

1010
option var-prefix crowdsec
1111
option set-on-error error
12-
timeout hello 100ms
13-
timeout idle 30s
12+
timeout hello 200ms
13+
timeout idle 55s
1414
timeout processing 500ms
1515
use-backend crowdsec-spoa
1616
log global
@@ -22,12 +22,14 @@ spoe-message crowdsec-tcp
2222
event on-client-session
2323

2424
## HTTP message with body - used when body size is within limit for AppSec
25+
## Note: Host and captcha cookie are extracted from headers=req.hdrs, no need to send separately
2526
spoe-message crowdsec-http-body
26-
args remediation=var(txn.crowdsec.remediation) crowdsec_captcha_cookie=req.cook(crowdsec_captcha_cookie) id=unique-id host=hdr(Host) method=method path=path query=query version=req.ver headers=req.hdrs body=req.body url=url ssl=ssl_fc src-ip=src src-port=src_port
27+
args remediation=var(txn.crowdsec.remediation) id=unique-id method=method path=path query=query version=req.ver headers=req.hdrs body=req.body url=url ssl=ssl_fc src-ip=src src-port=src_port
2728

2829
## HTTP message without body - used when body is too large or not needed
30+
## Note: Host and captcha cookie are extracted from headers=req.hdrs, no need to send separately
2931
spoe-message crowdsec-http-no-body
30-
args remediation=var(txn.crowdsec.remediation) crowdsec_captcha_cookie=req.cook(crowdsec_captcha_cookie) id=unique-id host=hdr(Host) method=method path=path query=query version=req.ver headers=req.hdrs url=url ssl=ssl_fc src-ip=src src-port=src_port
32+
args remediation=var(txn.crowdsec.remediation) id=unique-id method=method path=path query=query version=req.ver headers=req.hdrs url=url ssl=ssl_fc src-ip=src src-port=src_port
3133

3234
## Group for HTTP message with body - used when body size is within limit for AppSec
3335
spoe-group crowdsec-http-body

config/haproxy-upstreamproxy.cfg

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
global
66
log stdout format raw local0
7+
tune.bufsize 65536 # 64KB - increased for WAF body inspection
8+
stats socket /tmp/haproxy.sock mode 660 level admin
9+
stats timeout 30s
710
lua-prepend-path /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/?.lua
811
lua-load /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/crowdsec.lua
912
setenv CROWDSEC_BAN_TEMPLATE_PATH /var/lib/crowdsec-haproxy-spoa-bouncer/html/ban.html
@@ -39,7 +42,7 @@ frontend test
3942
# ACL for body size limit (100000 bytes = ~100KB) - adjust here to change limit globally
4043
# Note spop protocol has limitations on the size of the message, so altering this value will not ensure the whole
4144
# body is sent for processing but should be enough to prevent overwhelming the SPOA bouncer.
42-
acl body_within_limit req.body_size -m int le 100000
45+
acl body_within_limit req.body_size -m int le 51200 # 50KB - stay safely under SPOE frame limit
4346

4447
# Debug headers to verify IP extraction
4548
http-request set-header X-Debug-Direct-IP %[src]

config/haproxy.cfg

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
global
33
log stdout format raw local0
44
tune.bufsize 65536 # 64KB - increased for WAF body inspection
5+
stats socket /tmp/haproxy.sock mode 660 level admin
6+
stats timeout 30s
57
lua-prepend-path /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/?.lua
68
lua-load /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/crowdsec.lua
79
setenv CROWDSEC_BAN_TEMPLATE_PATH /var/lib/crowdsec-haproxy-spoa-bouncer/html/ban.html
@@ -29,7 +31,7 @@ frontend test
2931
# ACL for body size limit (100000 bytes = ~100KB) - adjust here to change limit globally
3032
# Note spop protocol has limitations on the size of the message, so altering this value will not ensure the whole
3133
# body is sent for processing but should be enough to prevent overwhelming the SPOA bouncer.
32-
acl body_within_limit req.body_size -m int le 100000
34+
acl body_within_limit req.body_size -m int le 51200 # 50KB - stay safely under SPOE frame limit
3335

3436
## If you don't want to render any content, you can use the following line
3537
# tcp-request content reject if !{ var(txn.crowdsec.remediation) -m str "allow" }

docker-compose.dev.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Development/Debug overlay for docker-compose.yaml
2+
# Usage: podman compose -f docker-compose.yaml -f docker-compose.dev.yaml up -d
3+
#
4+
# This adds a debug container with network tools for troubleshooting
5+
6+
services:
7+
# Debug sidecar with network tools
8+
debug:
9+
image: alpine:latest
10+
container_name: debug
11+
command: >
12+
sh -c "apk add --no-cache tcpdump socat curl bind-tools netcat-openbsd strace && sleep infinity"
13+
networks:
14+
- crowdsec
15+
cap_add:
16+
- NET_RAW # Required for tcpdump
17+
- NET_ADMIN # Required for some network debugging
18+
volumes:
19+
# Mount shared sockets volume
20+
- sockets:/run:ro
21+
# Mount HAProxy tmp for stats socket access
22+
- haproxy-tmp:/haproxy-tmp:ro
23+
# Mount configs for inspection
24+
- ./config:/config:ro
25+
depends_on:
26+
- haproxy
27+
- spoa
28+
29+
# Override HAProxy to share /tmp via named volume
30+
haproxy:
31+
volumes:
32+
- haproxy-tmp:/tmp
33+
34+
volumes:
35+
haproxy-tmp:
36+
37+
# Example debug commands:
38+
#
39+
# Enter debug container:
40+
# podman compose -f docker-compose.yaml -f docker-compose.dev.yaml exec debug ash
41+
#
42+
# Install tools (once inside):
43+
# apk add --no-cache tcpdump socat curl bind-tools netcat-openbsd strace
44+
#
45+
# Capture SPOE traffic:
46+
# tcpdump -i any -X port 9000
47+
#
48+
# Test HAProxy stats socket:
49+
# echo "show info" | socat /haproxy-tmp/haproxy.sock stdio
50+
#
51+
# DNS debugging:
52+
# dig crowdsec
53+
# nslookup spoa
54+
#
55+
# Test connectivity:
56+
# curl -v http://haproxy:8080/
57+
# nc -zv spoa 9000
58+

pkg/spoa/root.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ func New(config *SpoaConfig) (*Spoa, error) {
162162
func (s *Spoa) HandleSPOE(ctx context.Context, writer *encoding.ActionWriter, message *encoding.Message) {
163163
messageNameBytes := message.NameBytes()
164164

165-
s.logger.Debugf("Received message: %s", messageNameBytes)
165+
s.logger.Tracef("Received message: %s", messageNameBytes)
166166

167167
switch {
168168
case bytes.Equal(messageNameBytes, messageCrowdsecTCP): //TCP message type always runs so match it first
@@ -174,7 +174,7 @@ func (s *Spoa) HandleSPOE(ctx context.Context, writer *encoding.ActionWriter, me
174174
s.handleHTTPRequest(ctx, writer, message)
175175
default:
176176
// Unknown message type
177-
s.logger.Debugf("Unknown message type: %s", messageNameBytes)
177+
s.logger.Tracef("Unknown message type: %s", messageNameBytes)
178178
}
179179
}
180180

@@ -431,6 +431,9 @@ func (s *Spoa) handleHTTPRequest(ctx context.Context, writer *encoding.ActionWri
431431
httpMessageDataPool.Put(msgData)
432432
}()
433433

434+
//log body length here
435+
s.logger.Tracef("body length: %d", len(msgData.BodyCopied))
436+
434437
var tcpRemediation remediation.Remediation
435438

436439
// Get remediation passed from crowdsec-tcp handler (if any)
@@ -621,6 +624,8 @@ func (s *Spoa) validateWithAppSec(
621624
return currentRemediation
622625
}
623626

627+
logger.WithField("remediation", appSecRemediation.String()).Debug("AppSec validation result")
628+
624629
// Track AppSec block metrics
625630
if appSecRemediation > remediation.Allow && appSecReq.RemoteIP != "" {
626631
if ipAddr, parseErr := netip.ParseAddr(appSecReq.RemoteIP); parseErr == nil {

0 commit comments

Comments
 (0)