mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
antibot inherit from plugin
This commit is contained in:
parent
840c295684
commit
29c57915cb
4 changed files with 67 additions and 131 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue