update
This commit is contained in:
parent
fb423e01f6
commit
6c4ec23899
4 changed files with 71 additions and 26 deletions
|
@ -35,14 +35,15 @@ function config.loadConfig(file)
|
|||
return nil, "File ".. file .." doesn't exist"
|
||||
end
|
||||
local conf = {}
|
||||
local valid_params = {'API_URL', 'API_KEY', 'BOUNCING_ON_TYPE', 'MODE', 'SECRET_KEY', 'SITE_KEY', 'CAPTCHA_TEMPLATE_PATH'}
|
||||
local valid_int_params = {'CACHE_EXPIRATION', 'CACHE_SIZE', 'REQUEST_TIMEOUT', 'UPDATE_FREQUENCY'}
|
||||
local valid_params = {'API_URL', 'API_KEY', 'BOUNCING_ON_TYPE', 'MODE', 'SECRET_KEY', 'SITE_KEY', 'CAPTCHA_TEMPLATE_PATH', 'REDIRECT_PATH', 'RET_CODE', 'EXCLUDE_LOCATION'}
|
||||
local valid_int_params = {'CACHE_EXPIRATION', 'CACHE_SIZE', 'REQUEST_TIMEOUT', 'UPDATE_FREQUENCY', 'CAPTCHA_EXPIRATION'}
|
||||
local valid_bouncing_on_type_values = {'ban', 'captcha', 'all'}
|
||||
local default_values = {
|
||||
['REQUEST_TIMEOUT'] = 0.2,
|
||||
['BOUNCING_ON_TYPE'] = "ban",
|
||||
['MODE'] = "stream",
|
||||
['UPDATE_FREQUENCY'] = 10
|
||||
['CAPTCHA_EXPIRATION'] = 3600
|
||||
}
|
||||
for line in io.lines(file) do
|
||||
local isOk = false
|
||||
|
@ -66,6 +67,14 @@ function config.loadConfig(file)
|
|||
ngx.log(ngx.ERR, "unsupported value '" .. s[2] .. "' for variable '" .. v .. "'. Using default value 'stream' instead")
|
||||
break
|
||||
end
|
||||
if v == "EXCLUDE_LOCATION" then
|
||||
local value = s[2]
|
||||
exclude_location = {}
|
||||
for match in (value..","):gmatch("(.-)"..",") do
|
||||
table.insert(exclude_location, match)
|
||||
end
|
||||
conf[v] = exclude_location
|
||||
break
|
||||
end
|
||||
local n = next(s, k)
|
||||
conf[v] = s[n]
|
||||
|
|
|
@ -28,8 +28,15 @@ function csmod.init(configFile, userAgent)
|
|||
runtime.conf = conf
|
||||
runtime.userAgent = userAgent
|
||||
runtime.cache = ngx.shared.crowdsec_cache
|
||||
runtime.captcha_ok = true
|
||||
|
||||
err = recaptcha.New(runtime.conf["SITE_KEY"], runtime.conf["SECRET_KEY"], runtime.conf["CAPTCHA_TEMPLATE_PATH"])
|
||||
|
||||
if err ~= nil then
|
||||
ngx.log(ngx.ERR, "using reCaptcha is not possible: " .. err)
|
||||
runtime.captcha_ok = false
|
||||
end
|
||||
|
||||
-- if stream mode, add callback to stream_query and start timer
|
||||
if runtime.conf["MODE"] == "stream" then
|
||||
runtime.cache:set("startup", true)
|
||||
|
@ -308,25 +315,30 @@ end
|
|||
|
||||
|
||||
function csmod.Allow(ip)
|
||||
previous_uri, state_id = ngx.shared.crowdsec_cache:get("captcha_"..ngx.var.remote_addr)
|
||||
if previous_uri ~= nil and state_id == recaptcha.GetStateID(recaptcha._VERIFY_STATE) then
|
||||
ngx.req.read_body()
|
||||
local recaptcha_res = ngx.req.get_post_args()["g-recaptcha-response"] or 0
|
||||
if recaptcha_res ~= 0 then
|
||||
valid, err = cs.validateCaptcha(recaptcha_res, ngx.var.remote_addr)
|
||||
if err ~= nil then
|
||||
ngx.log(ngx.ERR, "Error while validating captcha: " .. err)
|
||||
end
|
||||
if valid == true then
|
||||
ngx.shared.crowdsec_cache:set("captcha_"..ngx.var.remote_addr, "/" , 10, recaptcha.GetStateID(recaptcha._VALIDATED_STATE))
|
||||
ngx.req.set_method(ngx.HTTP_GET)
|
||||
ngx.redirect(previous_uri)
|
||||
return
|
||||
else
|
||||
ngx.log(ngx.ALERT, "Invalid captcha from " .. ngx.var.remote_addr)
|
||||
end
|
||||
end
|
||||
if runtime.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
|
||||
previous_uri, state_id = ngx.shared.crowdsec_cache:get("captcha_"..ngx.var.remote_addr)
|
||||
if previous_uri ~= nil and state_id == recaptcha.GetStateID(recaptcha._VERIFY_STATE) then
|
||||
ngx.req.read_body()
|
||||
local recaptcha_res = ngx.req.get_post_args()["g-recaptcha-response"] or 0
|
||||
if recaptcha_res ~= 0 then
|
||||
valid, err = cs.validateCaptcha(recaptcha_res, ngx.var.remote_addr)
|
||||
if err ~= nil then
|
||||
ngx.log(ngx.ERR, "Error while validating captcha: " .. err)
|
||||
end
|
||||
if valid == true then
|
||||
-- captcha is valid, we redirect the IP to its previous URI but in GET method
|
||||
ngx.shared.crowdsec_cache:set("captcha_"..ngx.var.remote_addr, "/" , 10, recaptcha.GetStateID(recaptcha._VALIDATED_STATE))
|
||||
ngx.req.set_method(ngx.HTTP_GET)
|
||||
ngx.redirect(previous_uri)
|
||||
return
|
||||
else
|
||||
ngx.log(ngx.ALERT, "Invalid captcha from " .. ngx.var.remote_addr)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local ok, remediation, err = csmod.allowIp(ip)
|
||||
if err ~= nil then
|
||||
ngx.log(ngx.ERR, "[Crowdsec] bouncer error: " .. err)
|
||||
|
@ -336,12 +348,16 @@ function csmod.Allow(ip)
|
|||
if remediation == "ban" then
|
||||
ngx.exit(ngx.HTTP_FORBIDDEN)
|
||||
end
|
||||
if remediation == "captcha" then
|
||||
|
||||
-- if the remediation is a captcha and captcha is well configured
|
||||
if remediation == "captcha" and runtime.captcha_ok then
|
||||
previous_uri, state_id = ngx.shared.crowdsec_cache:get("captcha_"..ngx.var.remote_addr)
|
||||
-- we check if the IP is already in cache for captcha and not yet validated
|
||||
if previous_uri == nil or state_id ~= recaptcha.GetStateID(recaptcha._VALIDATED_STATE) then
|
||||
ngx.header.content_type = "text/html"
|
||||
ngx.say(cs.GetCaptchaTemplate())
|
||||
local uri = ngx.var.uri
|
||||
-- in case its not a GET request, we prefer to fallback on referer
|
||||
if ngx.req.get_method() ~= "GET" then
|
||||
headers, err = ngx.req.get_headers()
|
||||
for k, v in pairs(headers) do
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local template = require "resty.template.safe"
|
||||
local template = require "template"
|
||||
local http = require "resty.http"
|
||||
local cjson = require "cjson"
|
||||
|
||||
|
@ -37,10 +37,28 @@ local function read_file(path)
|
|||
return content
|
||||
end
|
||||
|
||||
local function file_exist(path)
|
||||
local f = io.open(path, "r")
|
||||
if f ~= nil then io.close(f) return true else return false
|
||||
end
|
||||
|
||||
function M.New(siteKey, secretKey, TemplateFilePath)
|
||||
M.SecretKey = secretKey
|
||||
|
||||
if siteKey == nil or siteKey == "" then
|
||||
return "no recaptcha site key provided, can't use recaptcha"
|
||||
end
|
||||
M.SiteKey = siteKey
|
||||
|
||||
if secretKey == nil or secretKey == "" then
|
||||
return "no recaptcha secret key provided, can't use recaptcha"
|
||||
end
|
||||
|
||||
M.SecretKey = secretKey
|
||||
|
||||
if file_exist(TemplateFilePath) == false then
|
||||
return "captcha template file doesn't exist, can't use recaptcha"
|
||||
end
|
||||
|
||||
local captcha_template = read_file(TemplateFilePath)
|
||||
if captcha_template == nil then
|
||||
return "Template file " .. TemplateFilePath .. "not found."
|
||||
|
|
|
@ -4,13 +4,15 @@ 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=0.2
|
||||
REQUEST_TIMEOUT=200
|
||||
UPDATE_FREQUENCY=10
|
||||
MODE=stream
|
||||
EXCLUDE_LOCATION=/admin,/toto # exclude the bouncing on those location
|
||||
#those apply for "ban" action
|
||||
REDIRECT_PATH=/toto
|
||||
REDIRECT_PATH=/
|
||||
RET_CODE=403
|
||||
#those apply for "captcha" action
|
||||
SECRET_KEY=123
|
||||
SITE_KEY=123
|
||||
CAPTCHA_TEMPLATE_PATH=/usr/local/lua/crowdsec/captcha.js
|
||||
CAPTCHA_TEMPLATE_PATH=/usr/local/lua/crowdsec/captcha.js
|
||||
CAPTCHA_EXPIRATION=3600
|
||||
|
|
Loading…
Add table
Reference in a new issue