Skip to content

Commit 60de9e9

Browse files
authored
update some details to smooth the user experience (#176)
* add new example configuration for enabling challenge * fix http mode * fix duplicate inherited option * the file is really without challenge
1 parent 71d1c70 commit 60de9e9

3 files changed

Lines changed: 31 additions & 64 deletions

File tree

‎config/crowdsec-spoa-bouncer.yaml.challenge‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ insecure_skip_verify: false
1717
# Configure TCP and/or Unix socket listeners
1818
listen_tcp: 0.0.0.0:9000
1919
listen_unix: /run/crowdsec-spoa/spoa.sock
20-
challenge_http_listen: 127.0.0.1:9100
20+
challenge_http_listen: 127.0.0.1:9100
21+
2122
# Caps how many pending AppSec challenge responses can be held in memory at once,
2223
#challenge_cache_max_entries: 1000
2324
#asn_database_path: /var/lib/crowdsec/data/GeoLite2-ASN.mmdb

‎config/haproxy-nochallenge.cfg‎

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -16,104 +16,70 @@ defaults
1616
timeout queue 15s
1717
timeout tunnel 4h # for websocket
1818

19-
frontend test
20-
mode http
21-
bind *:9090
22-
19+
frontend http-in
20+
bind *:80
21+
option http-buffer-request
22+
2323
unique-id-format %[uuid()]
2424
unique-id-header X-Unique-ID
2525
filter spoe engine crowdsec config /etc/haproxy/crowdsec.cfg
26-
acl crowdsec_challenge_backend_path path_beg /crowdsec-challenge/ /crowdsec-internal/challenge/
27-
28-
# /crowdsec-internal/challenge/* bypasses the normal SPOE crowdsec-tcp/http
29-
# checks below (it's how the bouncer relays challenge follow-up asset/proof
30-
# requests to AppSec without re-triggering a fresh challenge decision), so it
31-
# gets no dataset-based IP check from CrowdSec at that layer. Rate-limit it
32-
# here independently, in addition to the IP/dataset ban check the bouncer
33-
# itself now performs in handleInternalChallengeHTTP.
34-
stick-table type ip size 100k expire 10s store http_req_rate(10s)
35-
http-request track-sc0 src if crowdsec_challenge_backend_path
36-
http-request deny status 429 if crowdsec_challenge_backend_path { sc_http_req_rate(0) gt 20 }
37-
38-
# ACL for body size limit (100000 bytes = ~100KB) - adjust here to change limit globally
39-
# Note spop protocol has limitations on the size of the message, so altering this value will not ensure the whole
40-
# body is sent for processing but should be enough to prevent overwhelming the SPOA bouncer.
41-
acl body_within_limit req.body_size -m int le 51200 # 50KB - stay safely under SPOE frame limit
42-
43-
# HTML-capable client detection. Clients without an Accept header are treated as HTML-capable.
44-
# '-m sub text/html' covers the common case; '*/*' covers wildcard acceptors.
45-
# html_rejected excludes clients that include text/html but mark it as not acceptable (q=0).
26+
27+
acl crowdsec_challenge_backend_path path_beg /crowdsec-internal/challenge/
28+
acl is_challenge var(txn.crowdsec.remediation) -m str "challenge"
29+
30+
# Body size limit: stay safely under SPOE frame limit (~50 KB)
31+
acl body_within_limit req.body_size -m int le 51200
32+
33+
# HTML-capable client detection
4634
acl render_html req.hdr_cnt(Accept) eq 0
4735
acl render_html req.hdr(Accept) -m sub text/html
4836
acl render_html req.hdr(Accept) -m sub */*
49-
# Single-quoted (not double-quoted): HAProxy expands $VAR references inside double-quoted
50-
# strings, and this regex's own trailing "$" (end-of-line anchor) would otherwise be
51-
# misparsed as the start of an environment variable reference.
52-
acl html_rejected req.hdr(Accept) -m reg -i 'text/html[[:space:]]*;[[:space:]]*q=0(\.0+)?([,[:space:]]|$)'
37+
acl html_rejected req.hdr(Accept) -m reg "text/html;q=0"
5338

5439
acl has_contact_url var(txn.crowdsec.contact_us_url) -m found
5540
acl empty_contact_url var(txn.crowdsec.contact_us_url) -m str ""
5641

57-
## If you don't want to render any content, you can use the following line
58-
# tcp-request content reject if !{ var(txn.crowdsec.remediation) -m str "allow" }
59-
60-
## Drop ban requests before http handler is called
61-
# tcp-request content reject if { var(txn.crowdsec.remediation) -m str "ban" }
62-
63-
# Send HTTP group conditionally based on body size
64-
# TCP handler already checked IP (triggered on client session), HTTP handler will use that remediation
65-
# Send group with body when body size <= limit (or no body present)
66-
http-request send-spoe-group crowdsec crowdsec-http-body if !crowdsec_challenge_backend_path body_within_limit || !crowdsec_challenge_backend_path !{ req.body_size -m found }
67-
# Send group without body when body exists and size > limit
68-
http-request send-spoe-group crowdsec crowdsec-http-no-body if !crowdsec_challenge_backend_path !body_within_limit { req.body_size -m found }
42+
http-request send-spoe-group crowdsec crowdsec-http-body if body_within_limit || !{ req.body_size -m found }
43+
http-request send-spoe-group crowdsec crowdsec-http-no-body if !body_within_limit { req.body_size -m found }
6944

70-
## Set a custom header on the request for upstream services to use
7145
http-request set-header X-Crowdsec-Remediation %[var(txn.crowdsec.remediation)] if { var(txn.crowdsec.remediation) -m found }
72-
## Set a custom header on the request for upstream services to use
7346
http-request set-header X-Crowdsec-IsoCode %[var(txn.crowdsec.isocode)] if { var(txn.crowdsec.isocode) -m found }
7447

75-
## Handle 302 redirect for successful captcha validation (redirect to current request URL)
48+
## Redirect after successful captcha validation
7649
http-request redirect code 302 location %[url] if { var(txn.crowdsec.remediation) -m str "allow" } { var(txn.crowdsec.redirect) -m found }
77-
acl is_challenge var(txn.crowdsec.remediation) -m str "challenge"
78-
http-request set-path %[var(txn.crowdsec.challenge_url)] if is_challenge { var(txn.crowdsec.challenge_url) -m found }
7950

80-
## Render ban and captcha remediations with native HAProxy templates.
81-
## Template paths are fixed at /var/lib/crowdsec-haproxy-spoa-bouncer/html/; edit the lf-file
82-
## directives below if you need custom locations (the old CROWDSEC_*_TEMPLATE_PATH env vars
83-
## are not read by this non-Lua config).
51+
## Serve remediation pages with native HAProxy lf-file directives (no Lua needed)
8452
http-request return status 200 content-type "text/html; charset=utf-8" hdr Cache-Control "no-cache, no-store" lf-file /var/lib/crowdsec-haproxy-spoa-bouncer/html/captcha.html if { var(txn.crowdsec.remediation) -m str "captcha" } render_html !html_rejected
8553
http-request return status 403 content-type "text/html; charset=utf-8" hdr Cache-Control "no-cache, no-store" lf-file /var/lib/crowdsec-haproxy-spoa-bouncer/html/ban-with-contact.html if { var(txn.crowdsec.remediation) -m str "ban" } render_html !html_rejected has_contact_url !empty_contact_url
8654
http-request return status 403 content-type "text/html; charset=utf-8" hdr Cache-Control "no-cache, no-store" lf-file /var/lib/crowdsec-haproxy-spoa-bouncer/html/ban.html if { var(txn.crowdsec.remediation) -m str "ban" } render_html !html_rejected !has_contact_url
8755
http-request return status 403 content-type "text/html; charset=utf-8" hdr Cache-Control "no-cache, no-store" lf-file /var/lib/crowdsec-haproxy-spoa-bouncer/html/ban.html if { var(txn.crowdsec.remediation) -m str "ban" } render_html !html_rejected empty_contact_url
88-
## Non-HTML clients get plain-text responses; captcha returns 200 to preserve the old
89-
## Lua handler's status code (captcha is a challenge, not a hard rejection).
56+
## Plain-text fallback for non-HTML clients
9057
http-request return status 200 content-type text/plain hdr Cache-Control "no-cache, no-store" string "Captcha required\n" if { var(txn.crowdsec.remediation) -m str "captcha" } !render_html
9158
http-request return status 200 content-type text/plain hdr Cache-Control "no-cache, no-store" string "Captcha required\n" if { var(txn.crowdsec.remediation) -m str "captcha" } html_rejected
9259
http-request return status 403 content-type text/plain hdr Cache-Control "no-cache, no-store" string "Forbidden\n" if { var(txn.crowdsec.remediation) -m str "ban" } !render_html
9360
http-request return status 403 content-type text/plain hdr Cache-Control "no-cache, no-store" string "Forbidden\n" if { var(txn.crowdsec.remediation) -m str "ban" } html_rejected
9461

95-
## Handle captcha cookie management via HAProxy (new approach)
96-
## Set captcha cookie when SPOA provides captcha_status (pending or valid)
62+
## Captcha cookie management
9763
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 }
98-
## Clear captcha cookie when cookie exists but no captcha_status (Allow decision)
9964
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 }
10065

10166
# REQUIRED if you use challenge mode - do not remove the following line.
10267
http-request set-header X-Crowdsec-Real-Src %[src] if crowdsec_challenge_backend_path
103-
68+
69+
http-request set-path %[var(txn.crowdsec.challenge_url)] if is_challenge { var(txn.crowdsec.challenge_url) -m found }
10470
use_backend crowdsec-challenge if is_challenge { var(txn.crowdsec.challenge_url) -m found }
10571
use_backend crowdsec-challenge if crowdsec_challenge_backend_path
106-
use_backend test_backend
72+
use_backend app
10773

108-
backend test_backend
109-
mode http
110-
server s1 whoami:2020
111-
11274
backend crowdsec-spoa
11375
mode tcp
11476
timeout connect 2s
11577
timeout server 60s
116-
server s2 spoa:9000
78+
server spoa 127.0.0.1:9000
79+
80+
backend app
81+
mode http
82+
http-request return status 200 content-type "text/html" string "<html><body><h1>Hello from HAProxy</h1></body></html>"
11783

11884
backend crowdsec-challenge
11985
mode http
@@ -123,4 +89,4 @@ backend crowdsec-challenge
12389
# Challenge responses are cached in the bouncer process that handled SPOE.
12490
# Keep this backend pinned to that same instance; do not load-balance it
12591
# independently unless challenge is shared.
126-
server s3 spoa:9100
92+
server s2 127.0.0.1:9100

‎config/haproxy.cfg‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ global
77

88
defaults
99
log global
10+
mode http
1011
option httplog
1112
option http-buffer-request
1213
timeout client 1m
@@ -18,7 +19,6 @@ defaults
1819

1920
frontend http-in
2021
bind *:80
21-
option http-buffer-request
2222

2323
unique-id-format %[uuid()]
2424
unique-id-header X-Unique-ID

0 commit comments

Comments
 (0)