refactor of session management

This commit is contained in:
bunkerity 2023-04-19 18:09:06 +02:00
parent 0fdb108fe9
commit a5e08e1c67
9 changed files with 97 additions and 138 deletions

2
TODO
View file

@ -1,6 +1,4 @@
- utils refactoring
- load inline values for white/black/grey list core
- check if correct setting is set to yes in new() before loading stuff in self
- store object in ngx.ctx
- bwcli with redis
- move bans to cachestore

View file

@ -433,55 +433,59 @@ end
utils.get_session = function()
-- Session already in context
if ngx.ctx.bw.session then
return ngx.ctx.bw.session, ngx.ctx.bw.session_err, ngx.ctx.bw.session_exists
return ngx.ctx.bw.session, ngx.ctx.bw.session_err, ngx.ctx.bw.session_exists, ngx.ctx.bw.session_refreshed
end
-- Open session
local _session, err, exists = session.start()
if err then
-- Open session and fill ctx
local _session, err, exists, refreshed = session.start()
ngx.ctx.bw.session_err = nil
if err and err ~= "missing session cookie" and err ~= "no session" then
logger:log(ngx.ERR, "can't start session : " .. err)
ngx.ctx.bw.session_err = nil
end
-- Fill ctx
ngx.ctx.session = _session
ngx.ctx.session_err = err
ngx.ctx.session_exists = exists
ngx.ctx.session_saved = false
ngx.ctx.session_data = _session.get_data()
if not ngx.ctx.session_data then
ngx.ctx.session_data = {}
ngx.ctx.bw.session = _session
ngx.ctx.bw.session_exists = exists
ngx.ctx.bw.session_refreshed = refreshed
ngx.ctx.bw.session_saved = false
ngx.ctx.bw.session_data = _session:get_data()
if not ngx.ctx.bw.session_data then
ngx.ctx.bw.session_data = {}
end
return _session, err, exists
return _session, ngx.ctx.bw.session_err, exists, refreshed
end
utils.save_session = function()
-- Check if save is needed
if ngx.ctx.session and not ngx.ctx.session_err and not ngx.ctx.session_saved then
ngx.ctx.session:set_data(ngx.ctx.session_data)
local ok, err = ngx.ctx.session:save()
if ngx.ctx.bw.session and not ngx.ctx.bw.session_err and not ngx.ctx.bw.session_saved then
ngx.ctx.bw.session:set_data(ngx.ctx.bw.session_data)
local ok, err = ngx.ctx.bw.session:save()
if err then
logger:log(ngx.ERR, "can't save session : " .. err)
return false, "can't save session : " .. err
end
ngx.ctx.session_saved = true
ngx.ctx.bw.session_saved = true
return true, "session saved"
elseif ngx.ctx.session_saved then
elseif ngx.ctx.bw.session_saved then
return true, "session already saved"
end
return true, "no session"
end
utils.set_session = function(key, value)
utils.set_session_var = function(key, value)
-- Set new data
if ngx.ctx.session and not ngx.ctx.session_err then
ngx.ctx.session_data[key] = value
if ngx.ctx.bw.session and not ngx.ctx.bw.session_err then
ngx.ctx.bw.session_data[key] = value
return true, "value set"
end
return true, "no session"
return false, "no session"
end
utils.get_session = function(key)
utils.get_session_var = function(key)
-- Get data
if ngx.ctx.session and not ngx.ctx.session_err then
return true, "value get", ngx.ctx.session_data[key]
if ngx.ctx.bw.session and not ngx.ctx.bw.session_err then
if ngx.ctx.bw.session_data[key] then
return true, "data present", ngx.ctx.bw.session_data[key]
end
return true, "no data"
end
return false, "no session"
end

View file

@ -1,68 +0,0 @@
local datastore = require "datastore"
local cjson = require "cjson"
local plugins = {}
plugins.load = function(self, path)
-- Read plugin.json file
local file = io.open(path .. "/plugin.json")
if not file then
return false, "can't read plugin.json file"
end
-- Decode plugin.json
-- TODO : check return value of file:read and cjson.encode
local data = cjson.decode(file:read("*a"))
file:close()
-- Check required fields
local required_fields = {"id", "order", "name", "description", "version", "settings"}
for i, field in ipairs(required_fields) do
if data[field] == nil then
return false, "missing field " .. field .. " in plugin.json"
end
-- TODO : check values and types with regex
end
-- Get existing plugins
local list, err = plugins:list()
if not list then
return false, err
end
-- Add our plugin to existing list and sort it
table.insert(list, data)
table.sort(list, function (a, b)
return a.order < b.order
end)
-- Save new plugin list in datastore
local ok, err = datastore:set("plugins", cjson.encode(list))
if not ok then
return false, "can't save new plugin list"
end
-- Save default settings value
for variable, value in pairs(data.settings) do
ok, err = datastore:set("plugin_" .. data.id .. "_" .. variable, value["default"])
if not ok then
return false, "can't save default variable value of " .. variable .. " into datastore"
end
end
-- Return the plugin
return data, "success"
end
plugins.list = function(self)
-- Get encoded plugins from datastore
local encoded_plugins, err = datastore:get("plugins")
if not encoded_plugins then
return false, "can't get encoded plugins from datastore"
end
-- Decode and return the list
return cjson.decode(encoded_plugins), "success"
end
return plugins

View file

@ -49,6 +49,8 @@ plugins = cjson.decode(plugins)
-- Call access() methods
logger:log(ngx.INFO, "calling access() methods of plugins ...")
local status = nil
local redirect = nil
for i, plugin in ipairs(plugins) do
-- Require call
local plugin_lua, err = helpers.require_plugin(plugin.id)
@ -75,15 +77,15 @@ for i, plugin in ipairs(plugins) do
if ret.status then
if ret.status == utils.get_deny_status() then
ngx.ctx.reason = plugin.id
logger:log(ngx.WARN, "denied access from " .. plugin.id .. " : " .. err)
logger:log(ngx.WARN, "denied access from " .. plugin.id .. " : " .. ret.msg)
else
logger:log(ngx.NOTICE, plugin.id .. " returned status " .. tostring(ret.status))
logger:log(ngx.NOTICE, plugin.id .. " returned status " .. tostring(ret.status) .. " : " .. ret.msg)
end
ngx.ctx.status = ret.status
status = ret.status
break
elseif ret.redirect then
logger:log(ngx.NOTICE, plugin.id .. " redirect to " .. ret.redirect .. " : " .. err)
ngx.ctx.redirect = ret.redirect
logger:log(ngx.NOTICE, plugin.id .. " redirect to " .. ret.redirect .. " : " .. ret.msg)
redirect = ret.redirect
break
end
end
@ -103,13 +105,13 @@ end
logger:log(ngx.INFO, "access phase ended")
-- Return status if needed
if ngx.ctx.status then
return ngx.exit(ngx.ctx.status)
if status then
return ngx.exit(status)
end
-- Redirect if needed
if ngx.ctx.redirect then
return ngx.redirect(ngx.ctx.redirect)
if redirect then
return ngx.redirect(redirect)
end
return true

View file

@ -60,7 +60,7 @@ for i, plugin in ipairs(plugins) do
elseif not ret.ret then
logger:log(ngx.ERR, plugin.id .. ":header() call failed : " .. ret.msg)
else
logger:log(ngx.NOTICE, plugin.id .. ":header() call successful : " .. ret.msg)
logger:log(ngx.INFO, plugin.id .. ":header() call successful : " .. ret.msg)
end
end
else

View file

@ -24,7 +24,7 @@ function antibot:access()
end
-- Prepare challenge
local ok, err = self:prepare_challenge(antibot, challenge_uri)
local ok, err = self:prepare_challenge(antibot, self.variables["ANTIBOT_URI"])
if not ok then
return self:ret(false, "can't prepare challenge : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
end
@ -35,20 +35,20 @@ function antibot:access()
return self:ret(false, "can't check if challenge is resolved : " .. err)
end
if resolved then
if ngx.ctx.bw.uri == challenge_uri then
if ngx.ctx.bw.uri == self.variables["ANTIBOT_URI"] then
return self:ret(true, "client already resolved the challenge", nil, original_uri)
end
return self:ret(true, "client already resolved the challenge")
end
-- Redirect to challenge page
if ngx.ctx.bw.uri ~= challenge_uri then
return self:ret(true, "redirecting client to the challenge uri", nil, challenge_uri)
if ngx.ctx.bw.uri ~= self.variables["ANTIBOT_URI"] then
return self:ret(true, "redirecting client to the challenge uri", nil, self.variables["ANTIBOT_URI"])
end
-- Display challenge needed
if ngx.ctx.bw.request_method == "GET" then
ngx.ctx.antibot_display_content = true
ngx.ctx.bw.antibot_display_content = true
return self:ret(true, "displaying challenge to client", ngx.HTTP_OK)
end
@ -61,7 +61,7 @@ function antibot:access()
if redirect then
return self:ret(true, "check challenge redirect : " .. redirect, nil, redirect)
end
ngx.ctx.antibot_display_content = true
ngx.ctx.bw.antibot_display_content = true
return self:ret(true, "displaying challenge to client", ngx.HTTP_OK)
end
@ -79,7 +79,7 @@ function antibot:content()
return self:ret(true, "antibot not activated")
end
-- Check if display content is needed
if not ngx.ctx.antibot_display_content then
if not ngx.ctx.bw.antibot_display_content then
return self:ret(true, "display content not needed")
end
-- Display content
@ -91,13 +91,15 @@ function antibot:content()
end
function antibot:challenge_resolved()
local session, err, exists = utils.get_session()
local session, err, exists, refreshed = utils.get_session()
if err then
return false, "session error : " .. err
return nil, "session error : " .. err
elseif not exists then
return false, "no session set"
end
local raw_data = get_session("antibot")
local ok, err, raw_data = utils.get_session_var("antibot")
if not raw_data then
return false, "session is set but no antibot data", nil
return nil, "session is set but no antibot data", nil
end
local data = cjson.decode(raw_data)
if data.resolved and self.variables["USE_ANTIBOT"] == data.antibot then
@ -107,19 +109,19 @@ function antibot:challenge_resolved()
end
function antibot:prepare_challenge()
local session, err, exists = utils.get_session()
local session, err, exists, refreshed = utils.get_session()
if err then
return false, "session error : " .. err
end
local set_needed = false
local data = nil
if exists then
local raw_data = get_session("antibot")
local ok, err, raw_data = utils.get_session_var("antibot")
if raw_data then
data = cjson.decode(raw_data)
end
end
if not data or current_data.antibot ~= self.variables["USE_ANTIBOT"] then
if not data or data.antibot ~= self.variables["USE_ANTIBOT"] then
data = {
type = self.variables["USE_ANTIBOT"],
resolved = self.variables["USE_ANTIBOT"] == "cookie",
@ -144,20 +146,25 @@ function antibot:prepare_challenge()
end
end
if set_needed then
utils.set_session("antibot", cjson.encode(data))
local ok, err = utils.set_session_var("antibot", cjson.encode(data))
if not ok then
return false, "error while setting session antibot : " .. err
end
end
return true, "prepared"
end
function antibot:display_challenge(challenge_uri)
-- Open session
local session, err, exists = utils.get_session()
local session, err, exists, refreshed = utils.get_session()
if err then
return false, "can't open session : " .. err
elseif not exists then
return false, "no session set"
end
-- Get data
local raw_data = get_session("antibot")
local ok, err, raw_data = utils.get_session_var("antibot")
if not raw_data then
return false, "session is set but no data"
end
@ -201,13 +208,15 @@ end
function antibot:check_challenge()
-- Open session
local session, err, exists = utils.get_session()
local session, err, exists, refreshed = utils.get_session()
if err then
return nil, "can't open session : " .. err, nil
elseif not exists then
return false, "no session set"
end
-- Get data
local raw_data = get_session("antibot")
local ok, err, raw_data = utils.get_session_var("antibot")
if not raw_data then
return false, "session is set but no data", nil
end
@ -237,7 +246,10 @@ function antibot:check_challenge()
return false, "wrong value", nil
end
data.resolved = true
utils.set_session("antibot", cjson.encode(data))
local ok, err = utils.set_session("antibot", cjson.encode(data))
if not ok then
return false, "error while setting session antibot : " .. err
end
return true, "resolved", data.original_uri
end
@ -252,7 +264,10 @@ function antibot:check_challenge()
return false, "wrong value", nil
end
data.resolved = true
utils.set_session("antibot", cjson.encode(data))
local ok, err = utils.set_session("antibot", cjson.encode(data))
if not ok then
return false, "error while setting session antibot : " .. err
end
return true, "resolved", data.original_uri
end
@ -286,7 +301,10 @@ function antibot:check_challenge()
return false, "client failed challenge with score " .. tostring(rdata.score), nil
end
data.resolved = true
utils.set_session("antibot", cjson.encode(data))
local ok, err = utils.set_session("antibot", cjson.encode(data))
if not ok then
return false, "error while setting session antibot : " .. err
end
return true, "resolved", data.original_uri
end
@ -320,7 +338,10 @@ function antibot:check_challenge()
return false, "client failed challenge", nil
end
data.resolved = true
utils.set_session("antibot", cjson.encode(data))
local ok, err = utils.set_session("antibot", cjson.encode(data))
if not ok then
return false, "error while setting session antibot : " .. err
end
return true, "resolved", data.original_uri
end

View file

@ -1,5 +1,6 @@
{% if USE_ANTIBOT == "yes" +%}
{% if USE_ANTIBOT != "no" +%}
location /{{ ANTIBOT_URI }} {
default_type 'text/html';
root /usr/share/bunkerweb/core/antibot/files;
content_by_lua_block {
local cantibot = require "antibot.antibot"

View file

@ -4,24 +4,24 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Bot Detection</title>
<link rel="icon" href="data:image/svg+xml, %%3Csvg version='1.0' xmlns='http://www.w3.org/2000/svg' width='96.000000pt' height='96.000000pt' viewBox='0 0 96.000000 96.000000' preserveAspectRatio='xMidYMid meet'%%3E%%3Cg transform='translate(0.000000,96.000000) scale(0.100000,-0.100000)'%%0Afill='%%23085577' stroke='none'%%3E%%3Cpath d='M535 863 c-22 -2 -139 -17 -260 -34 -228 -31 -267 -43 -272 -85 -2%%0A-10 23 -181 55 -379 l57 -360 400 0 400 0 20 40 c16 31 20 59 19 125 -1 100%%0A-24 165 -73 199 -41 29 -46 57 -22 111 30 67 29 188 -3 256 -13 28 -37 60 -53%%0A72 -55 39 -169 62 -268 55z m-15 -348 c30 -16 60 -61 60 -90 0 -10 -8 -33 -17%%0A-52 -16 -34 -16 -41 0 -116 9 -44 15 -82 12 -85 -6 -7 -92 -21 -131 -21 l-31%%0A-1 -6 85 c-4 75 -8 89 -31 112 -20 20 -26 36 -26 70 0 38 5 50 34 79 39 39 86%%0A45 136 19z'/%%3E%%3C/g%%3E%%3C/svg%%3E" type="image/svg+xml"/>
<link rel="icon" href="data:image/svg+xml, %3Csvg version='1.0' xmlns='http://www.w3.org/2000/svg' width='96.000000pt' height='96.000000pt' viewBox='0 0 96.000000 96.000000' preserveAspectRatio='xMidYMid meet'%3E%3Cg transform='translate(0.000000,96.000000) scale(0.100000,-0.100000)'%0Afill='%23085577' stroke='none'%3E%3Cpath d='M535 863 c-22 -2 -139 -17 -260 -34 -228 -31 -267 -43 -272 -85 -2%0A-10 23 -181 55 -379 l57 -360 400 0 400 0 20 40 c16 31 20 59 19 125 -1 100%0A-24 165 -73 199 -41 29 -46 57 -22 111 30 67 29 188 -3 256 -13 28 -37 60 -53%0A72 -55 39 -169 62 -268 55z m-15 -348 c30 -16 60 -61 60 -90 0 -10 -8 -33 -17%0A-52 -16 -34 -16 -41 0 -116 9 -44 15 -82 12 -85 -6 -7 -92 -21 -131 -21 l-31%0A-1 -6 85 c-4 75 -8 89 -31 112 -20 20 -26 36 -26 70 0 38 5 50 34 79 39 39 86%0A45 136 19z'/%3E%3C/g%3E%3C/svg%3E" type="image/svg+xml"/>
<style type="text/css">
body,
html {
width: 100%%;
height: 100%%;
width: 100%;
height: 100%;
background-color: #125678;
}
body {
color: #fff;
text-align: center;
padding: 0;
min-height: 100%%;
min-height: 100%;
display: table;
font-family: "Open Sans", Arial, sans-serif;
margin: 0;
-ms-text-size-adjust: 100%%;
-webkit-text-size-adjust: 100%%;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
h1 {
display: flex;
@ -49,7 +49,7 @@
}
footer {
position: fixed;
width: 100%%;
width: 100%;
letter-spacing: 1px;
left: 0;
bottom: 0;
@ -130,8 +130,8 @@
<div class="cover message">
<h1>Please prove that you are Human before accessing this website</h1>
<p class="lead">
<form class="form" method="POST" action="%s">
<img src="data:image/jpeg;base64,%s"/>
<form class="form" method="POST" action="{{antibot_uri}}">
<img src="data:image/jpeg;base64,{{captcha}}"/>
<input class="captcha" type="text" name="captcha" /> <input class="btn" type="submit" value="Submit" />
</form>
</p>

View file

@ -142,6 +142,7 @@ CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-pack" do_and_check_cmd make INST_LIBDIR=
# Installing lua-resty-openssl
echo " Installing lua-resty-openssl"
CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-openssl" do_and_check_cmd make LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
do_and_check_cmd cp /tmp/bunkerweb/deps/src/lua-resty-openssl/lib/resty/openssl.lua /usr/share/bunkerweb/deps/lib/lua/resty
# Compile dynamic modules
echo " Compiling and installing dynamic modules"