various redis fixes and display ready log

This commit is contained in:
florian 2023-04-24 13:21:28 +02:00
parent 17a3d933b3
commit edf7e06e07
No known key found for this signature in database
GPG Key ID: 3D80806F12602A7C
10 changed files with 162 additions and 22 deletions

3
TODO
View File

@ -1,4 +1,5 @@
- load inline values for white/black/grey list core
- bwcli with redis
- move bans to cachestore
- direct access to ANTIBOT_URI without prepare_challenge call
- don't fail if session err is not nil (new session will be created)
- limit refactoring with redis scripts if needed

View File

@ -69,13 +69,12 @@ function cachestore:get(key)
return {ret_get, ret_ttl}
]]
local ret, err = clusterstore:call("eval", redis_script, 1, key)
-- local cjson = require "cjson"
-- require "bunkerweb.logger":new("DEBUG"):log(ngx.ERR, cjson.encode(ret))
if not ret then
clusterstore:close()
return nil, err, nil
end
-- Extract values
clusterstore:close(redis)
clusterstore:close()
if ret[1] == ngx.null then
ret[1] = nil
end

View File

@ -30,7 +30,7 @@ function datastore:keys()
return self.dict:get_keys(0)
end
function datastore:exp(key)
function datastore:ttl(key)
local ttl, err = self.dict:ttl(key)
if not ttl then
return false, err

View File

@ -490,4 +490,96 @@ utils.get_session_var = function(key)
return false, "no session"
end
utils.is_banned = function(ip)
-- Check on local datastore
local reason, err = datastore:get("bans_ip_" .. ip)
if not reason and err ~= "not found" then
return nil, "datastore:get() error : " .. reason
elseif reason and err ~= "not found" then
local ok, ttl = datastore:ttl("bans_ip_" .. ip)
if not ok then
return true, reason, -1
end
return true, reason, ttl
end
-- Redis case
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return nil, "can't get USE_REDIS variable : " .. err
elseif use_redis ~= "yes" then
return false, "not banned"
end
-- Connect
local clusterstore = require "bunkerweb.clusterstore":new()
local ok, err = clusterstore:connect()
if not ok then
return nil, "can't connect to redis server : " .. err
end
-- Redis atomic script : GET+TTL
local redis_script = [[
local ret_get = redis.pcall("GET", KEYS[1])
if type(ret_get) == "table" and ret_get["err"] ~= nil then
redis.log(redis.LOG_WARNING, "access GET error : " .. ret_get["err"])
return ret_get
end
local ret_ttl = nil
if ret_get ~= nil then
ret_ttl = redis.pcall("TTL", KEYS[1])
if type(ret_ttl) == "table" and ret_ttl["err"] ~= nil then
redis.log(redis.LOG_WARNING, "access TTL error : " .. ret_ttl["err"])
return ret_ttl
end
end
return {ret_get, ret_ttl}
]]
-- Execute redis script
local data, err = clusterstore:call("eval", redis_script, 1, "bans_ip_" .. ip)
if not data then
clusterstore:close()
return nil, "redis call error : " .. err
elseif data.err then
clusterstore:close()
return nil, "redis script error : " .. data.err
elseif data[1] ~= ngx.null then
clusterstore:close()
-- Update local cache
local ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
if not ok then
return nil, "datastore:set() error : " .. err
end
return true, data[1], data[2]
end
clusterstore:close()
return false, "not banned"
end
utils.add_ban = function(ip, reason, ttl)
-- Set on local datastore
local ok, err = datastore:set("bans_ip_" .. ip, reason, ttl)
if not ok then
return false, "datastore:set() error : " .. err
end
-- Set on redis
local use_redis, err = utils.get_variable("USE_REDIS", false)
if not use_redis then
return nil, "can't get USE_REDIS variable : " .. err
elseif use_redis ~= "yes" then
return true, "success"
end
-- Connect
local clusterstore = require "bunkerweb.clusterstore":new()
local ok, err = clusterstore:connect()
if not ok then
return false, "can't connect to redis server : " .. err
end
-- SET call
local ok, err = clusterstore:call("set", "bans_ip_" .. ip, reason, "EX", ttl)
if not ok then
clusterstore:close()
return false, "redis SET failed : " .. err
end
clusterstore:close()
return true, "success"
end
return utils

View File

@ -53,6 +53,9 @@ lua_shared_dict cachestore_locks {{ CACHESTORE_LOCKS_MEMORY_SIZE }};
# LUA init block
include /etc/nginx/init-lua.conf;
# LUA init worker block
include /etc/nginx/init-worker-lua.conf;
# API server
{% if USE_API == "yes" %}include /etc/nginx/api.conf;{% endif +%}

View File

@ -0,0 +1,39 @@
lua_shared_dict ready_lock 16k;
init_worker_by_lua_block {
-- Our timer function
local ready_log = function(premature)
-- Instantiate objects
local logger = require "bunkerweb.logger":new("INIT")
local datastore = require "bunkerweb.datastore":new()
local lock = require "resty.lock":new("ready_lock")
if not lock then
logger:log(ngx.ERR, "lock:new() failed : " .. err)
return
end
-- Acquire lock
local elapsed, err = lock:lock("ready")
if elapsed == nil then
logger:log(ngx.ERR, "lock:lock() failed : " .. err)
else
-- Display ready log
local ok, err = datastore:get("misc_ready")
if not ok and err ~= "not found" then
logger:log(ngx.ERR, "datastore:get() failed : " .. err)
elseif not ok and err == "not found" then
logger:log(ngx.NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
local ok, err = datastore:set("misc_ready", "ok")
if not ok then
logger:log(ngx.ERR, "datastore:set() failed : " .. err)
end
end
end
-- Release lock
lock:unlock()
end
-- Start timer
ngx.timer.at(5, ready_log)
}

View File

@ -5,6 +5,7 @@ local clogger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local utils = require "bunkerweb.utils"
local cdatastore = require "bunkerweb.datastore"
local cclusterstore = require "bunkerweb.clusterstore"
local cjson = require "cjson"
-- Don't process internal requests
@ -31,12 +32,14 @@ end
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
-- Process bans as soon as possible
local reason, err = datastore:get("bans_ip_" .. ngx.ctx.bw.remote_addr)
if not reason and err ~= "not found" then
logger:log(ngx.ERR, "error while checking if client is banned : " .. reason)
elseif reason and err ~= "not found" then
logger:log(ngx.WARN, "IP " .. ngx.ctx.bw.remote_addr .. " is banned with reason : " .. reason)
local banned, reason, ttl = utils.is_banned(ngx.ctx.bw.remote_addr)
if banned == nil then
logger:log(ngx.ERR, "can't check if IP " .. ngx.ctx.bw.remote_addr .. " is banned : " .. reason)
elseif banned then
logger:log(ngx.WARN, "IP " .. ngx.ctx.bw.remote_addr .. " is banned with reason " .. reason .. " (" .. tostring(ttl) .. "s remaining)")
return ngx.exit(utils.get_deny_status())
else
logger:log(ngx.INFO, "IP " .. ngx.ctx.bw.remote_addr .. " is not banned")
end
-- Get plugins

View File

@ -53,7 +53,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
local counter = false
-- Redis case
if use_redis then
local redis_counter, err = bad_behavior.redis_increase(ip, count_time, ban_time)
local redis_counter, err = badbehavior.redis_increase(ip, count_time, ban_time)
if not redis_counter then
logger:log(ngx.ERR, "(increase) redis_increase failed, falling back to local : " .. err)
else
@ -84,9 +84,9 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
end
-- Store local ban
if counter > threshold then
local ok, err = datastore:set("bans_ip_" .. ip, "bad behavior", ban_time)
local ok, err = utils.add_ban(ip, "bad behavior", ban_time)
if not ok then
logger:log(ngx.ERR, "(increase) can't save ban to the datastore : " .. err)
logger:log(ngx.ERR, "(increase) can't save ban : " .. err)
return
end
logger:log(ngx.WARN, "IP " .. ip .. " is banned for " .. ban_time .. "s (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
@ -102,9 +102,9 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
local counter = false
-- Redis case
if use_redis then
local redis_counter, err = badbehavior.redis_decrease(ip)
local redis_counter, err = badbehavior.redis_decrease(ip, count_time)
if not redis_counter then
logger:log(ngx.ERR, "(increase) redis_increase failed, falling back to local : " .. err)
logger:log(ngx.ERR, "(decrease) redis_decrease failed, falling back to local : " .. err)
else
counter = redis_counter
end
@ -113,7 +113,7 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
if not counter then
local local_counter, err = datastore:get("plugin_badbehavior_count_" .. ip)
if not local_counter and err ~= "not found" then
logger:log(ngx.ERR, "(increase) can't get counts from the datastore : " .. err)
logger:log(ngx.ERR, "(decrease) can't get counts from the datastore : " .. err)
end
if local_counter == nil or local_counter <= 1 then
counter = 0
@ -123,11 +123,12 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
end
-- Store local counter
if counter <= 0 then
counter = 0
local ok, err = datastore:delete("plugin_badbehavior_count_" .. ip)
else
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
if not ok then
logger:log(ngx.ERR, "(increase) can't save counts to the datastore : " .. err)
logger:log(ngx.ERR, "(decrease) can't save counts to the datastore : " .. err)
return
end
end
@ -149,7 +150,7 @@ function badbehavior.redis_increase(ip, count_time, ban_time)
redis.log(redis.LOG_WARNING, "Bad behavior increase EXPIRE error : " .. ret_expire["err"])
return ret_expire
end
if ret_incr > ARGV[2] then
if ret_incr > tonumber(ARGV[2]) then
local ret_set = redis.pcall("SET", KEYS[2], "bad behavior", "EX", ARGV[2])
if type(ret_set) == "table" and ret_set["err"] ~= nil then
redis.log(redis.LOG_WARNING, "Bad behavior increase SET error : " .. ret_set["err"])

View File

@ -16,7 +16,7 @@ function redis:init()
if self.variables["USE_REDIS"] ~= "yes" or self.is_loading then
return self:ret(true, "init not needed")
end
-- Check redis connection
-- Check redis connection ()
local ok, err = clusterstore:connect()
if not ok then
return self:ret(false, "redis connect error : " .. err)

View File

@ -19,16 +19,18 @@ function sessions:init()
["USE_REDIS"] = "",
["REDIS_HOST"] = "",
["REDIS_PORT"] = "",
["REDIS_DATABASE"] = "",
["REDIS_SSL"] = "",
["REDIS_TIMEOUT"] = "",
["REDIS_KEEPALIVE_IDLE"] = "",
["REDIS_KEEPALIVE_POOL"] = ""
}
for k, v in pairs(redis_vars) do
local var, err = utils.get_variable(k, false)
if var == nil then
local value, err = utils.get_variable(k, false)
if value == nil then
return self:ret(false, "can't get " .. k .. " variable : " .. err)
end
redis_vars[k] = value
end
-- Init configuration
local config = {
@ -58,7 +60,7 @@ function sessions:init()
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
ssl = redis_vars["REDIS_SSL"] == "yes",
host = redis_vars["REDIS_HOST"],
port = tonumber(redis_vars["REDIS_HOST"]),
port = tonumber(redis_vars["REDIS_PORT"]),
database = tonumber(redis_vars["REDIS_DATABASE"])
}
end