antibot inherit from plugin

This commit is contained in:
florian 2023-04-12 09:35:16 +02:00
parent 840c295684
commit 29c57915cb
No known key found for this signature in database
GPG key ID: 3D80806F12602A7C
4 changed files with 67 additions and 131 deletions

View file

@ -1,5 +1,5 @@
local mlcache = require "resty.mlcache"
local clogger = require "bunkerweb.logger"
local logger = require "bunkerweb.logger"
local class = require "middleclass"
local cachestore = class("cachestore")
@ -35,7 +35,7 @@ local cache, err = mlcache.new(
ipc_shm = ipc_shm
}
)
local logger = clogger:new("CACHESTORE")
logger:new("CACHESTORE")
if not store then
logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
end

View file

@ -11,8 +11,8 @@ function plugin:get_id()
return self.id
end
function plugin:ret(ret, msg, status)
return {ret = ret, msg = msg, status = status}
function plugin:ret(ret, msg, status, redirect)
return {ret = ret, msg = msg, status = status, redirect = redirect}
end
return plugin

View file

@ -56,10 +56,13 @@ for i, plugin in ipairs(plugins) do
ngx.ctx.reason = plugin.id
logger:log(ngx.WARN, "denied access from " .. plugin.id .. " : " .. err)
else
logger:log(ngx.NOTICE, plugin.id .. " returned status " .. tostring(value) .. " : " .. err)
logger:log(ngx.NOTICE, plugin.id .. " returned status " .. tostring(ret.status) .. " : " .. err)
end
ngx.ctx.status = ret.status
break
elseif ret.redirect then
logger:log(ngx.NOTICE, plugin.id .. " redirect to " .. ret.redirect .. " : " .. err)
ngx.ctx.redirect = ret.redirect
end
else
logger:log(ngx.ERR, plugin.id .. ":access() call failed : " .. ret.msg)
@ -79,96 +82,14 @@ logger:log(ngx.INFO, "access phase ended")
-- Return status if needed
if ngx.ctx.status then
return ngx.exit(ret.status)
return ngx.exit(ngx.ctx.status)
end
-- Redirect if needed
if ngx.ctx.redirect then
return ngx.redirect(ngx.ctx.redirect)
end
return true
local logger = require "logger"
local datastore = require "datastore"
local plugins = require "plugins"
local utils = require "utils"
local redisutils = require "redisutils"
-- Don't process internal requests
if ngx.req.is_internal() then
logger.log(ngx.INFO, "ACCESS", "Skipped access phase because request is internal")
return
end
logger.log(ngx.INFO, "ACCESS", "Access phase started")
-- Process bans as soon as possible
local banned = nil
-- Redis case
local use_redis = utils.get_variable("USE_REDIS")
if use_redis == "yes" then
local redis_banned, reason = redisutils.ban(ngx.var.remote_addr)
if redis_banned == nil then
logger.log(ngx.ERR, "ACCESS", "Error while checking ban from redis, falling back to local : " .. reason)
elseif not redis_banned then
banned = false
else
banned = reason
end
end
-- Local case
if banned == nil then
local reason, err = datastore:get("bans_ip_" .. ngx.var.remote_addr)
if reason then
banned = reason
else
banned = false
end
end
-- Deny request
if banned then
logger.log(ngx.WARN, "ACCESS", "IP " .. ngx.var.remote_addr .. " is banned with reason : " .. banned)
ngx.exit(utils.get_deny_status())
end
-- List all plugins
local list, err = plugins:list()
if not list then
logger.log(ngx.ERR, "ACCESS", "Can't list loaded plugins : " .. err)
list = {}
end
-- Call access method of plugins
for i, plugin in ipairs(list) do
local ret, plugin_lua = pcall(require, plugin.id .. "/" .. plugin.id)
if ret then
local plugin_obj = plugin_lua.new()
if plugin_obj.access ~= nil then
logger.log(ngx.INFO, "ACCESS", "Executing access() of " .. plugin.id)
local ok, err, ret, value = plugin_obj:access()
if not ok then
logger.log(ngx.ERR, "ACCESS", "Error while calling access() on plugin " .. plugin.id .. " : " .. err)
else
logger.log(ngx.INFO, "ACCESS", "Return value from " .. plugin.id .. ".access() is : " .. err)
end
if ret then
if type(value) == "number" then
if value == utils.get_deny_status() then
logger.log(ngx.WARN, "ACCESS", "Denied access from " .. plugin.id .. " : " .. err)
ngx.var.reason = plugin.id
else
logger.log(ngx.NOTICE, "ACCESS", plugin.id .. " returned status " .. tostring(value) .. " : " .. err)
end
return ngx.exit(value)
else
return value
end
end
else
logger.log(ngx.INFO, "ACCESS", "access() method not found in " .. plugin.id .. ", skipped execution")
end
end
end
logger.log(ngx.INFO, "ACCESS", "Access phase ended")
}

View file

@ -1,9 +1,8 @@
local _M = {}
_M.__index = _M
local utils = require "utils"
local datastore = require "datastore"
local logger = require "logger"
local class = require "middleclass"
local plugin = require "bunkerweb.plugin"
local utils = require "bunkerweb.utils"
local datastore = require "bunkerweb.datastore"
local logger = require "bunkerweb.logger"
local cjson = require "cjson"
local captcha = require "antibot.captcha"
local base64 = require "base64"
@ -11,103 +10,119 @@ local sha256 = require "resty.sha256"
local str = require "resty.string"
local http = require "resty.http"
function _M.new()
local self = setmetatable({}, _M)
return self, nil
local antibot = class("antibot", plugin)
function antibot:new()
plugin.new(self, "antibot")
end
function _M:init()
function antibot:init()
-- Check if init is needed
local init_needed, err = utils.has_not_variable("USE_ANTIBOT", "no")
if init_needed == nil then
return false, err
return self:ret(false, err)
end
if not init_needed then
return true, "no service uses Antibot, skipping init"
return self:ret(true, "no service uses Antibot, skipping init")
end
-- Load templates
local templates = {}
for i, template in ipairs({ "javascript", "captcha", "recaptcha", "hcaptcha" }) do
local f, err = io.open("/usr/share/bunkerweb/core/antibot/files/" .. template .. ".html")
if not f then
return false, "error while loading " .. template .. ".html : " .. err
return self:ret(false, "error while loading " .. template .. ".html : " .. err)
end
templates[template] = f:read("*all")
f:close()
end
local ok, err = datastore:set("plugin_antibot_templates", cjson.encode(templates))
if not ok then
return false, "can't save templates to datastore : " .. err
return self:ret(false, "can't save templates to datastore : " .. err)
end
return true, "success"
return self:ret(true, "success")
end
function _M:access()
function antibot:access()
-- Check if access is needed
local antibot, err = utils.get_variable("USE_ANTIBOT")
if antibot == nil then
return false, err, nil, nil
return self:ret(false, err)
end
if antibot == "no" then
return true, "Antibot not activated", nil, nil
return self:ret(true, "antibot not activated")
end
-- Get challenge URI
local challenge_uri, err = utils.get_variable("ANTIBOT_URI")
if not challenge_uri then
return false, "can't get Antibot URI from datastore : " .. err, nil, nil
return self:ret(false, "can't get antibot URI from datastore : " .. err)
end
-- Prepare challenge
local ok, err = self:prepare_challenge(antibot, challenge_uri)
if not ok then
return false, "can't prepare challenge : " .. err, true, ngx.HTTP_INTERNAL_SERVER_ERROR
return self:ret(false, "can't prepare challenge : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- Don't go further if client resolved the challenge
local resolved, err, original_uri = self:challenge_resolved(antibot)
if resolved == nil then
return false, "can't check if challenge is resolved : " .. err, nil, nil
return self:ret(false, "can't check if challenge is resolved : " .. err)
end
if resolved then
if ngx.var.uri == challenge_uri then
return true, "client already resolved the challenge", true, ngx.redirect(original_uri)
return self:ret(true, "client already resolved the challenge", nil, original_uri)
end
return true, "client already resolved the challenge", nil, nil
return self:ret(true, "client already resolved the challenge")
end
-- Redirect to challenge page
if ngx.var.uri ~= challenge_uri then
return true, "redirecting client to the challenge uri", true, ngx.redirect(challenge_uri)
return self:ret(true, "redirecting client to the challenge uri", nil, challenge_uri)
end
-- Display challenge
-- Display challenge needed
if ngx.var.request_method == "GET" then
local ok, err = self:display_challenge(antibot, challenge_uri)
if not ok then
return false, "display challenge error : " .. err, true, ngx.HTTP_INTERNAL_SERVER_ERROR
end
return true, "displaying challenge to client", true, ngx.HTTP_OK
ngx.ctx.antibot_display_content = true
return self:ret(true, "displaying challenge to client", ngx.HTTP_OK)
end
-- Check challenge
if ngx.var.request_method == "POST" then
local ok, err, redirect = self:check_challenge(antibot)
if ok == nil then
return false, "check challenge error : " .. err, true, ngx.HTTP_INTERNAL_SERVER_ERROR
return self:ret(false, "check challenge error : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
end
if redirect then
return true, "check challenge redirect : " .. redirect, true, ngx.redirect(redirect)
return self:ret(true, "check challenge redirect : " .. redirect, nil, redirect)
end
local ok, err = self:display_challenge(antibot)
if not ok then
return false, "display challenge error : " .. err, true, ngx.HTTP_INTERNAL_SERVER_ERROR
end
return true, "displaying challenge to client", true, ngx.HTTP_OK
ngx.ctx.antibot_display_content = true
return self:ret(true, "displaying challenge to client", ngx.HTTP_OK)
end
-- Method is suspicious, let's deny the request
return true, "unsupported HTTP method for Antibot", true, utils.get_deny_status()
return self:ret(true, "unsupported HTTP method for antibot", utils.get_deny_status())
end
function antibot:content()
-- Check if access is needed
local antibot, err = utils.get_variable("USE_ANTIBOT")
if antibot == nil then
return self:ret(false, err)
end
if antibot == "no" then
return self:ret(true, "antibot not activated")
end
-- Check if display content is needed
if not ngx.ctx.antibot_display_content then
return self:ret(true, "display content not needed")
end
-- Display content
local ok, err = self:display_challenge(antibot)
if not ok then
return self:ret(false, "display challenge error : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
end
end
function _M:challenge_resolved(antibot)