From 535f009e59e479671eff1665c73e1e252362eb35 Mon Sep 17 00:00:00 2001 From: AlteredCoder <64792091+AlteredCoder@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:49:12 +0100 Subject: [PATCH] fix IP usage in csmod.Allow(ip) and repair captcha for bouncing (#53) --------- Co-authored-by: Laurence Jones --- lib/crowdsec.lua | 32 ++++++++++++++++---------------- lib/plugins/crowdsec/flag.lua | 19 +++++-------------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/lib/crowdsec.lua b/lib/crowdsec.lua index 1c4d683..1fb62f7 100644 --- a/lib/crowdsec.lua +++ b/lib/crowdsec.lua @@ -495,7 +495,7 @@ function csmod.allowIp(ip) end -function csmod.AppSecCheck() +function csmod.AppSecCheck(ip) local httpc = http.new() httpc:set_timeouts(runtime.conf["APPSEC_CONNECT_TIMEOUT"], runtime.conf["APPSEC_SEND_TIMEOUT"], runtime.conf["APPSEC_PROCESS_TIMEOUT"]) @@ -503,7 +503,7 @@ function csmod.AppSecCheck() local headers = ngx.req.get_headers() -- overwrite headers with crowdsec appsec require headers - headers["x-crowdsec-appsec-ip"] = ngx.var.remote_addr + headers["x-crowdsec-appsec-ip"] = ip headers["x-crowdsec-appsec-host"] = ngx.var.http_host headers["x-crowdsec-appsec-verb"] = ngx.var.request_method headers["x-crowdsec-appsec-uri"] = uri @@ -602,7 +602,7 @@ function csmod.Allow(ip) -- that user configured the remediation component to always check on the appSec (even if there is a decision for the IP) if ok == true or runtime.conf["ALWAYS_SEND_TO_APPSEC"] == true then if runtime.conf["APPSEC_ENABLED"] == true and ngx.var.no_appsec ~= "1" then - local appsecOk, appsecRemediation, err = csmod.AppSecCheck() + local appsecOk, appsecRemediation, err = csmod.AppSecCheck(ip) if err ~= nil then ngx.log(ngx.ERR, "AppSec check: " .. err) end @@ -630,13 +630,13 @@ function csmod.Allow(ip) if captcha_ok then -- if captcha can be use (configuration is valid) -- we check if the IP need to validate its captcha before checking it against crowdsec local API - local previous_uri, flags = ngx.shared.crowdsec_cache:get("captcha_"..ngx.var.remote_addr) + local previous_uri, flags = ngx.shared.crowdsec_cache:get("captcha_"..ip) local source, state_id, err = flag.GetFlags(flags) if previous_uri ~= nil and state_id == flag.VERIFY_STATE then ngx.req.read_body() local captcha_res = ngx.req.get_post_args()[csmod.GetCaptchaBackendKey()] or 0 if captcha_res ~= 0 then - local valid, err = csmod.validateCaptcha(captcha_res, ngx.var.remote_addr) + local valid, err = csmod.validateCaptcha(captcha_res, ip) if err ~= nil then ngx.log(ngx.ERR, "Error while validating captcha: " .. err) end @@ -646,11 +646,11 @@ function csmod.Allow(ip) -- we will not propose a captcha until the 'CAPTCHA_EXPIRATION'. -- But for the Application security component, we serve the captcha each time the user trigger it. if source == flag.APPSEC_SOURCE then - ngx.shared.crowdsec_cache:delete("captcha_"..ngx.var.remote_addr) + ngx.shared.crowdsec_cache:delete("captcha_"..ip) else - local succ, err, forcible = ngx.shared.crowdsec_cache:set("captcha_"..ngx.var.remote_addr, previous_uri, runtime.conf["CAPTCHA_EXPIRATION"], bit.bor(flag.VALIDATED_STATE, source) ) + local succ, err, forcible = ngx.shared.crowdsec_cache:set("captcha_"..ip, previous_uri, runtime.conf["CAPTCHA_EXPIRATION"], bit.bor(flag.VALIDATED_STATE, source) ) if not succ then - ngx.log(ngx.ERR, "failed to add key about captcha for ip '" .. ngx.var.remote_addr .. "' in cache: "..err) + ngx.log(ngx.ERR, "failed to add key about captcha for ip '" .. ip .. "' in cache: "..err) end if forcible then ngx.log(ngx.ERR, "Lua shared dict (crowdsec cache) is full, please increase dict size in config") @@ -661,23 +661,23 @@ function csmod.Allow(ip) ngx.redirect(previous_uri) return else - ngx.log(ngx.ALERT, "Invalid captcha from " .. ngx.var.remote_addr) + ngx.log(ngx.ALERT, "Invalid captcha from " .. ip) end end end end if not ok then if remediation == "ban" then - ngx.log(ngx.ALERT, "[Crowdsec] denied '" .. ngx.var.remote_addr .. "' with '"..remediation.."' (by " .. flag.Flags[remediationSource] .. ")") + ngx.log(ngx.ALERT, "[Crowdsec] denied '" .. ip .. "' with '"..remediation.."' (by " .. flag.Flags[remediationSource] .. ")") ban.apply() return end -- if the remediation is a captcha and captcha is well configured if remediation == "captcha" and captcha_ok and ngx.var.uri ~= "/favicon.ico" then - local previous_uri, flags = ngx.shared.crowdsec_cache:get("captcha_"..ngx.var.remote_addr) - source, state_id, err = flag.GetFlags(flags) + local previous_uri, flags = ngx.shared.crowdsec_cache:get("captcha_"..ip) + local source, state_id, err = flag.GetFlags(flags) -- we check if the IP is already in cache for captcha and not yet validated - if previous_uri == nil or remediationSource == flag.APPSEC_SOURCE then + if previous_uri == nil or state_id ~= flag.VALIDATED_STATE or remediationSource == flag.APPSEC_SOURCE then ngx.header.content_type = "text/html" ngx.header.cache_control = "no-cache" ngx.say(csmod.GetCaptchaTemplate()) @@ -691,14 +691,14 @@ function csmod.Allow(ip) end end end - local succ, err, forcible = ngx.shared.crowdsec_cache:set("captcha_"..ngx.var.remote_addr, uri , 60, bit.bor(flag.VERIFY_STATE, remediationSource)) + local succ, err, forcible = ngx.shared.crowdsec_cache:set("captcha_"..ip, uri , 60, bit.bor(flag.VERIFY_STATE, remediationSource)) if not succ then - ngx.log(ngx.ERR, "failed to add key about captcha for ip '" .. ngx.var.remote_addr .. "' in cache: "..err) + ngx.log(ngx.ERR, "failed to add key about captcha for ip '" .. ip .. "' in cache: "..err) end if forcible then ngx.log(ngx.ERR, "Lua shared dict (crowdsec cache) is full, please increase dict size in config") end - ngx.log(ngx.ALERT, "[Crowdsec] denied '" .. ngx.var.remote_addr .. "' with '"..remediation.."'") + ngx.log(ngx.ALERT, "[Crowdsec] denied '" .. ip .. "' with '"..remediation.."'") end end end diff --git a/lib/plugins/crowdsec/flag.lua b/lib/plugins/crowdsec/flag.lua index e271084..63ce8c9 100644 --- a/lib/plugins/crowdsec/flag.lua +++ b/lib/plugins/crowdsec/flag.lua @@ -25,28 +25,19 @@ function M.GetFlags(flags) return source, state, err end - if bit.band(flags, M.BOUNCER_SOURCE) then + if bit.band(flags, M.BOUNCER_SOURCE) ~= 0 then source = M.BOUNCER_SOURCE - elseif bit.band(flags, M.APPSEC_SOURCE) then + elseif bit.band(flags, M.APPSEC_SOURCE) ~= 0 then source = M.APPSEC_SOURCE end - if bit.band(flags, M.VERIFY_STATE) then + if bit.band(flags, M.VERIFY_STATE) ~= 0 then state = M.VERIFY_STATE - elseif bit.band(flags, M.VALIDATED_STATE) then + elseif bit.band(flags, M.VALIDATED_STATE) ~= 0 then state = M.VALIDATED_STATE end return source, state, err end -function M.GetStateID(state) - for k, v in pairs(M.State) do - if v == state then - return tonumber(k) - end - end - return nil -end - -return M \ No newline at end of file +return M