crowdsec icon indicating copy to clipboard operation
crowdsec copied to clipboard

"CSRF Verification Failed" error in the Cloudflare Turnstile Captcha Stage with Nginx Proxy Manager + Authentik

Open obadaahmar opened this issue 1 month ago • 2 comments

I've recentely implemented the captcha remediation phase in CrowdSec as per the latest available documentation from CrowdSec.

This is my current config:

name: captcha_remediation
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip" && Alert.GetScenario() contains "http"
decisions:
- type: captcha
  duration: 4h
notifications:
  - discord
  - report_abuse_ip_db
on_success: break
---
name: default_ip_remediation
#debug: true
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
 - type: ban
   duration: 72h
notifications:
  - discord
  - report_abuse_ip_db
#duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)
# notifications:
#   - slack_default  # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
#   - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
#   - http_default   # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
#   - email_default  # Set the required email parameters in /etc/crowdsec/notifications/email.yaml before enabling this.
on_success: break

I've also setup my Nginx Proxy Manager with the OpenResty bouncer setup as following:

ENABLED=true
API_URL=http://XXX.XXX.XXX.XXX:8086
API_KEY=[REDACTED]
CACHE_EXPIRATION=1
# bounce for all type of remediation that the bouncer can receive from the local API
BOUNCING_ON_TYPE=all
FALLBACK_REMEDIATION=ban
REQUEST_TIMEOUT=3000
UPDATE_FREQUENCY=10
# By default internal requests are ignored, such as any path affected by rewrite rule.
# set ENABLE_INTERNAL=true to allow checking on these internal requests.
ENABLE_INTERNAL=false
# live or stream
MODE=live
# exclude the bouncing on those location
EXCLUDE_LOCATION=
#those apply for "ban" action
# /!\ REDIRECT_LOCATION and RET_CODE can't be used together. REDIRECT_LOCATION take priority over RET_CODE
BAN_TEMPLATE_PATH=/config/crowdsec/templates/ban.html
REDIRECT_LOCATION=
RET_CODE=404
#those apply for "captcha" action
#valid providers are recaptcha, hcaptcha, turnstile
CAPTCHA_PROVIDER=turnstile
# Captcha Secret Key
SECRET_KEY=[REDACTED]
# Captcha Site key
SITE_KEY=[REDACTED]
CAPTCHA_TEMPLATE_PATH=/config/crowdsec/templates/captcha.html
CAPTCHA_EXPIRATION=3600

## Application Security Component Configuration
APPSEC_URL=http://XXX.XXX.XXX.XXX:7422
#### default ###
APPSEC_FAILURE_ACTION=passthrough  
APPSEC_CONNECT_TIMEOUT=100        
APPSEC_SEND_TIMEOUT=100            
APPSEC_PROCESS_TIMEOUT=1000        
ALWAYS_SEND_TO_APPSEC=false        
SSL_VERIFY=true
##############

Which has always worked for the ban type of remediation, however when the user is presented with the Captcha challenge and after it is successfully resolved, we are receiving this type of error: Image

I've enabled debug mode in the OpenResty bouncer and this was the type of logging I was receiving:

2025/12/07 16:11:39 [debug] 470#470: *154 [lua] crowdsec.lua:466: allowIp(): live mode
2025/12/07 16:11:39 [debug] 470#470: *154 [lua] http.lua:633: send_request(): 
GET /v1/decisions?ip=2001:8004:d01:b94f:1972:42b0:729f:19c4 HTTP/1.1
x-api-key: [REDACTED]
Connection: close
Host: XXX.XXX.XXX.XXX:8086
User-Agent: crowdsec-openresty-bouncer/v1.1.0


2025/12/07 16:11:39 [debug] 470#470: *154 [lua] live.lua:72: live_query(): [CACHE] Adding 'ipv6_[REDACTED_IPv6_ADDRESS]' in cache for '1' seconds with decision type'captcha'with origin'cscli
2025/12/07 16:11:39 [debug] 470#470: *154 [lua] crowdsec.lua:479: allowIp(): live_query: [REDACTED_IPv6_ADDRESS] | banned with | captcha | cscli | nil
2025/12/07 16:11:39 [debug] 470#470: *154 [lua] crowdsec.lua:291: get_body(): No content-length header in request
2025/12/07 16:11:39 [debug] 470#470: *154 [lua] http.lua:633: send_request(): 
GET / HTTP/1.1
cf-connecting-ip: [REDACTED_IPv6_ADDRESS]
cf-ipcountry: AU
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
accept-language: en-AU
accept-encoding: gzip, br
x-crowdsec-appsec-api-key: [REDACTED]
user-agent: Mozilla/5.0 (Android 16; Mobile; rv:145.0) Gecko/145.0 Firefox/145.0
x-forwarded-for: [REDACTED_IPv6_ADDRESS]
sec-fetch-mode: navigate
cookie: authentik_csrf=[REDACTED]; cf_clearance=[REDACTED]; authentik_session=[REDACTED]
sec-fetch-site: none
priority: u=0, i
sec-fetch-user: ?1
cf-ray: [REDACTED]
host: XXX.XXX.XXX.XXX:7422
x-crowdsec-appsec-ip: [REDACTED_IPv6_ADDRESS]
upgrade-insecure-requests: 1
x-crowdsec-appsec-host: auth.domain.tld
cf-visitor: {"scheme":"https"}
x-crowdsec-appsec-verb: GET
x-forwarded-proto: https
x-crowdsec-appsec-uri: /if/user/
sec-fetch-dest: document
x-crowdsec-appsec-user-agent: Mozilla/5.0 (Android 16; Mobile; rv:145.0) Gecko/145.0 Firefox/145.0
cdn-loop: cloudflare; loops=1

Here is the current Nginx Proxy Manager for this example subdomain (auth.domain.tld):

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;

Not sure where the issue is happening, is it from CrowdSec config, the OpenResty bouncer, or Authentik itself. Would appreciate any type of guidance.

obadaahmar avatar Dec 12 '25 02:12 obadaahmar

@obadaahmar: Thanks for opening an issue, it is currently awaiting triage.

In the meantime, you can:

  1. Check Crowdsec Documentation to see if your issue can be self resolved.
  2. You can also join our Discord.
  3. Check Releases to make sure your agent is on the latest version.
Details

I am a bot created to help the crowdsecurity developers manage community feedback and contributions. You can check out my manifest file to understand my behavior and what I can do. If you want to use this for your project, you can check out the BirthdayResearch/oss-governance-bot repository.

github-actions[bot] avatar Dec 12 '25 02:12 github-actions[bot]

@obadaahmar: There are no 'kind' label on this issue. You need a 'kind' label to start the triage process.

  • /kind feature
  • /kind enhancement
  • /kind refactoring
  • /kind bug
  • /kind packaging
Details

I am a bot created to help the crowdsecurity developers manage community feedback and contributions. You can check out my manifest file to understand my behavior and what I can do. If you want to use this for your project, you can check out the BirthdayResearch/oss-governance-bot repository.

github-actions[bot] avatar Dec 12 '25 02:12 github-actions[bot]

We have never seen this type of page before, but it points to a CSP error or maybe a cloudflare WAF rule that is blocking CSP violations. Depending on the captcha provider they work differently I dont know the full extent of turnstile code, but I would open your browser console and check for CSP violations or where this response is coming from as its not from our lua code.

edit: just to make it clear, this page does look like a cloudflare response so check cloudflare logs.

mdn csp

edit edit: quick searching around and seems to be common based on CORS here is some links

mdn cors stackoverflow thread for django

the django thread states the error was in cloudflare worker but :shrug: if it relevant for you.

another thread

LaurenceJJones avatar Dec 12 '25 07:12 LaurenceJJones

We have never seen this type of page before, but it points to a CSP error or maybe a cloudflare WAF rule that is blocking CSP violations. Depending on the captcha provider they work differently I dont know the full extent of turnstile code, but I would open your browser console and check for CSP violations or where this response is coming from as its not from our lua code.

edit: just to make it clear, this page does look like a cloudflare response so check cloudflare logs.

mdn csp

edit edit: quick searching around and seems to be common based on CORS here is some links

mdn cors stackoverflow thread for django

the django thread states the error was in cloudflare worker but 🤷 if it relevant for you.

another thread

Unfortunately none of these apply to my case, I have had zero issues with my setup with any of the other 30+ services that I host and have reverse proxied via Cloudflare + Nginx Proxy Manager. Even Authentik's Turnstile captcha stage works flawlessly.

Not sure if there is a specific header that would be expected in this use case therefore that's why I'm asking people who probably had the same issue while creating their setups.

obadaahmar avatar Dec 13 '25 01:12 obadaahmar