Feat/cap captcha and override remediation - #152
Open
tommy-ng0 wants to merge 5 commits into
Open
Conversation
Adds CAPTCHA_PROVIDER=cap, a self-hosted proof-of-work captcha (https://capjs.js.org) that needs no third-party service. Cap is reCAPTCHA-shaped but not reCAPTCHA-compatible, so it gets its own verification path: a JSON POST to <endpoint>/<site_key>/siteverify, which reports failures as a plain "error" string rather than reCAPTCHA's "error-codes" array. Reusing the existing path would crash on pairs(nil). Because the instance is self-hosted, its URLs are configuration rather than constants. The browser and the bouncer can reach it on different addresses, so CAPTCHA_API_ENDPOINT is the public base the widget is pointed at, and the optional CAPTCHA_VERIFY_ENDPOINT lets verification stay on a private network. Both compose with SITE_KEY, which cap embeds in its paths. The widget is a custom element loaded as an ES module, not a script plus a div, so the template now receives the markup pre-rendered as captcha_frontend_js_tag and captcha_widget. The three previous variables are still populated so custom templates keep rendering. Naming the widget's hidden input via data-cap-hidden-field-name keeps GetCaptchaBackendKey() unchanged for every provider. An unknown CAPTCHA_PROVIDER is now rejected in New() instead of surfacing later as a nil concatenation while rendering.
…tion Serves one remediation for every bounced decision, whatever type the LAPI or the appsec component returned. Setting OVERRIDE_REMEDIATION=captcha challenges every blocked visitor rather than dropping them outright, which keeps false positives recoverable. Valid values are captcha and ban, mirroring FALLBACK_REMEDIATION; empty (the default) honours the remediation as received, so existing configs are unaffected. The override is applied before the fallback, so forcing captcha while the captcha provider is misconfigured still degrades to FALLBACK_REMEDIATION rather than serving nothing.
- pass remote_ip into ValidateCap and send it as X-Real-IP, so the cap instance sees the address that solved the challenge rather than the bouncer's own, which is all a server-to-server call would otherwise show - assert the forwarded header in the stub siteverify used by test 23
A self-hosted cap instance is commonly reached over TLS it terminates itself, and resty.http verifies by default, so a self-signed certificate failed verification with "18: self-signed certificate". - thread SSL_VERIFY from the config through captcha.New into the cap siteverify request - normalise the value inside New: it is called before crowdsec.lua turns SSL_VERIFY into a boolean, and the raw "false" string is truthy in Lua - leave the hosted providers verifying unconditionally, as they present publicly trusted certificates
ValidateCap returned true, meaning "solved", for any outcome short of a decoded JSON body. A transport error or an error page from a proxy in front of cap therefore let the visitor through and cached the result as VALIDATED_STATE for CAPTCHA_EXPIRATION, for a token nothing had checked. Cap is self-hosted and commonly rate limited per client IP, so a visitor can provoke that outcome for themselves: spend their own bucket, submit junk, and the 429 the edge returns is read as a solve. Every path now returns false, leaving them on the captcha page to try again. - fail closed on transport errors and on responses that are not JSON - reject non-200 responses, kept separate from the decode failure so an outage or a tripped rate limit stays distinguishable in the logs from a visitor submitting a bad token - add t/25, which stubs the 429 error page an nginx edge returns when limit_req trips, and asserts the captcha page is served again rather than the redirect that the previous behaviour produced The hosted providers are left as they are: their endpoints are not something a visitor can knock over to skip the captcha.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.