Skip to content

Commit

Permalink
bw - init work on reason data and fix nil REDIS_SENTINEL_HOSTS for se…
Browse files Browse the repository at this point in the history
…ssions
  • Loading branch information
fl0ppy-d1sk committed Jan 4, 2024
1 parent 68b3d67 commit e108d3f
Show file tree
Hide file tree
Showing 16 changed files with 221 additions and 104 deletions.
4 changes: 2 additions & 2 deletions src/bw/lua/bunkerweb/plugin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ function plugin:get_id()
end

-- luacheck: ignore 212
function plugin:ret(ret, msg, status, redirect)
return { ret = ret, msg = msg, status = status, redirect = redirect }
function plugin:ret(ret, msg, status, redirect, data)
return { ret = ret, msg = msg, status = status, redirect = redirect, data = data }
end

return plugin
34 changes: 28 additions & 6 deletions src/bw/lua/bunkerweb/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -290,15 +290,24 @@ end
utils.get_reason = function(ctx)
-- ngx.ctx
if ctx and ctx.bw and ctx.bw.reason then
return ctx.bw.reason
return ctx.bw.reason, ctx.bw.reason_data or {}
end
-- ngx.var
if var.reason and var.reason ~= "" then
return var.reason
local var_reason = var.reason
if var_reason and var_reason ~= "" then
local reason_data = {}
local var_reason_data = var.reason_data
if var_reason_data and reason_data ~= "" then
local ok, data = pcall(decode, var_reason_data)
if ok then
reason_data = data
end
end
return var_reason, reason_data
end
-- os.getenv
if os.getenv("REASON") == "modsecurity" then
return "modsecurity"
return "modsecurity", {}
end
-- datastore ban
local ip
Expand All @@ -309,15 +318,28 @@ utils.get_reason = function(ctx)
end
local banned, _ = datastore:get("bans_ip_" .. ip)
if banned then
return banned
return banned, {}
end
-- unknown
if ngx.status == utils.get_deny_status() then
return "unknown"
return "unknown", {}
end
return nil
end

utils.set_reason = function(reason, reason_data, ctx)
if ctx and ctx.bw then
ctx.bw.reason = reason or "unknown"
ctx.bw.reason_data = reason_data or {}
end
if var.reason then
var.reason = reason
if var.reason_data then
var.reason_data = encode(reason_data or {})
end
end
end

utils.is_whitelisted = function(ctx)
-- ngx.ctx
if ctx and ctx.bw and ctx.bw.is_whitelisted then
Expand Down
5 changes: 3 additions & 2 deletions src/common/confs/server-http/access-lua.conf
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ access_by_lua_block {
local call_plugin = helpers.call_plugin
local is_whitelisted = utils.is_whitelisted
local is_banned = utils.is_banned
local set_reason = utils.set_reason
local get_deny_status = utils.get_deny_status
local tostring = tostring

Expand Down Expand Up @@ -56,7 +57,7 @@ access_by_lua_block {
logger:log(ERR, "can't check if IP " .. ctx.bw.remote_addr .. " is banned : " .. reason)
elseif banned then
ctx.bw.is_banned = true
ctx.bw.reason = reason
set_reason(reason, {}, ctx)
save_ctx(ctx)
logger:log(WARN,
"IP " .. ctx.bw.remote_addr .. " is banned with reason " .. reason .. " (" .. tostring(ttl) .. "s remaining)")
Expand Down Expand Up @@ -102,7 +103,7 @@ access_by_lua_block {
end
if ret.status then
if ret.status == get_deny_status() then
ctx.bw.reason = plugin_id
set_reason(plugin_id, ret.data, ctx)
logger:log(WARN, "denied access from " .. plugin_id .. " : " .. ret.msg)
else
logger:log(NOTICE, plugin_id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
Expand Down
6 changes: 4 additions & 2 deletions src/common/confs/server-http/log-lua.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ log_by_lua_block {
local helpers = require "bunkerweb.helpers"
local cdatastore = require "bunkerweb.datastore"
local utils = require "bunkerweb.utils"
local cjson = require "cjson"

local ngx = ngx
local ERR = ngx.ERR
Expand All @@ -13,6 +14,7 @@ log_by_lua_block {
local new_plugin = helpers.new_plugin
local call_plugin = helpers.call_plugin
local tostring = tostring
local encode = cjson.encode

-- Start log phase
local logger = clogger:new("LOG")
Expand Down Expand Up @@ -72,9 +74,9 @@ log_by_lua_block {
logger:log(INFO, "called log() methods of plugins")

-- Display reason at info level
local reason = get_reason(ctx)
local reason, reason_data = get_reason(ctx)
if reason then
logger:log(INFO, "client was denied with reason : " .. reason)
logger:log(INFO, "client was denied with reason " .. reason .. " and data = " .. encode(reason_data))
end

logger:log(INFO, "log phase ended")
Expand Down
1 change: 1 addition & 0 deletions src/common/confs/server-http/server.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ server {

# variables
set $reason '';
set $reason_data = '';
set $ctx_ref '';

# include LUA files
Expand Down
58 changes: 35 additions & 23 deletions src/common/confs/server-stream/log-stream-lua.conf
Original file line number Diff line number Diff line change
@@ -1,71 +1,83 @@
log_by_lua_block {
local class = require "middleclass"
local clogger = require "bunkerweb.logger"
local helpers = require "bunkerweb.helpers"
local cdatastore = require "bunkerweb.datastore"
local utils = require "bunkerweb.utils"
local cjson = require "cjson"

local ngx = ngx
local ERR = ngx.ERR
local INFO = ngx.INFO
local fill_ctx = helpers.fill_ctx
local get_reason = utils.get_reason
local require_plugin = helpers.require_plugin
local new_plugin = helpers.new_plugin
local call_plugin = helpers.call_plugin
local tostring = tostring
local encode = cjson.encode

-- Start log phase
local logger = clogger:new("LOG")
local datastore = cdatastore:new()
logger:log(ngx.INFO, "log phase started")
logger:log(INFO, "log phase started")

-- Fill ctx
logger:log(ngx.INFO, "filling ngx.ctx ...")
local ok, ret, errors, ctx = helpers.fill_ctx()
logger:log(INFO, "filling ngx.ctx ...")
local ok, ret, errors, ctx = fill_ctx()
if not ok then
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
logger:log(ERR, "fill_ctx() failed : " .. ret)
elseif errors then
for i, error in ipairs(errors) do
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
end
end
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")

-- Get plugins order
local order, err = datastore:get("plugins_order", true)
if not order then
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
logger:log(ERR, "can't get plugins order from datastore : " .. err)
return
end

-- Call log_stream() methods
logger:log(ngx.INFO, "calling log_stream() methods of plugins ...")
logger:log(INFO, "calling log_stream() methods of plugins ...")
for i, plugin_id in ipairs(order.log_stream) do
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin_id)
local plugin_lua, err = require_plugin(plugin_id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
logger:log(ERR, err)
elseif plugin_lua == nil then
logger:log(ngx.INFO, err)
logger:log(INFO, err)
else
-- Check if plugin has log_stream method
if plugin_lua.log_stream ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua, ctx)
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
if not ok then
logger:log(ngx.ERR, plugin_obj)
logger:log(ERR, plugin_obj)
else
local ok, ret = helpers.call_plugin(plugin_obj, "log_stream")
local ok, ret = call_plugin(plugin_obj, "log_stream")
if not ok then
logger:log(ngx.ERR, ret)
logger:log(ERR, ret)
elseif not ret.ret then
logger:log(ngx.ERR, plugin_id .. ":log_stream() call failed : " .. ret.msg)
logger:log(ERR, plugin_id .. ":log_stream() call failed : " .. ret.msg)
else
logger:log(ngx.INFO, plugin_id .. ":log_stream() call successful : " .. ret.msg)
logger:log(INFO, plugin_id .. ":log_stream() call successful : " .. ret.msg)
end
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method log_stream() is not defined")
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method log_stream() is not defined")
end
end
end
logger:log(ngx.INFO, "called log_stream() methods of plugins")
logger:log(INFO, "called log_stream() methods of plugins")

-- Display reason at info level
if ctx.bw.reason then
logger:log(ngx.INFO, "client was denied with reason : " .. ctx.bw.reason)
local reason, reason_data = get_reason(ctx)
if reason then
logger:log(INFO, "client was denied with reason " .. reason .. " and data = " .. encode(reason_data))
end

logger:log(ngx.INFO, "log phase ended")
logger:log(INFO, "log phase ended")
}
86 changes: 50 additions & 36 deletions src/common/confs/server-stream/preread-stream-lua.conf
Original file line number Diff line number Diff line change
@@ -1,105 +1,119 @@
preread_by_lua_block {
ngx.ctx
local class = require "middleclass"
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"

local ngx = ngx
local exit = ngx.exit
local ERR = ngx.ERR
local INFO = ngx.INFO
local WARN = ngx.WARN
local NOTICE = ngx.NOTICE
local fill_ctx = helpers.fill_ctx
local save_ctx = helpers.save_ctx
local require_plugin = helpers.require_plugin
local new_plugin = helpers.new_plugin
local call_plugin = helpers.call_plugin
local is_whitelisted = utils.is_whitelisted
local is_banned = utils.is_banned
local set_reason = utils.set_reason
local get_deny_status = utils.get_deny_status
local tostring = tostring

-- Start preread phase
local logger = clogger:new("PREREAD")
local datastore = cdatastore:new()
logger:log(ngx.INFO, "preread phase started")
logger:log(INFO, "preread phase started")

-- Fill ctx
logger:log(ngx.INFO, "filling ngx.ctx ...")
local ok, ret, errors, ctx = helpers.fill_ctx()
logger:log(INFO, "filling ngx.ctx ...")
local ok, ret, errors, ctx = fill_ctx()
if not ok then
logger:log(ngx.ERR, "fill_ctx() failed : " .. ret)
logger:log(ERR, "fill_ctx() failed : " .. ret)
elseif errors then
for i, error in ipairs(errors) do
logger:log(ngx.ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
logger:log(ERR, "fill_ctx() error " .. tostring(i) .. " : " .. error)
end
end
logger:log(ngx.INFO, "ngx.ctx filled (ret = " .. ret .. ")")
logger:log(INFO, "ngx.ctx filled (ret = " .. ret .. ")")

-- Process bans as soon as possible
if ctx.bw.is_whitelisted ~= "yes" then
local banned, reason, ttl = utils.is_banned(ctx.bw.remote_addr)
if not is_whitelisted(ctx) then
local banned, reason, ttl = is_banned(ctx.bw.remote_addr)
if banned == nil then
logger:log(ngx.ERR, "can't check if IP " .. ctx.bw.remote_addr .. " is banned : " .. reason)
logger:log(ERR, "can't check if IP " .. ctx.bw.remote_addr .. " is banned : " .. reason)
elseif banned then
ctx.bw.is_banned = true
helpers.save_ctx(ctx)
set_reason(reason, {}, ctx)
save_ctx(ctx)
logger:log(ngx.WARN,
"IP " .. ctx.bw.remote_addr .. " is banned with reason " .. reason .. " (" .. tostring(ttl) .. "s remaining)")
return ngx.exit(utils.get_deny_status(ctx))
return ngx.exit(get_deny_status())
else
logger:log(ngx.INFO, "IP " .. ctx.bw.remote_addr .. " is not banned")
logger:log(INFO, "IP " .. ctx.bw.remote_addr .. " is not banned")
end
end

-- Get plugins order
local order, err = datastore:get("plugins_order", true)
if not order then
logger:log(ngx.ERR, "can't get plugins order from datastore : " .. err)
logger:log(ERR, "can't get plugins order from datastore : " .. err)
return
end

-- Call preread() methods
logger:log(ngx.INFO, "calling preread() methods of plugins ...")
logger:log(INFO, "calling preread() methods of plugins ...")
local status = nil
for i, plugin_id in ipairs(order.preread) do
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin_id)
local plugin_lua, err = require_plugin(plugin_id)
if plugin_lua == false then
logger:log(ngx.ERR, err)
logger:log(ERR, err)
elseif plugin_lua == nil then
logger:log(ngx.INFO, err)
logger:log(INFO, err)
else
-- Check if plugin has preread method
if plugin_lua.preread ~= nil then
-- New call
local ok, plugin_obj = helpers.new_plugin(plugin_lua, ctx)
local ok, plugin_obj = new_plugin(plugin_lua, ctx)
if not ok then
logger:log(ngx.ERR, plugin_obj)
logger:log(ERR, plugin_obj)
else
local ok, ret = helpers.call_plugin(plugin_obj, "preread")
local ok, ret = call_plugin(plugin_obj, "preread")
if not ok then
logger:log(ngx.ERR, ret)
logger:log(ERR, ret)
elseif not ret.ret then
logger:log(ngx.ERR, plugin_id .. ":preread() call failed : " .. ret.msg)
logger:log(ERR, plugin_id .. ":preread() call failed : " .. ret.msg)
else
logger:log(ngx.INFO, plugin_id .. ":preread() call successful : " .. ret.msg)
logger:log(INFO, plugin_id .. ":preread() call successful : " .. ret.msg)
end
if ret.status then
if ret.status == utils.get_deny_status(ctx) then
ctx.bw.reason = plugin_id
logger:log(ngx.WARN, "denied preread from " .. plugin_id .. " : " .. ret.msg)
if ret.status == get_deny_status() then
set_reason(plugin_id, ret.data, ctx)
logger:log(WARN, "denied preread from " .. plugin_id .. " : " .. ret.msg)
else
logger:log(ngx.NOTICE, plugin_id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
logger:log(NOTICE, plugin_id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
end
status = ret.status
break
end
end
else
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method preread() is not defined")
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method preread() is not defined")
end
end
end
logger:log(ngx.INFO, "called preread() methods of plugins")
logger:log(INFO, "called preread() methods of plugins")

-- Save ctx
helpers.save_ctx(ctx)
save_ctx(ctx)

logger:log(ngx.INFO, "preread phase ended")
logger:log(INFO, "preread phase ended")

-- Return status if needed
if status then
return ngx.exit(status)
return exit(status)
end

return true
Expand Down
Loading

0 comments on commit e108d3f

Please sign in to comment.