Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions lib/crowdsec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ local captcha = require "plugins.crowdsec.captcha"
local flag = require "plugins.crowdsec.flag"
local utils = require "plugins.crowdsec.utils"
local ban = require "plugins.crowdsec.ban"
local challenge = require "plugins.crowdsec.challenge"
local url = require "plugins.crowdsec.url"
local metrics = require "plugins.crowdsec.metrics"
local live = require "plugins.crowdsec.live"
Expand Down Expand Up @@ -637,13 +638,21 @@ function csmod.AppSecCheck(ip)
else
status_code = ngx.HTTP_FORBIDDEN
end
if remediation == "challenge" then
local appsec_response = {
body = response.user_body_content,
headers = response.user_headers,
cookies = response.user_cookies,
}
return ok, remediation, status_code, appsec_response, err
end
elseif res.status == 401 then
ngx.log(ngx.ERR, "Unauthenticated request to APPSEC")
else
ngx.log(ngx.ERR, "Bad request to APPSEC (" .. res.status .. "): " .. res.body)
end

return ok, remediation, status_code, err
return ok, remediation, status_code, nil, err

end

Expand All @@ -655,6 +664,7 @@ function csmod.Allow(ip)
local remediationSource = flag.BOUNCER_SOURCE
local ret_code = nil
local remediation = ""
local appsec_response = nil
local ok = true
local err = ""
if runtime.conf["ENABLED"] ~= "false" then
Expand Down Expand Up @@ -698,7 +708,7 @@ function csmod.Allow(ip)
-- OR
-- that user configured the remediation component to always check on the appSec (even if there is a decision for the IP)
if is_appsec_enabled() and (ok == true or is_always_send_to_appsec()) then
local appsecOk, appsecRemediation, status_code, err = csmod.AppSecCheck(ip)
local appsecOk, appsecRemediation, status_code, appsec_resp, err = csmod.AppSecCheck(ip)
if err ~= nil then
ngx.log(ngx.ERR, "AppSec check: " .. err)
end
Expand All @@ -707,6 +717,7 @@ function csmod.Allow(ip)
remediationSource = flag.APPSEC_SOURCE
remediation = appsecRemediation
ret_code = status_code
appsec_response = appsec_resp
end
end

Expand All @@ -719,7 +730,7 @@ function csmod.Allow(ip)
end

-- if remediation is not supported, fallback
if remediation ~= "captcha" and remediation ~= "ban" then
if remediation ~= "captcha" and remediation ~= "ban" and remediation ~= "challenge" then
remediation = runtime.fallback
end
end
Expand Down Expand Up @@ -784,6 +795,17 @@ function csmod.Allow(ip)
ban.apply(ret_code)
return
end
if remediation == "challenge" then
if appsec_response ~= nil then
ngx.log(ngx.DEBUG, "[Crowdsec] challenge '" .. ip .. "' (by " .. flag.Flags[remediationSource] .. ")")
challenge.apply(ret_code, appsec_response.body, appsec_response.headers, appsec_response.cookies)
return
else
ngx.log(ngx.ERR, "[Crowdsec] challenge remediation for '" .. ip .. "' but no response data, falling back to ban")
ban.apply(ret_code)
return
end
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_"..ip)
Expand Down
41 changes: 41 additions & 0 deletions lib/plugins/crowdsec/challenge.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
local M = {_TYPE='module', _NAME='challenge.funcs', _VERSION='1.0-0'}

--- Serve a challenge response from AppSec.
-- Sets the HTTP status, response headers, cookies, and body as provided by CrowdSec.
-- @param status_code number: HTTP status code (typically 200)
-- @param body string: the HTML body content to serve
-- @param headers table: map of header name -> list of values, e.g. {["Content-Type"] = {"text/html"}}
-- @param cookies table: list of Set-Cookie header value strings
function M.apply(status_code, body, headers, cookies)
ngx.status = status_code or ngx.HTTP_OK

if headers ~= nil then
for name, values in pairs(headers) do
if type(values) == "table" then
if #values == 1 then
ngx.header[name] = values[1]
else
ngx.header[name] = values
end
else
ngx.header[name] = values
end
end
end

if cookies ~= nil and #cookies > 0 then
if #cookies == 1 then
ngx.header["Set-Cookie"] = cookies[1]
else
ngx.header["Set-Cookie"] = cookies
end
end

if body ~= nil then
ngx.say(body)
end

ngx.exit(ngx.status)
end

return M
Loading