mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
bw - various improvements and refactoring (WIP)
This commit is contained in:
parent
aea3fae2ba
commit
077b2c1c13
38 changed files with 1383 additions and 885 deletions
|
|
@ -1,24 +1,52 @@
|
|||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local cjson = require "cjson"
|
||||
local class = require "middleclass"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local process = require "ngx.process"
|
||||
local rsignal = require "resty.signal"
|
||||
local upload = require "resty.upload"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
|
||||
local api = class("api")
|
||||
|
||||
local datastore = cdatastore:new()
|
||||
local logger = clogger:new("API")
|
||||
|
||||
local get_variable = utils.get_variable
|
||||
local is_ip_in_networks = utils.is_ip_in_networks
|
||||
-- local run = shell.run
|
||||
local NOTICE = ngx.NOTICE
|
||||
local ERR = ngx.ERR
|
||||
local HTTP_OK = ngx.HTTP_OK
|
||||
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local HTTP_NOT_FOUND = ngx.HTTP_NOT_FOUND
|
||||
local kill = rsignal.kill
|
||||
local get_master_pid = process.get_master_pid
|
||||
local execute = os.execute
|
||||
local open = io.open
|
||||
local read_body = ngx_req.read_body
|
||||
local get_body_data = ngx_req.get_body_data
|
||||
local get_body_file = ngx_req.get_body_file
|
||||
local decode = cjson.decode
|
||||
local encode = cjson.encode
|
||||
local floor = math.floor
|
||||
local match = string.match
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
|
||||
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
||||
|
||||
function api:initialize()
|
||||
self.datastore = datastore:new()
|
||||
self.logger = logger:new("API")
|
||||
self.ctx = ngx.ctx
|
||||
local data, err = utils.get_variable("API_WHITELIST_IP", false)
|
||||
function api:initialize(ctx)
|
||||
self.ctx = ctx
|
||||
local data, err = get_variable("API_WHITELIST_IP", false)
|
||||
self.ips = {}
|
||||
if not data then
|
||||
self.logger.log(ngx.ERR, "can't get API_WHITELIST_IP variable : " .. err)
|
||||
logger:log(ERR, "can't get API_WHITELIST_IP variable : " .. err)
|
||||
else
|
||||
for ip in data:gmatch("%S+") do
|
||||
table.insert(self.ips, ip)
|
||||
|
|
@ -28,23 +56,23 @@ end
|
|||
|
||||
-- luacheck: ignore 212
|
||||
function api:log_cmd(cmd, status, stdout, stderr)
|
||||
local level = ngx.NOTICE
|
||||
local level = NOTICE
|
||||
local prefix = "success"
|
||||
if status ~= 0 then
|
||||
level = ngx.ERR
|
||||
level = ERR
|
||||
prefix = "error"
|
||||
end
|
||||
self.logger:log(level, prefix .. " while running command " .. cmd)
|
||||
self.logger:log(level, "stdout = " .. stdout)
|
||||
self.logger:log(level, "stdout = " .. stderr)
|
||||
logger:log(level, prefix .. " while running command " .. cmd)
|
||||
logger:log(level, "stdout = " .. stdout)
|
||||
logger:log(level, "stdout = " .. stderr)
|
||||
end
|
||||
|
||||
-- TODO : use this if we switch to OpenResty
|
||||
function api:cmd(cmd)
|
||||
-- Non-blocking command
|
||||
-- luacheck: ignore 113
|
||||
local ok, stdout, stderr, reason, status = shell.run(cmd, nil, 10000)
|
||||
self.logger:log_cmd(cmd, status, stdout, stderr)
|
||||
local ok, stdout, stderr, reason, status = run(cmd, nil, 10000)
|
||||
self:log_cmd(cmd, status, stdout, stderr)
|
||||
-- Timeout
|
||||
if ok == nil then
|
||||
return nil, reason
|
||||
|
|
@ -62,25 +90,30 @@ function api:response(http_status, api_status, msg)
|
|||
end
|
||||
|
||||
api.global.GET["^/ping$"] = function(self)
|
||||
return self:response(ngx.HTTP_OK, "success", "pong")
|
||||
return self:response(HTTP_OK, "success", "pong")
|
||||
end
|
||||
|
||||
api.global.POST["^/reload$"] = function(self)
|
||||
-- Send HUP signal to master process
|
||||
local ok, err = rsignal.kill(process.get_master_pid(), "HUP")
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
-- Check config
|
||||
local status = execute("nginx -t")
|
||||
if status ~= 0 then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "config check failed")
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "reload successful")
|
||||
-- Send HUP signal to master process
|
||||
local ok, err = kill(get_master_pid(), "HUP")
|
||||
if not ok then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
end
|
||||
return self:response(HTTP_OK, "success", "reload successful")
|
||||
end
|
||||
|
||||
api.global.POST["^/stop$"] = function(self)
|
||||
-- Send QUIT signal to master process
|
||||
local ok, err = rsignal.kill(process.get_master_pid(), "QUIT")
|
||||
local ok, err = kill(get_master_pid(), "QUIT")
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "err = " .. err)
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "stop successful")
|
||||
return self:response(HTTP_OK, "success", "stop successful")
|
||||
end
|
||||
|
||||
api.global.POST["^/confs$"] = function(self)
|
||||
|
|
@ -99,16 +132,19 @@ api.global.POST["^/confs$"] = function(self)
|
|||
end
|
||||
local form, err = upload:new(4096)
|
||||
if not form then
|
||||
return self:response(ngx.HTTP_BAD_REQUEST, "error", err)
|
||||
return self:response(HTTP_BAD_REQUEST, "error", err)
|
||||
end
|
||||
form:set_timeout(1000)
|
||||
local file = io.open(tmp, "w+")
|
||||
local file, err = open(tmp, "w+")
|
||||
if not file then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", err)
|
||||
end
|
||||
while true do
|
||||
-- luacheck: ignore 421
|
||||
local typ, res, err = form:read()
|
||||
if not typ then
|
||||
file:close()
|
||||
return self:response(ngx.HTTP_BAD_REQUEST, "error", err)
|
||||
return self:response(HTTP_BAD_REQUEST, "error", err)
|
||||
end
|
||||
if typ == "eof" then
|
||||
break
|
||||
|
|
@ -124,12 +160,12 @@ api.global.POST["^/confs$"] = function(self)
|
|||
"tar xzf " .. tmp .. " -C " .. destination,
|
||||
}
|
||||
for _, cmd in ipairs(cmds) do
|
||||
local status = os.execute(cmd)
|
||||
local status = execute(cmd)
|
||||
if status ~= 0 then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||
end
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", "saved data at " .. destination)
|
||||
return self:response(HTTP_OK, "success", "saved data at " .. destination)
|
||||
end
|
||||
|
||||
api.global.POST["^/data$"] = api.global.POST["^/confs$"]
|
||||
|
|
@ -141,80 +177,86 @@ api.global.POST["^/custom_configs$"] = api.global.POST["^/confs$"]
|
|||
api.global.POST["^/plugins$"] = api.global.POST["^/confs$"]
|
||||
|
||||
api.global.POST["^/unban$"] = function(self)
|
||||
ngx.req.read_body()
|
||||
local data = ngx.req.get_body_data()
|
||||
read_body()
|
||||
local data = get_body_data()
|
||||
if not data then
|
||||
local data_file = ngx.req.get_body_file()
|
||||
local data_file = get_body_file()
|
||||
if data_file then
|
||||
local file = io.open(data_file)
|
||||
local file, err = open(data_file)
|
||||
if not file then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", err)
|
||||
end
|
||||
data = file:read("*a")
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
local ok, ip = pcall(cjson.decode, data)
|
||||
local ok, ip = pcall(decode, data)
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
end
|
||||
self.datastore:delete("bans_ip_" .. ip["ip"])
|
||||
return self:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " unbanned")
|
||||
datastore:delete("bans_ip_" .. ip["ip"])
|
||||
return self:response(HTTP_OK, "success", "ip " .. ip["ip"] .. " unbanned")
|
||||
end
|
||||
|
||||
api.global.POST["^/ban$"] = function(self)
|
||||
ngx.req.read_body()
|
||||
local data = ngx.req.get_body_data()
|
||||
read_body()
|
||||
local data = get_body_data()
|
||||
if not data then
|
||||
local data_file = ngx.req.get_body_file()
|
||||
local data_file = get_body_file()
|
||||
if data_file then
|
||||
local file = io.open(data_file)
|
||||
local file, err = io.open(data_file)
|
||||
if not file then
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", err)
|
||||
end
|
||||
data = file:read("*a")
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
local ok, ip = pcall(cjson.decode, data)
|
||||
local ok, ip = pcall(decode, data)
|
||||
if not ok then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't decode JSON : " .. ip)
|
||||
end
|
||||
self.datastore:set("bans_ip_" .. ip["ip"], "manual", ip["exp"])
|
||||
return self:response(ngx.HTTP_OK, "success", "ip " .. ip["ip"] .. " banned")
|
||||
datastore:set("bans_ip_" .. ip["ip"], "manual", ip["exp"])
|
||||
return self:response(HTTP_OK, "success", "ip " .. ip["ip"] .. " banned")
|
||||
end
|
||||
|
||||
api.global.GET["^/bans$"] = function(self)
|
||||
local data = {}
|
||||
for _, k in ipairs(self.datastore:keys()) do
|
||||
for _, k in ipairs(datastore:keys()) do
|
||||
if k:find("^bans_ip_") then
|
||||
local reason, err = self.datastore:get(k)
|
||||
local reason, err = datastore:get(k)
|
||||
if err then
|
||||
return self:response(
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
"error",
|
||||
"can't access " .. k .. " from datastore : " .. reason
|
||||
)
|
||||
end
|
||||
local ok, ttl = self.datastore:ttl(k)
|
||||
local ok, ttl = datastore:ttl(k)
|
||||
if not ok then
|
||||
return self:response(
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
HTTP_INTERNAL_SERVER_ERROR,
|
||||
"error",
|
||||
"can't access ttl " .. k .. " from datastore : " .. ttl
|
||||
)
|
||||
end
|
||||
local ban = { ip = k:sub(9, #k), reason = reason, exp = math.floor(ttl) }
|
||||
local ban = { ip = k:sub(9, #k), reason = reason, exp = floor(ttl) }
|
||||
table.insert(data, ban)
|
||||
end
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", data)
|
||||
return self:response(HTTP_OK, "success", data)
|
||||
end
|
||||
|
||||
api.global.GET["^/variables$"] = function(self)
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't access variables from datastore : " .. err)
|
||||
return self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't access variables from datastore : " .. err)
|
||||
end
|
||||
return self:response(ngx.HTTP_OK, "success", variables)
|
||||
return self:response(HTTP_OK, "success", variables)
|
||||
end
|
||||
|
||||
function api:is_allowed_ip()
|
||||
if utils.is_ip_in_networks(self.ctx.bw.remote_addr, self.ips) then
|
||||
if is_ip_in_networks(self.ctx.bw.remote_addr, self.ips) then
|
||||
return true, "ok"
|
||||
end
|
||||
return false, "IP is not in API_WHITELIST_IP"
|
||||
|
|
@ -223,10 +265,10 @@ end
|
|||
function api:do_api_call()
|
||||
if self.global[self.ctx.bw.request_method] ~= nil then
|
||||
for uri, api_fun in pairs(self.global[self.ctx.bw.request_method]) do
|
||||
if string.match(self.ctx.bw.uri, uri) then
|
||||
if match(self.ctx.bw.uri, uri) then
|
||||
local status, resp = api_fun(self)
|
||||
local ret = true
|
||||
if status ~= ngx.HTTP_OK then
|
||||
if status ~= HTTP_OK then
|
||||
ret = false
|
||||
end
|
||||
if #resp["msg"] == 0 then
|
||||
|
|
@ -235,26 +277,36 @@ function api:do_api_call()
|
|||
resp["data"] = resp["msg"]
|
||||
resp["msg"] = resp["status"]
|
||||
end
|
||||
return ret, resp["msg"], status, cjson.encode(resp)
|
||||
return ret, resp["msg"], status, encode(resp)
|
||||
end
|
||||
end
|
||||
end
|
||||
local list, err = self.datastore:get("plugins", true)
|
||||
local list, err = datastore:get("plugins", true)
|
||||
if not list then
|
||||
local _, resp = self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
|
||||
return false, resp["msg"], ngx.HTTP_INTERNAL_SERVER_ERROR, cjson.encode(resp)
|
||||
local _, resp = self:response(HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
|
||||
return false, resp["msg"], HTTP_INTERNAL_SERVER_ERROR, encode(resp)
|
||||
end
|
||||
for _, plugin in ipairs(list) do
|
||||
if pcall(require, plugin.id .. "/" .. plugin.id) then
|
||||
local plugin_lua = require(plugin.id .. "/" .. plugin.id)
|
||||
if plugin_lua.api ~= nil then
|
||||
local matched, status, resp = plugin_lua:api(self.ctx)
|
||||
if matched then
|
||||
local ret = true
|
||||
if status ~= ngx.HTTP_OK then
|
||||
ret = false
|
||||
local plugin_lua, err = require_plugin(plugin.id)
|
||||
if plugin_lua and plugin_lua.api ~= nil then
|
||||
local ok, plugin_obj = new_plugin(plugin_lua, self.ctx)
|
||||
if not ok then
|
||||
logger:log(ERR, "can't instantiate " .. plugin.id .. " : " .. plugin_obj)
|
||||
else
|
||||
local ok, ret = call_plugin(plugin_obj, "api")
|
||||
if not ok then
|
||||
logger:log(ERR, "error while executing " .. plugin.id .. ":api() : " .. ret)
|
||||
else
|
||||
if ret.ret then
|
||||
local resp = {}
|
||||
if ret.status == HTTP_OK then
|
||||
resp["status"] = "success"
|
||||
else
|
||||
resp["status"] = "error"
|
||||
end
|
||||
resp["msg"] = ret.msg
|
||||
return ret.status == HTTP_OK, resp["status"], ret.status, encode(resp)
|
||||
end
|
||||
return ret, resp["msg"], status, cjson.encode(resp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -262,7 +314,7 @@ function api:do_api_call()
|
|||
local resp = {}
|
||||
resp["status"] = "error"
|
||||
resp["msg"] = "not found"
|
||||
return false, "error", ngx.HTTP_NOT_FOUND, cjson.encode(resp)
|
||||
return false, "error", HTTP_NOT_FOUND, encode(resp)
|
||||
end
|
||||
|
||||
return api
|
||||
|
|
|
|||
|
|
@ -1,17 +1,27 @@
|
|||
local ngx = ngx
|
||||
local class = require "middleclass"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local mlcache = require "resty.mlcache"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = class("cachestore")
|
||||
|
||||
local logger = clogger:new("CACHESTORE")
|
||||
|
||||
local subsystem = ngx.config.subsystem
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local null = ngx.null
|
||||
local get_ctx_obj = utils.get_ctx_obj
|
||||
local is_cosocket_available = utils.is_cosocket_available
|
||||
|
||||
-- Instantiate mlcache object at module level (which will be cached when running init phase)
|
||||
-- TODO : custom settings
|
||||
local shm = "cachestore"
|
||||
local ipc_shm = "cachestore_ipc"
|
||||
local shm_miss = "cachestore_miss"
|
||||
local shm_locks = "cachestore_locks"
|
||||
if not ngx.shared.cachestore then
|
||||
if subsystem == "stream" then
|
||||
shm = "cachestore_stream"
|
||||
ipc_shm = "cachestore_ipc_stream"
|
||||
shm_miss = "cachestore_miss_stream"
|
||||
|
|
@ -33,22 +43,18 @@ local cache, err = mlcache.new("cachestore", shm, {
|
|||
},
|
||||
ipc_shm = ipc_shm,
|
||||
})
|
||||
local module_logger = logger:new("CACHESTORE")
|
||||
if not cache then
|
||||
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
||||
logger:log(ERR, "can't instantiate mlcache : " .. err)
|
||||
end
|
||||
|
||||
function cachestore:initialize(use_redis, new_cs, ctx)
|
||||
self.ctx = ctx
|
||||
self.cache = cache
|
||||
function cachestore:initialize(use_redis, ctx, pool)
|
||||
self.use_redis = use_redis or false
|
||||
self.logger = module_logger
|
||||
if new_cs then
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
self.shared_cs = false
|
||||
else
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore", self.ctx)
|
||||
self.shared_cs = true
|
||||
if self.use_redis then
|
||||
if ctx then
|
||||
self.clusterstore = get_ctx_obj("clusterstore", ctx)
|
||||
else
|
||||
self.clusterstore = clusterstore:new(pool)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -57,8 +63,7 @@ function cachestore:get(key)
|
|||
local callback = function(key, cs)
|
||||
-- Connect to redis
|
||||
-- luacheck: ignore 431
|
||||
local clusterstore = cs or require "bunkerweb.clusterstore":new(false)
|
||||
local ok, err, _ = clusterstore:connect()
|
||||
local ok, err, _ = cs:connect()
|
||||
if not ok then
|
||||
return nil, "can't connect to redis : " .. err, nil
|
||||
end
|
||||
|
|
@ -76,14 +81,14 @@ function cachestore:get(key)
|
|||
end
|
||||
return {ret_get, ret_ttl}
|
||||
]]
|
||||
local ret, err = clusterstore:call("eval", redis_script, 1, key)
|
||||
local ret, err = cs:call("eval", redis_script, 1, key)
|
||||
if not ret then
|
||||
clusterstore:close()
|
||||
cs:close()
|
||||
return nil, err, nil
|
||||
end
|
||||
-- Extract values
|
||||
clusterstore:close()
|
||||
if ret[1] == ngx.null then
|
||||
cs:close()
|
||||
if ret[1] == null then
|
||||
ret[1] = nil
|
||||
ret[2] = -1
|
||||
elseif ret[2] < 0 then
|
||||
|
|
@ -96,29 +101,25 @@ function cachestore:get(key)
|
|||
end
|
||||
-- luacheck: ignore 431
|
||||
local value, err, hit_level
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local cs = nil
|
||||
if self.shared_cs then
|
||||
cs = self.clusterstore
|
||||
end
|
||||
value, err, hit_level = self.cache:get(key, nil, callback, key, cs)
|
||||
if self.use_redis and is_cosocket_available() then
|
||||
value, err, hit_level = self.cache:get(key, nil, callback, key, self.clusterstore)
|
||||
else
|
||||
value, err, hit_level = self.cache:get(key, nil, callback_no_miss)
|
||||
end
|
||||
if value == nil and err ~= nil then
|
||||
return false, err
|
||||
end
|
||||
self.logger:log(ngx.INFO, "hit level for " .. key .. " = " .. tostring(hit_level))
|
||||
logger:log(INFO, "hit level for " .. key .. " = " .. tostring(hit_level))
|
||||
return true, value
|
||||
end
|
||||
|
||||
function cachestore:set(key, value, ex)
|
||||
-- luacheck: ignore 431
|
||||
local ok, err
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
if self.use_redis and is_cosocket_available() then
|
||||
ok, err = self:set_redis(key, value, ex)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
end
|
||||
end
|
||||
if ex then
|
||||
|
|
@ -153,10 +154,10 @@ end
|
|||
function cachestore:delete(key)
|
||||
-- luacheck: ignore 431
|
||||
local ok, err
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
if self.use_redis and is_cosocket_available() then
|
||||
ok, err = self:del_redis(key)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
end
|
||||
end
|
||||
ok, err = self.cache:delete(key)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,18 @@
|
|||
local ngx = ngx
|
||||
local class = require "middleclass"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local redis = require "resty.redis"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local clusterstore = class("clusterstore")
|
||||
|
||||
local logger = clogger:new("CLUSTERSTORE")
|
||||
|
||||
local get_variable = utils.get_variable
|
||||
local ERR = ngx.ERR
|
||||
local tonumber = tonumber
|
||||
|
||||
function clusterstore:initialize(pool)
|
||||
-- Instantiate logger
|
||||
self.logger = logger:new("CLUSTERSTORE")
|
||||
-- Get variables
|
||||
local variables = {
|
||||
["REDIS_HOST"] = "",
|
||||
|
|
@ -18,33 +23,32 @@ function clusterstore:initialize(pool)
|
|||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = "",
|
||||
}
|
||||
-- Set them for later user
|
||||
-- Set them for later use
|
||||
self.variables = {}
|
||||
for k, _ in pairs(variables) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
local value, err = get_variable(k, false)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
logger:log(ERR, err)
|
||||
end
|
||||
self.variables[k] = value
|
||||
end
|
||||
-- Don't instantiate a redis object for now
|
||||
self.redis_client = nil
|
||||
-- Instantiate object
|
||||
self.pool = pool == nil or pool
|
||||
local redis_client, err = redis:new()
|
||||
self.redis_client = redis_client
|
||||
if self.redis_client == nil then
|
||||
logger:log(ERR, "can't instantiate redis object : " .. err)
|
||||
return
|
||||
end
|
||||
self.redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
end
|
||||
|
||||
function clusterstore:connect()
|
||||
-- Check if we are already connected
|
||||
if self.redis_client then
|
||||
return true, "already connected", self.redis_client:get_reused_times()
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
-- Instantiate object
|
||||
local redis_client, err = redis:new()
|
||||
if redis_client == nil then
|
||||
return false, err
|
||||
end
|
||||
-- Set timeouts
|
||||
redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
-- Connect
|
||||
-- Set options
|
||||
local options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
}
|
||||
|
|
@ -52,22 +56,22 @@ function clusterstore:connect()
|
|||
options.pool = "bw-redis"
|
||||
options.pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
end
|
||||
local ok, err = redis_client:connect(self.variables["REDIS_HOST"], tonumber(self.variables["REDIS_PORT"]), options)
|
||||
-- Connect
|
||||
local ok, err = self.redis_client:connect(self.variables["REDIS_HOST"], tonumber(self.variables["REDIS_PORT"]), options)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
self.redis_client = redis_client
|
||||
-- Select database if needed
|
||||
local times, err = self.redis_client:get_reused_times()
|
||||
if err then
|
||||
self:close()
|
||||
self.redis_client:close()
|
||||
return false, err
|
||||
end
|
||||
if times == 0 then
|
||||
if times < 2 then
|
||||
-- luacheck: ignore 421
|
||||
local _, err = self.redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||
if err then
|
||||
self:close()
|
||||
self.redis_client:close()
|
||||
return false, err
|
||||
end
|
||||
end
|
||||
|
|
@ -75,40 +79,40 @@ function clusterstore:connect()
|
|||
end
|
||||
|
||||
function clusterstore:close()
|
||||
if self.redis_client then
|
||||
-- Equivalent to close but keep a pool of connections
|
||||
if self.pool then
|
||||
local ok, err = self.redis_client:set_keepalive(
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
)
|
||||
self.redis_client = nil
|
||||
if not ok then
|
||||
require("bunkerweb.logger"):new("clusterstore-close"):log(ngx.ERR, err)
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
-- Close
|
||||
local ok, err = self.redis_client:close()
|
||||
self.redis_client.redis_client = nil
|
||||
return ok, err
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
return false, "not connected"
|
||||
-- Pool case
|
||||
local ok, err
|
||||
if self.pool then
|
||||
ok, err = self.redis_client:set_keepalive(
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
)
|
||||
-- No pool
|
||||
else
|
||||
ok, err = self.redis_client:close()
|
||||
end
|
||||
if err then
|
||||
logger:log(ERR, "error while closing redis_client : " .. err)
|
||||
end
|
||||
return ok ~= nil, err
|
||||
end
|
||||
|
||||
function clusterstore:call(method, ...)
|
||||
-- Check if we are connected
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
-- Call method
|
||||
return self.redis_client[method](self.redis_client, ...)
|
||||
end
|
||||
|
||||
function clusterstore:multi(calls)
|
||||
-- Check if we are connected
|
||||
-- Check if client is created
|
||||
if not self.redis_client then
|
||||
return false, "not connected"
|
||||
return false, "client is not instantiated"
|
||||
end
|
||||
-- Start transaction
|
||||
local ok, err = self.redis_client:multi()
|
||||
|
|
@ -121,7 +125,7 @@ function clusterstore:multi(calls)
|
|||
local args = unpack(call[2])
|
||||
ok, err = self.redis_client[method](self.redis_client, args)
|
||||
if not ok then
|
||||
return false, method + "() failed : " .. err
|
||||
return false, method .. "() failed : " .. err
|
||||
end
|
||||
end
|
||||
-- Exec transaction
|
||||
|
|
|
|||
|
|
@ -1,18 +1,25 @@
|
|||
local ngx = ngx
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local lrucache = require "resty.lrucache"
|
||||
local datastore = class("datastore")
|
||||
|
||||
local lru, err = lrucache.new(100000)
|
||||
local logger = clogger:new("DATASTORE")
|
||||
|
||||
local ERR = ngx.ERR
|
||||
local subsystem = ngx.config.subsystem
|
||||
local shared = ngx.shared
|
||||
|
||||
local lru, err_lru = lrucache.new(100000)
|
||||
if not lru then
|
||||
require "bunkerweb.logger"
|
||||
:new("DATASTORE")
|
||||
:log(ngx.ERR, "failed to instantiate LRU cache : " .. (err or "unknown error"))
|
||||
logger:log(ERR, "failed to instantiate LRU cache : " .. err_lru)
|
||||
end
|
||||
|
||||
function datastore:initialize()
|
||||
self.dict = ngx.shared.datastore
|
||||
if not self.dict then
|
||||
self.dict = ngx.shared.datastore_stream
|
||||
if subsystem == "http" then
|
||||
self.dict = shared.datastore
|
||||
else
|
||||
self.dict = shared.datastore_stream
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -20,6 +27,9 @@ function datastore:get(key, worker)
|
|||
-- luacheck: ignore 431
|
||||
local value, err
|
||||
if worker then
|
||||
if not lru then
|
||||
return nil, "lru is not instantiated"
|
||||
end
|
||||
value, err = lru:get(key)
|
||||
return value, err or "not found"
|
||||
end
|
||||
|
|
@ -32,6 +42,9 @@ end
|
|||
|
||||
function datastore:set(key, value, exptime, worker)
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
lru:set(key, value, exptime)
|
||||
return true, "success"
|
||||
end
|
||||
|
|
@ -41,6 +54,9 @@ end
|
|||
|
||||
function datastore:delete(key, worker)
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
lru:delete(key)
|
||||
return true, "success"
|
||||
end
|
||||
|
|
@ -50,6 +66,9 @@ end
|
|||
|
||||
function datastore:keys(worker)
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
return lru:keys(0)
|
||||
end
|
||||
return self.dict:get_keys(0)
|
||||
|
|
@ -70,6 +89,9 @@ end
|
|||
function datastore:delete_all(pattern, worker)
|
||||
local keys
|
||||
if worker then
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
keys = lru:keys(0)
|
||||
else
|
||||
keys = self.dict:get_keys(0)
|
||||
|
|
@ -84,6 +106,9 @@ end
|
|||
|
||||
-- luacheck: ignore 212
|
||||
function datastore:flush_lru()
|
||||
if not lru then
|
||||
return false, "lru is not instantiated"
|
||||
end
|
||||
lru:flush_all()
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,37 @@
|
|||
local ngx = ngx
|
||||
local cjson = require "cjson"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local bwctx = require "bunkerweb.ctx"
|
||||
local base = require "resty.core.base"
|
||||
|
||||
local open = io.open
|
||||
local decode = cjson.decode
|
||||
local encode = cjson.encode
|
||||
local tostring = tostring
|
||||
local get_phases = utils.get_phases
|
||||
local get_request = base.get_request
|
||||
local apply_ref = bwctx.apply_ref
|
||||
local stash_ref = bwctx.stash_ref
|
||||
local subsystem = ngx.config.subsystem
|
||||
local var = ngx.var
|
||||
local req = ngx.req
|
||||
local ip_is_global = utils.ip_is_global
|
||||
local get_integration = utils.get_integration
|
||||
local get_version = utils.get_version
|
||||
local is_ipv4 = utils.is_ipv4
|
||||
local is_ipv6 = utils.is_ipv6
|
||||
local get_variable = utils.get_variable
|
||||
|
||||
local helpers = {}
|
||||
|
||||
helpers.load_plugin = function(json)
|
||||
-- Open file
|
||||
local file, err, nb = io.open(json, "r")
|
||||
local file, err, nb = open(json, "r")
|
||||
if not file then
|
||||
return false, "can't load JSON at " .. json .. " : " .. err .. " (nb = " .. tostring(nb) .. ")"
|
||||
end
|
||||
-- Decode JSON
|
||||
local ok, plugin = pcall(cjson.decode, file:read("*a"))
|
||||
local ok, plugin = pcall(decode, file:read("*a"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid JSON at " .. json .. " : " .. err
|
||||
|
|
@ -26,7 +45,7 @@ helpers.load_plugin = function(json)
|
|||
end
|
||||
end
|
||||
if #missing_fields > 0 then
|
||||
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
|
||||
return false, "missing field(s) " .. encode(missing_fields) .. " for JSON at " .. json
|
||||
end
|
||||
-- Try require
|
||||
local plugin_lua, err = helpers.require_plugin(plugin.id)
|
||||
|
|
@ -34,7 +53,7 @@ helpers.load_plugin = function(json)
|
|||
return false, err
|
||||
end
|
||||
-- Fill phases
|
||||
local phases = utils.get_phases()
|
||||
local phases = get_phases()
|
||||
plugin.phases = {}
|
||||
if plugin_lua then
|
||||
for _, phase in ipairs(phases) do
|
||||
|
|
@ -49,11 +68,11 @@ end
|
|||
|
||||
helpers.order_plugins = function(plugins)
|
||||
-- Extract orders
|
||||
local file, err, nb = io.open("/usr/share/bunkerweb/core/order.json", "r")
|
||||
local file, err, nb = open("/usr/share/bunkerweb/core/order.json", "r")
|
||||
if not file then
|
||||
return false, err .. " (nb = " .. tostring(nb) .. ")"
|
||||
end
|
||||
local ok, orders = pcall(cjson.decode, file:read("*a"))
|
||||
local ok, orders = pcall(decode, file:read("*a"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid order.json : " .. err
|
||||
|
|
@ -68,7 +87,7 @@ helpers.order_plugins = function(plugins)
|
|||
end
|
||||
-- Order result
|
||||
local result_orders = {}
|
||||
for _, phase in ipairs(utils.get_phases()) do
|
||||
for _, phase in ipairs(get_phases()) do
|
||||
result_orders[phase] = {}
|
||||
end
|
||||
-- Fill order first
|
||||
|
|
@ -82,7 +101,7 @@ helpers.order_plugins = function(plugins)
|
|||
end
|
||||
end
|
||||
-- Then append missing plugins to the end
|
||||
for _, phase in ipairs(utils.get_phases()) do
|
||||
for _, phase in ipairs(get_phases()) do
|
||||
for id, plugin in pairs(plugins_phases) do
|
||||
if plugin[phase] then
|
||||
table.insert(result_orders[phase], id)
|
||||
|
|
@ -141,7 +160,7 @@ helpers.call_plugin = function(plugin, method)
|
|||
end
|
||||
end
|
||||
if #missing_values > 0 then
|
||||
return false, "missing required return value(s) : " .. cjson.encode(missing_values)
|
||||
return false, "missing required return value(s) : " .. encode(missing_values)
|
||||
end
|
||||
-- Return
|
||||
return true, ret
|
||||
|
|
@ -151,64 +170,66 @@ helpers.fill_ctx = function()
|
|||
-- Return errors as table
|
||||
local errors = {}
|
||||
-- Try to load saved ctx
|
||||
if base.get_request() then
|
||||
bwctx.apply_ref()
|
||||
local request = get_request()
|
||||
if request then
|
||||
apply_ref()
|
||||
end
|
||||
local ctx = ngx.ctx
|
||||
-- Check if ctx is already filled
|
||||
if not ctx.bw then
|
||||
-- Instantiate bw table
|
||||
local data = {}
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
data.kind = "stream"
|
||||
if request then
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if subsystem == "stream" then
|
||||
data.kind = "stream"
|
||||
end
|
||||
data.remote_addr = var.remote_addr
|
||||
data.server_name = var.server_name
|
||||
if data.kind == "http" then
|
||||
data.uri = var.uri
|
||||
data.request_uri = var.request_uri
|
||||
data.request_method = var.request_method
|
||||
data.http_user_agent = var.http_user_agent
|
||||
data.http_host = var.http_host
|
||||
data.http_content_type = var.http_content_type
|
||||
data.http_content_length = var.http_content_length
|
||||
data.http_origin = var.http_origin
|
||||
data.http_version = req.http_version()
|
||||
data.scheme = var.scheme
|
||||
end
|
||||
-- IP data : global
|
||||
local ip_global, err = ip_is_global(data.remote_addr)
|
||||
if ip_global == nil then
|
||||
table.insert(errors, "can't check if IP is global : " .. err)
|
||||
else
|
||||
data.ip_is_global = ip_global
|
||||
end
|
||||
-- IP data : v4 / v6
|
||||
data.ip_is_ipv4 = is_ipv4(data.ip)
|
||||
data.ip_is_ipv6 = is_ipv6(data.ip)
|
||||
-- Misc info
|
||||
data.integration = get_integration()
|
||||
data.version = get_version()
|
||||
end
|
||||
data.remote_addr = ngx.var.remote_addr
|
||||
data.server_name = ngx.var.server_name
|
||||
if data.kind == "http" then
|
||||
data.uri = ngx.var.uri
|
||||
data.request_uri = ngx.var.request_uri
|
||||
data.request_method = ngx.var.request_method
|
||||
data.http_user_agent = ngx.var.http_user_agent
|
||||
data.http_host = ngx.var.http_host
|
||||
data.server_name = ngx.var.server_name
|
||||
data.http_content_type = ngx.var.http_content_type
|
||||
data.http_content_length = ngx.var.http_content_length
|
||||
data.http_origin = ngx.var.http_origin
|
||||
data.http_version = ngx.req.http_version()
|
||||
data.scheme = ngx.var.scheme
|
||||
end
|
||||
-- IP data : global
|
||||
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
|
||||
if ip_is_global == nil then
|
||||
table.insert(errors, "can't check if IP is global : " .. err)
|
||||
else
|
||||
data.ip_is_global = ip_is_global
|
||||
end
|
||||
-- IP data : v4 / v6
|
||||
data.ip_is_ipv4 = utils.is_ipv4(data.ip)
|
||||
data.ip_is_ipv6 = utils.is_ipv6(data.ip)
|
||||
-- Misc info
|
||||
data.integration = utils.get_integration()
|
||||
data.version = utils.get_version()
|
||||
-- Fill ctx
|
||||
ctx.bw = data
|
||||
end
|
||||
-- Always create new objects for current phases in case of cosockets
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
local use_redis, err = get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
table.insert(errors, "can't get variable from datastore : " .. err)
|
||||
end
|
||||
ctx.bw.datastore = require "bunkerweb.datastore":new()
|
||||
ctx.bw.clusterstore = require "bunkerweb.clusterstore":new()
|
||||
ctx.bw.cachestore = require "bunkerweb.cachestore":new(use_redis == "yes")
|
||||
ctx.bw.cachestore = require "bunkerweb.cachestore":new(use_redis == "yes", ctx)
|
||||
return true, "ctx filled", errors, ctx
|
||||
end
|
||||
|
||||
helpers.save_ctx = function(ctx)
|
||||
if base.get_request() then
|
||||
bwctx.stash_ref(ctx)
|
||||
if get_request() then
|
||||
stash_ref(ctx)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -222,11 +243,11 @@ function helpers.load_variables(all_variables, plugins)
|
|||
end
|
||||
end
|
||||
end
|
||||
local file = io.open("/usr/share/bunkerweb/settings.json")
|
||||
local file = open("/usr/share/bunkerweb/settings.json")
|
||||
if not file then
|
||||
return false, "can't open settings.json"
|
||||
end
|
||||
local ok, settings = pcall(cjson.decode, file:read("*a"))
|
||||
local ok, settings = pcall(decode, file:read("*a"))
|
||||
file:close()
|
||||
if not ok then
|
||||
return false, "invalid settings.json : " .. settings
|
||||
|
|
|
|||
|
|
@ -2,12 +2,15 @@ local class = require "middleclass"
|
|||
local errlog = require "ngx.errlog"
|
||||
local logger = class("logger")
|
||||
|
||||
local upper = string.upper
|
||||
local raw_log = errlog.raw_log
|
||||
|
||||
function logger:initialize(prefix)
|
||||
self.prefix = string.upper(prefix)
|
||||
self.prefix = upper(prefix)
|
||||
end
|
||||
|
||||
function logger:log(level, msg)
|
||||
errlog.raw_log(level, "[" .. self.prefix .. "] " .. msg)
|
||||
raw_log(level, "[" .. self.prefix .. "] " .. msg)
|
||||
end
|
||||
|
||||
return logger
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
local ngx = ngx
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local class = require "middleclass"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
|
|
@ -6,50 +7,55 @@ local logger = require "bunkerweb.logger"
|
|||
local utils = require "bunkerweb.utils"
|
||||
local plugin = class("plugin")
|
||||
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local get_variable = utils.get_variable
|
||||
local get_ctx_obj = utils.get_ctx_obj
|
||||
local subsystem = ngx.config.subsystem
|
||||
|
||||
function plugin:initialize(id, ctx)
|
||||
-- Store common, values
|
||||
self.id = id
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
local is_request = false
|
||||
local current_phase = get_phase()
|
||||
for _, check_phase in ipairs {
|
||||
"set",
|
||||
"rewrite",
|
||||
"access",
|
||||
"content",
|
||||
"header_filter",
|
||||
"body_filter",
|
||||
"log",
|
||||
"preread",
|
||||
"log_stream",
|
||||
"log_default",
|
||||
"preread"
|
||||
} do
|
||||
if current_phase == check_phase then
|
||||
multisite = true
|
||||
is_request = true
|
||||
break
|
||||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
self.is_request = is_request
|
||||
-- Store common objects
|
||||
self.logger = logger:new(self.id)
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
local use_redis, err = get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
if self.is_request then
|
||||
-- Store ctx
|
||||
self.ctx = ctx or ngx.ctx
|
||||
self.datastore = utils.get_ctx_obj("datastore", self.ctx) or datastore:new()
|
||||
self.cachestore = utils.get_ctx_obj("cachestore", self.ctx)
|
||||
or cachestore:new(use_redis == "yes", true, self.ctx)
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore", self.ctx) or clusterstore:new(false)
|
||||
self.datastore = get_ctx_obj("datastore", self.ctx) or datastore:new()
|
||||
self.cachestore = get_ctx_obj("cachestore", self.ctx)
|
||||
or cachestore:new(use_redis == "yes", self.ctx)
|
||||
self.clusterstore = get_ctx_obj("clusterstore", self.ctx) or clusterstore:new()
|
||||
else
|
||||
self.datastore = datastore:new()
|
||||
self.cachestore = cachestore:new(use_redis == "yes", true)
|
||||
self.cachestore = cachestore:new(use_redis == "yes")
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
end
|
||||
-- Get metadata
|
||||
local metadata, err = self.datastore:get("plugin_" .. id, true)
|
||||
if not metadata then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
return
|
||||
end
|
||||
-- Store variables
|
||||
|
|
@ -57,21 +63,22 @@ function plugin:initialize(id, ctx)
|
|||
self.multiples = {}
|
||||
local value
|
||||
for k, v in pairs(metadata.settings) do
|
||||
value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||
value, err = get_variable(k, v.context == "multisite" and self.is_request)
|
||||
if value == nil then
|
||||
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
|
||||
self.logger:log(ERR, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
self.variables[k] = value
|
||||
end
|
||||
-- Is loading
|
||||
local is_loading, err = utils.get_variable("IS_LOADING", false)
|
||||
local is_loading, err = get_variable("IS_LOADING", false)
|
||||
if is_loading == nil then
|
||||
self.logger:log(ngx.ERR, "can't get IS_LOADING variable : " .. err)
|
||||
self.logger:log(ERR, "can't get IS_LOADING variable : " .. err)
|
||||
end
|
||||
self.is_loading = is_loading == "yes"
|
||||
-- Kind of server
|
||||
self.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
if subsystem == "http" then
|
||||
self.kind = "http"
|
||||
else
|
||||
self.kind = "stream"
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
local ngx = ngx
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local mmdb = require "bunkerweb.mmdb"
|
||||
|
|
@ -10,11 +11,32 @@ local session = require "resty.session"
|
|||
local logger = clogger:new("UTILS")
|
||||
local datastore = cdatastore:new()
|
||||
|
||||
local var = ngx.var
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local WARN = ngx.WARN
|
||||
local null = ngx.null
|
||||
local re_match = ngx.re.match
|
||||
local subsystem = ngx.config.subsystem
|
||||
local get_phase = ngx.get_phase
|
||||
local kill = ngx.thread.kill
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local parse_ipv4 = ipmatcher.parse_ipv4
|
||||
local parse_ipv6 = ipmatcher.parse_ipv6
|
||||
local open = io.open
|
||||
local encode = cjson.encode
|
||||
local decode = cjson.decode
|
||||
local char = string.char
|
||||
local random = math.random
|
||||
local session_start = session.start
|
||||
local session_open = session.open
|
||||
local tonumber = tonumber
|
||||
|
||||
local utils = {}
|
||||
|
||||
math.randomseed(os.time())
|
||||
|
||||
utils.get_variable = function(var, site_search)
|
||||
utils.get_variable = function(variable, site_search, ctx)
|
||||
-- Default site search to true
|
||||
if site_search == nil then
|
||||
site_search = true
|
||||
|
|
@ -24,20 +46,27 @@ utils.get_variable = function(var, site_search)
|
|||
if not variables then
|
||||
return nil, "can't access variables from datastore : " .. err
|
||||
end
|
||||
local value = variables["global"][var]
|
||||
local value = variables["global"][variable]
|
||||
-- Site search case
|
||||
local multisite = site_search and variables["global"]["MULTISITE"] == "yes" and ngx.var.server_name ~= "_"
|
||||
if multisite then
|
||||
value = variables[ngx.var.server_name][var]
|
||||
if site_search and variables["global"]["MULTISITE"] == "yes" then
|
||||
local server_name
|
||||
if ctx and ctx.bw then
|
||||
server_name = ctx.bw.server_name
|
||||
else
|
||||
server_name = var.server_name
|
||||
end
|
||||
if variables[server_name] then
|
||||
value = variables[server_name][variable]
|
||||
end
|
||||
end
|
||||
return value, "success"
|
||||
end
|
||||
|
||||
utils.has_variable = function(var, value)
|
||||
utils.has_variable = function(variable, value)
|
||||
-- Get global variable
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||
return nil, "can't access variables " .. variable .. " from datastore : " .. err
|
||||
end
|
||||
-- Multisite case
|
||||
local multisite = variables["global"]["MULTISITE"] == "yes"
|
||||
|
|
@ -45,7 +74,7 @@ utils.has_variable = function(var, value)
|
|||
local servers = variables["global"]["SERVER_NAME"]
|
||||
-- Check each server
|
||||
for server in servers:gmatch("%S+") do
|
||||
if variables[server][var] == value then
|
||||
if variables[server][variable] == value then
|
||||
return true, "success"
|
||||
end
|
||||
end
|
||||
|
|
@ -53,14 +82,14 @@ utils.has_variable = function(var, value)
|
|||
return false, "success"
|
||||
end
|
||||
end
|
||||
return variables["global"][var] == value, "success"
|
||||
return variables["global"][variable] == value, "success"
|
||||
end
|
||||
|
||||
utils.has_not_variable = function(var, value)
|
||||
utils.has_not_variable = function(variable, value)
|
||||
-- Get global variable
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||
return nil, "can't access variables " .. variable .. " from datastore : " .. err
|
||||
end
|
||||
-- Multisite case
|
||||
local multisite = variables["global"]["MULTISITE"] == "yes"
|
||||
|
|
@ -68,7 +97,7 @@ utils.has_not_variable = function(var, value)
|
|||
local servers = variables["global"]["SERVER_NAME"]
|
||||
-- Check each server
|
||||
for server in servers:gmatch("%S+") do
|
||||
if variables[server][var] ~= "value" then
|
||||
if variables[server][variable] ~= "value" then
|
||||
return true, "success"
|
||||
end
|
||||
end
|
||||
|
|
@ -76,7 +105,7 @@ utils.has_not_variable = function(var, value)
|
|||
return false, "success"
|
||||
end
|
||||
end
|
||||
return variables["global"][var] ~= value, "success"
|
||||
return variables["global"][variable] ~= value, "success"
|
||||
end
|
||||
|
||||
utils.get_multiple_variables = function(vars)
|
||||
|
|
@ -90,8 +119,8 @@ utils.get_multiple_variables = function(vars)
|
|||
result[scope] = {}
|
||||
-- Loop on vars
|
||||
for variable, value in pairs(scoped_vars) do
|
||||
for _, var in ipairs(vars) do
|
||||
if variable:find("^" .. var .. "_?[0-9]*$") then
|
||||
for _, tvar in ipairs(vars) do
|
||||
if variable:find("^" .. tvar .. "_?[0-9]*$") then
|
||||
result[scope][variable] = value
|
||||
end
|
||||
end
|
||||
|
|
@ -102,7 +131,7 @@ end
|
|||
|
||||
utils.is_ip_in_networks = function(ip, networks)
|
||||
-- Instantiate ipmatcher
|
||||
local ipm, err = ipmatcher.new(networks)
|
||||
local ipm, err = ipmatcher_new(networks)
|
||||
if not ipm then
|
||||
return nil, "can't instantiate ipmatcher : " .. err
|
||||
end
|
||||
|
|
@ -115,11 +144,11 @@ utils.is_ip_in_networks = function(ip, networks)
|
|||
end
|
||||
|
||||
utils.is_ipv4 = function(ip)
|
||||
return ipmatcher.parse_ipv4(ip)
|
||||
return parse_ipv4(ip)
|
||||
end
|
||||
|
||||
utils.is_ipv6 = function(ip)
|
||||
return ipmatcher.parse_ipv6(ip)
|
||||
return parse_ipv6(ip)
|
||||
end
|
||||
|
||||
utils.ip_is_global = function(ip)
|
||||
|
|
@ -157,7 +186,7 @@ utils.ip_is_global = function(ip)
|
|||
"ff00::/8",
|
||||
}
|
||||
-- Instantiate ipmatcher
|
||||
local ipm, err = ipmatcher.new(reserved_ips)
|
||||
local ipm, err = ipmatcher_new(reserved_ips)
|
||||
if not ipm then
|
||||
return nil, "can't instantiate ipmatcher : " .. err
|
||||
end
|
||||
|
|
@ -177,7 +206,7 @@ utils.get_integration = function()
|
|||
end
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ngx.ERR, "can't get variables from datastore : " .. err)
|
||||
logger:log(ERR, "can't get variables from datastore : " .. err)
|
||||
return "unknown"
|
||||
end
|
||||
-- Swarm
|
||||
|
|
@ -193,12 +222,12 @@ utils.get_integration = function()
|
|||
integration = "autoconf"
|
||||
else
|
||||
-- Already present (e.g. : linux)
|
||||
local f, _ = io.open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||
local f, _ = open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||
if f then
|
||||
integration = f:read("*a"):gsub("[\n\r]", "")
|
||||
f:close()
|
||||
else
|
||||
f, _ = io.open("/etc/os-release", "r")
|
||||
f, _ = open("/etc/os-release", "r")
|
||||
if f then
|
||||
local data = f:read("*a")
|
||||
f:close()
|
||||
|
|
@ -217,7 +246,7 @@ utils.get_integration = function()
|
|||
-- Save integration
|
||||
local ok, err = datastore:set("misc_integration", integration, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't cache integration to datastore : " .. err)
|
||||
logger:log(ERR, "can't cache integration to datastore : " .. err)
|
||||
end
|
||||
return integration
|
||||
end
|
||||
|
|
@ -229,9 +258,9 @@ utils.get_version = function()
|
|||
return version
|
||||
end
|
||||
-- Read VERSION file
|
||||
local f, err = io.open("/usr/share/bunkerweb/VERSION", "r")
|
||||
local f, err = open("/usr/share/bunkerweb/VERSION", "r")
|
||||
if not f then
|
||||
logger:log(ngx.ERR, "can't read VERSION file : " .. err)
|
||||
logger:log(ERR, "can't read VERSION file : " .. err)
|
||||
return nil
|
||||
end
|
||||
version = f:read("*a"):gsub("[\n\r]", "")
|
||||
|
|
@ -239,36 +268,54 @@ utils.get_version = function()
|
|||
-- Save it to datastore
|
||||
local ok, err = datastore:set("misc_version", version, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't cache version to datastore : " .. err)
|
||||
logger:log(ERR, "can't cache version to datastore : " .. err)
|
||||
end
|
||||
return version
|
||||
end
|
||||
|
||||
utils.get_reason = function(ctx)
|
||||
-- ngx.ctx
|
||||
if ctx.bw.reason then
|
||||
if ctx and ctx.bw and ctx.bw.reason then
|
||||
return ctx.bw.reason
|
||||
end
|
||||
-- ngx.var
|
||||
if ngx.var.reason and ngx.var.reason ~= "" then
|
||||
return ngx.var.reason
|
||||
if var.reason and var.reason ~= "" then
|
||||
return var.reason
|
||||
end
|
||||
-- os.getenv
|
||||
if os.getenv("REASON") == "modsecurity" then
|
||||
return "modsecurity"
|
||||
end
|
||||
-- datastore ban
|
||||
local banned, _ = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
||||
local ip
|
||||
if ctx and ctx.bw then
|
||||
ip = ctx.bw.remote_addr
|
||||
else
|
||||
ip = var.remote_addr
|
||||
end
|
||||
local banned, _ = datastore:get("bans_ip_" .. ip)
|
||||
if banned then
|
||||
return banned
|
||||
end
|
||||
-- unknown
|
||||
if ngx.status == utils.get_deny_status(ctx) then
|
||||
if ngx.status == utils.get_deny_status() then
|
||||
return "unknown"
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
utils.is_whitelisted = function(ctx)
|
||||
-- ngx.ctx
|
||||
if ctx and ctx.bw and ctx.bw.is_whitelisted then
|
||||
return ctx.bw.is_whitelisted
|
||||
end
|
||||
-- ngx.var
|
||||
if var.is_whitelisted and var.is_whitelisted == "yes" then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
utils.get_resolvers = function()
|
||||
-- Get resolvers from datastore if existing
|
||||
local resolvers, _ = datastore:get("misc_resolvers", true)
|
||||
|
|
@ -278,7 +325,7 @@ utils.get_resolvers = function()
|
|||
-- Otherwise extract DNS_RESOLVERS variable
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ngx.ERR, "can't get variables from datastore : " .. err)
|
||||
logger:log(ERR, "can't get variables from datastore : " .. err)
|
||||
return "unknown"
|
||||
end
|
||||
-- Make table for resolver1 resolver2 ... string
|
||||
|
|
@ -289,19 +336,19 @@ utils.get_resolvers = function()
|
|||
-- Add it to the datastore
|
||||
local ok, err = datastore:set("misc_resolvers", resolvers, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save misc_resolvers to datastore : " .. err)
|
||||
logger:log(ERR, "can't save misc_resolvers to datastore : " .. err)
|
||||
end
|
||||
return resolvers
|
||||
end
|
||||
|
||||
utils.get_rdns = function(ip)
|
||||
utils.get_rdns = function(ip, ctx, pool)
|
||||
-- Check cache
|
||||
local cachestore = utils.new_cachestore()
|
||||
local cachestore = utils.new_cachestore(ctx, pool)
|
||||
local ok, value = cachestore:get("rdns_" .. ip)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't get rdns from cachestore : " .. value)
|
||||
logger:log(ERR, "can't get rdns from cachestore : " .. value)
|
||||
elseif value then
|
||||
return cjson.decode(value), "success"
|
||||
return decode(value), "success"
|
||||
end
|
||||
-- Get resolvers
|
||||
local resolvers, err = utils.get_resolvers()
|
||||
|
|
@ -323,7 +370,7 @@ utils.get_rdns = function(ip)
|
|||
-- Do rDNS query
|
||||
local answers, err = rdns:reverse_query(ip)
|
||||
if not answers then
|
||||
logger:log(ngx.ERR, "error while doing reverse DNS query for " .. ip .. " : " .. err)
|
||||
logger:log(ERR, "error while doing reverse DNS query for " .. ip .. " : " .. err)
|
||||
ret_err = err
|
||||
else
|
||||
if answers.errcode then
|
||||
|
|
@ -337,21 +384,21 @@ utils.get_rdns = function(ip)
|
|||
end
|
||||
end
|
||||
-- Save to cache
|
||||
ok, err = cachestore:set("rdns_" .. ip, cjson.encode(ptrs), 3600)
|
||||
ok, err = cachestore:set("rdns_" .. ip, encode(ptrs), 3600)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't set rdns into cachestore : " .. err)
|
||||
logger:log(ERR, "can't set rdns into cachestore : " .. err)
|
||||
end
|
||||
return ptrs, ret_err
|
||||
end
|
||||
|
||||
utils.get_ips = function(fqdn, ipv6)
|
||||
utils.get_ips = function(fqdn, ipv6, ctx, pool)
|
||||
-- Check cache
|
||||
local cachestore = utils.new_cachestore()
|
||||
local cachestore = utils.new_cachestore(ctx, pool)
|
||||
local ok, value = cachestore:get("dns_" .. fqdn)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't get dns from cachestore : " .. value)
|
||||
logger:log(ERR, "can't get dns from cachestore : " .. value)
|
||||
elseif value then
|
||||
return cjson.decode(value), "success"
|
||||
return decode(value), "success"
|
||||
end
|
||||
-- By default perform ipv6 lookups (only if USE_IPV6=yes)
|
||||
if ipv6 == nil then
|
||||
|
|
@ -377,7 +424,7 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
-- luacheck: ignore 421
|
||||
local use_ipv6, err = utils.get_variable("USE_IPV6", false)
|
||||
if not use_ipv6 then
|
||||
logger:log(ngx.ERR, "can't get USE_IPV6 variable " .. err)
|
||||
logger:log(ERR, "can't get USE_IPV6 variable " .. err)
|
||||
elseif use_ipv6 == "yes" then
|
||||
table.insert(qtypes, res.TYPE_AAAA)
|
||||
end
|
||||
|
|
@ -401,7 +448,7 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
end
|
||||
end
|
||||
for qtype, error in pairs(res_errors) do
|
||||
logger:log(ngx.ERR, "error while doing " .. qtype .. " DNS query for " .. fqdn .. " : " .. error)
|
||||
logger:log(ERR, "error while doing " .. qtype .. " DNS query for " .. fqdn .. " : " .. error)
|
||||
end
|
||||
-- Extract all IPs
|
||||
local ips = {}
|
||||
|
|
@ -414,11 +461,11 @@ utils.get_ips = function(fqdn, ipv6)
|
|||
end
|
||||
end
|
||||
-- Save to cache
|
||||
ok, err = cachestore:set("dns_" .. fqdn, cjson.encode(ips), 3600)
|
||||
ok, err = cachestore:set("dns_" .. fqdn, encode(ips), 3600)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't set dns into cachestore : " .. err)
|
||||
logger:log(ERR, "can't set dns into cachestore : " .. err)
|
||||
end
|
||||
return ips, cjson.encode(res_errors) .. " " .. cjson.encode(ans_errors)
|
||||
return ips, encode(res_errors) .. " " .. encode(ans_errors)
|
||||
end
|
||||
|
||||
utils.get_country = function(ip)
|
||||
|
|
@ -458,38 +505,36 @@ utils.rand = function(nb, no_numbers)
|
|||
-- lowers, uppers and numbers
|
||||
if not no_numbers then
|
||||
for i = 48, 57 do
|
||||
table.insert(charset, string.char(i))
|
||||
table.insert(charset, char(i))
|
||||
end
|
||||
end
|
||||
for i = 65, 90 do
|
||||
table.insert(charset, string.char(i))
|
||||
table.insert(charset, char(i))
|
||||
end
|
||||
for i = 97, 122 do
|
||||
table.insert(charset, string.char(i))
|
||||
table.insert(charset, char(i))
|
||||
end
|
||||
local result = ""
|
||||
for _ = 1, nb do
|
||||
result = result .. charset[math.random(1, #charset)]
|
||||
result = result .. charset[random(1, #charset)]
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
utils.get_deny_status = function(ctx)
|
||||
-- Stream case
|
||||
if ctx.bw and ctx.bw.kind == "stream" then
|
||||
return 444
|
||||
utils.get_deny_status = function()
|
||||
if subsystem == "http" then
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ERR, "can't get variables from datastore : " .. err)
|
||||
return 403
|
||||
end
|
||||
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
||||
end
|
||||
-- http case
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ngx.ERR, "can't get variables from datastore : " .. err)
|
||||
return 403
|
||||
end
|
||||
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
||||
return 444
|
||||
end
|
||||
|
||||
utils.check_session = function(ctx)
|
||||
local _session, _, exists, _ = session.start({ audience = "metadata" })
|
||||
local _session, _, exists, _ = session_start({ audience = "metadata" })
|
||||
if exists then
|
||||
for _, check in ipairs(ctx.bw.sessions_checks) do
|
||||
local key = check[1]
|
||||
|
|
@ -500,7 +545,7 @@ utils.check_session = function(ctx)
|
|||
if not ok then
|
||||
return false, "session:destroy() error : " .. err
|
||||
end
|
||||
logger:log(ngx.WARN, "session check " .. key .. " failed, destroying session")
|
||||
logger:log(WARN, "session check " .. key .. " failed, destroying session")
|
||||
return utils.check_session(ctx)
|
||||
end
|
||||
end
|
||||
|
|
@ -527,9 +572,9 @@ utils.get_session = function(audience, ctx)
|
|||
end
|
||||
end
|
||||
-- Open session with specific audience
|
||||
local _session, err, _ = session.open({ audience = audience })
|
||||
local _session, err, _ = session_open({ audience = audience })
|
||||
if err then
|
||||
logger:log(ngx.INFO, "session:open() error : " .. err)
|
||||
logger:log(INFO, "session:open() error : " .. err)
|
||||
end
|
||||
return _session
|
||||
end
|
||||
|
|
@ -607,7 +652,7 @@ utils.is_banned = function(ip)
|
|||
elseif data.err then
|
||||
clusterstore:close()
|
||||
return nil, "redis script error : " .. data.err
|
||||
elseif data[1] ~= ngx.null then
|
||||
elseif data[1] ~= null then
|
||||
clusterstore:close()
|
||||
-- Update local cache
|
||||
ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
|
||||
|
|
@ -649,16 +694,17 @@ utils.add_ban = function(ip, reason, ttl)
|
|||
return true, "success"
|
||||
end
|
||||
|
||||
utils.new_cachestore = function()
|
||||
utils.new_cachestore = function(ctx, pool)
|
||||
-- Check if redis is used
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
logger:log(ngx.ERR, "can't get USE_REDIS variable : " .. err)
|
||||
logger:log(ERR, "can't get USE_REDIS variable : " .. err)
|
||||
use_redis = false
|
||||
else
|
||||
use_redis = use_redis == "yes"
|
||||
end
|
||||
-- Instantiate
|
||||
return require "bunkerweb.cachestore":new(use_redis, true)
|
||||
return require "bunkerweb.cachestore":new(use_redis, ctx, pool == nil or pool)
|
||||
end
|
||||
|
||||
utils.regex_match = function(str, regex, options)
|
||||
|
|
@ -666,9 +712,9 @@ utils.regex_match = function(str, regex, options)
|
|||
if options then
|
||||
all_options = all_options .. options
|
||||
end
|
||||
local match, err = ngx.re.match(str, regex, all_options)
|
||||
local match, err = re_match(str, regex, all_options)
|
||||
if err then
|
||||
logger:log(ngx.ERR, "error while matching regex " .. regex .. "with string " .. str)
|
||||
logger:log(ERR, "error while matching regex " .. regex .. "with string " .. str)
|
||||
return nil
|
||||
end
|
||||
return match
|
||||
|
|
@ -680,6 +726,7 @@ utils.get_phases = function()
|
|||
"init_worker",
|
||||
"set",
|
||||
"access",
|
||||
"content",
|
||||
"ssl_certificate",
|
||||
"header",
|
||||
"log",
|
||||
|
|
@ -696,7 +743,7 @@ utils.is_cosocket_available = function()
|
|||
"ssl_certificate",
|
||||
"preread",
|
||||
}
|
||||
local current_phase = ngx.get_phase()
|
||||
local current_phase = get_phase()
|
||||
for _, phase in ipairs(phases) do
|
||||
if current_phase == phase then
|
||||
return true
|
||||
|
|
@ -707,16 +754,17 @@ end
|
|||
|
||||
utils.kill_all_threads = function(threads)
|
||||
for _, thread in ipairs(threads) do
|
||||
local ok, err = ngx.thread.kill(thread)
|
||||
local ok, err = kill(thread)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while killing thread : " .. err)
|
||||
logger:log(ERR, "error while killing thread : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.get_ctx_obj = function(obj)
|
||||
if ngx.ctx and ngx.ctx.bw then
|
||||
return ngx.ctx.bw[obj]
|
||||
utils.get_ctx_obj = function(obj, ctx)
|
||||
local vctx = ctx or ngx.ctx
|
||||
if vctx and vctx.bw then
|
||||
return vctx.bw[obj]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,53 +19,61 @@ server {
|
|||
local logger = require "bunkerweb.logger":new("API")
|
||||
local api = require "bunkerweb.api":new()
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local WARN = ngx.WARN
|
||||
local NOTICE = ngx.NOTICE
|
||||
local HTTP_CLOSE = ngx.HTTP_CLOSE
|
||||
local exit = ngx.exit
|
||||
local say = ngx.say
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local tostring = tostring
|
||||
|
||||
-- Start API handler
|
||||
logger:log(ngx.INFO, "API handler started")
|
||||
logger:log(INFO, "API handler 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 .. ")")
|
||||
|
||||
-- Check host header
|
||||
if not ctx.bw.http_host or ctx.bw.http_host ~= "{{ API_SERVER_NAME }}" then
|
||||
logger:log(ngx.WARN, "wrong Host header from IP " .. ctx.bw.remote_addr)
|
||||
return ngx.exit(ngx.HTTP_CLOSE)
|
||||
logger:log(WARN, "wrong Host header from IP " .. ctx.bw.remote_addr)
|
||||
return exit(HTTP_CLOSE)
|
||||
end
|
||||
|
||||
-- Check IP
|
||||
local ok, err = api:is_allowed_ip()
|
||||
if not ok then
|
||||
logger:log(ngx.WARN, "can't validate access from IP " .. ctx.bw.remote_addr .. " : " .. err)
|
||||
return ngx.exit(ngx.HTTP_CLOSE)
|
||||
logger:log(WARN, "can't validate access from IP " .. ctx.bw.remote_addr .. " : " .. err)
|
||||
return exit(HTTP_CLOSE)
|
||||
end
|
||||
logger:log(ngx.NOTICE, "validated access from IP " .. ctx.bw.remote_addr)
|
||||
logger:log(NOTICE, "validated access from IP " .. ctx.bw.remote_addr)
|
||||
|
||||
-- Do API call
|
||||
local ok, err, status, resp = api:do_api_call()
|
||||
if not ok then
|
||||
logger:log(ngx.WARN, "call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " failed : " .. err)
|
||||
logger:log(WARN, "call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " failed : " .. err)
|
||||
else
|
||||
logger:log(ngx.NOTICE, "successful call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " : " .. err)
|
||||
logger:log(NOTICE, "successful call from " .. ctx.bw.remote_addr .. " on " .. ctx.bw.uri .. " : " .. err)
|
||||
end
|
||||
|
||||
-- Start API handler
|
||||
logger:log(ngx.INFO, "API handler ended")
|
||||
|
||||
-- Save ctx
|
||||
ngx.ctx = ctx
|
||||
-- Stop API handler
|
||||
logger:log(INFO, "API handler ended")
|
||||
|
||||
-- Send response
|
||||
ngx.status = status
|
||||
ngx.say(resp)
|
||||
return ngx.exit(status)
|
||||
say(resp)
|
||||
return exit(status)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,70 +45,78 @@ server {
|
|||
local helpers = require "bunkerweb.helpers"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local tostring = tostring
|
||||
local get_reason = utils.get_reason
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
|
||||
-- Start log phase
|
||||
local logger = clogger:new("LOG-DEFAULT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "log_default phase started")
|
||||
logger:log(INFO, "log_default 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_default() methods
|
||||
logger:log(ngx.INFO, "calling log_default() methods of plugins ...")
|
||||
logger:log(INFO, "calling log_default() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.log_default) 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 method
|
||||
if plugin_lua.log_default ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
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_default")
|
||||
local ok, ret = call_plugin(plugin_obj, "log_default")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":log_default() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":log_default() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method log_default() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method log_default() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called log_default() methods of plugins")
|
||||
logger:log(INFO, "called log_default() methods of plugins")
|
||||
|
||||
-- Display reason at info level
|
||||
if ctx.reason then
|
||||
logger:log(ngx.INFO, "client was denied with reason : " .. reason)
|
||||
local reason = get_reason(ctx)
|
||||
if reason then
|
||||
logger:log(INFO, "client was denied with reason : " .. reason)
|
||||
end
|
||||
|
||||
-- Save ctx
|
||||
ngx.ctx = ctx
|
||||
|
||||
logger:log(ngx.INFO, "log_default phase ended")
|
||||
logger:log(INFO, "log_default phase ended")
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,57 +5,71 @@ init_by_lua_block {
|
|||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local popen = io.popen
|
||||
local open = io.open
|
||||
local load_plugin = helpers.load_plugin
|
||||
local load_variables = helpers.load_variables
|
||||
local order_plugins = helpers.order_plugins
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local encode = cjson.encode
|
||||
|
||||
-- Start init phase
|
||||
local logger = clogger:new("INIT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init phase started")
|
||||
logger:log(NOTICE, "init phase started")
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
logger:log(NOTICE, "deleting old keys from datastore ...")
|
||||
datastore:flush_lru()
|
||||
local data_keys = { "^plugin", "^misc_" }
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
logger:log(ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
|
||||
logger:log(INFO, "deleted " .. key .. " from datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "deleted old keys from datastore")
|
||||
logger:log(NOTICE, "deleted old keys from datastore")
|
||||
|
||||
-- Load plugins into the datastore
|
||||
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
|
||||
logger:log(NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins" }
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
local paths = popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
|
||||
local ok, plugin = load_plugin(path .. "/plugin.json")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin)
|
||||
logger:log(ERR, plugin)
|
||||
else
|
||||
local ok, err = datastore:set("plugin_" .. plugin.id, plugin, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
logger:log(ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
else
|
||||
table.insert(plugins, plugin)
|
||||
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
logger:log(NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local ok, err = datastore:set("plugins", plugins, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Load variables into the datastore
|
||||
logger:log(ngx.NOTICE, "saving variables into datastore ...")
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
logger:log(NOTICE, "saving variables into datastore ...")
|
||||
local file = open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
|
||||
logger:log(ERR, "can't open /etc/nginx/variables.env file")
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
|
|
@ -64,73 +78,73 @@ init_by_lua_block {
|
|||
local variable, value = line:match("^([^=]+)=(.*)$")
|
||||
all_variables[variable] = value
|
||||
end
|
||||
local ok, variables = helpers.load_variables(all_variables, plugins)
|
||||
local ok, variables = load_variables(all_variables, plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while loading variables : " .. variables)
|
||||
logger:log(ERR, "error while loading variables : " .. variables)
|
||||
return false
|
||||
end
|
||||
local ok, err = datastore:set("variables", variables, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
logger:log(NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local cachestore = require "bunkerweb.cachestore":new(false)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
logger:log(ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
logger:log(ngx.NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = helpers.order_plugins(plugins)
|
||||
logger:log(NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = order_plugins(plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't compute plugins order : " .. err)
|
||||
logger:log(ERR, "can't compute plugins order : " .. err)
|
||||
return false
|
||||
end
|
||||
for phase, id_list in pairs(order) do
|
||||
logger:log(ngx.NOTICE, "plugins order for phase " .. phase .. " : " .. cjson.encode(id_list))
|
||||
logger:log(NOTICE, "plugins order for phase " .. phase .. " : " .. encode(id_list))
|
||||
end
|
||||
local ok, err = datastore:set("plugins_order", order, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins order into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins order into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved plugins order into datastore")
|
||||
logger:log(NOTICE, "saved plugins order into datastore")
|
||||
|
||||
-- Call init() method
|
||||
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
|
||||
logger:log(NOTICE, "calling init() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order["init"]) 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.NOTICE, err)
|
||||
logger:log(NOTICE, err)
|
||||
else
|
||||
-- Check if plugin has init method
|
||||
if plugin_lua.init ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init")
|
||||
local ok, ret = call_plugin(plugin_obj, "init")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
logger:log(NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE, "called init() methods of plugins")
|
||||
logger:log(NOTICE, "called init() methods of plugins")
|
||||
|
||||
logger:log(ngx.NOTICE, "init phase ended")
|
||||
logger:log(NOTICE, "init phase ended")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,57 +5,71 @@ init_by_lua_block {
|
|||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local popen = io.popen
|
||||
local open = io.open
|
||||
local load_plugin = helpers.load_plugin
|
||||
local load_variables = helpers.load_variables
|
||||
local order_plugins = helpers.order_plugins
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local encode = cjson.encode
|
||||
|
||||
-- Start init phase
|
||||
local logger = clogger:new("INIT")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init-stream phase started")
|
||||
logger:log(NOTICE, "init-stream phase started")
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
logger:log(NOTICE, "deleting old keys from datastore ...")
|
||||
datastore:flush_lru()
|
||||
local data_keys = { "^plugin", "^misc_" }
|
||||
for i, key in pairs(data_keys) do
|
||||
local ok, err = datastore:delete_all(key)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
logger:log(ERR, "can't delete " .. key .. " from datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.INFO, "deleted " .. key .. " from datastore")
|
||||
logger:log(INFO, "deleted " .. key .. " from datastore")
|
||||
end
|
||||
logger:log(ngx.NOTICE, "deleted old keys from datastore")
|
||||
logger:log(NOTICE, "deleted old keys from datastore")
|
||||
|
||||
-- Load plugins into the datastore
|
||||
logger:log(ngx.NOTICE, "saving plugins into datastore ...")
|
||||
logger:log(NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins" }
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = io.popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
local paths = popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
local ok, plugin = helpers.load_plugin(path .. "/plugin.json")
|
||||
local ok, plugin = load_plugin(path .. "/plugin.json")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin)
|
||||
logger:log(ERR, plugin)
|
||||
else
|
||||
local ok, err = datastore:set("plugin_" .. plugin.id, plugin, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
logger:log(ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
|
||||
else
|
||||
table.insert(plugins, plugin)
|
||||
logger:log(ngx.NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
logger:log(NOTICE, "loaded plugin " .. plugin.id .. " v" .. plugin.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local ok, err = datastore:set("plugins", plugins, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Load variables into the datastore
|
||||
logger:log(ngx.NOTICE, "saving variables into datastore ...")
|
||||
local file = io.open("/etc/nginx/variables.env")
|
||||
local file = open("/etc/nginx/variables.env")
|
||||
if not file then
|
||||
logger:log(ngx.ERR, "can't open /etc/nginx/variables.env file")
|
||||
logger:log(ERR, "can't open /etc/nginx/variables.env file")
|
||||
return false
|
||||
end
|
||||
file:close()
|
||||
|
|
@ -64,73 +78,73 @@ init_by_lua_block {
|
|||
local variable, value = line:match("^([^=]+)=(.*)$")
|
||||
all_variables[variable] = value
|
||||
end
|
||||
local ok, variables = helpers.load_variables(all_variables, plugins)
|
||||
local ok, variables = load_variables(all_variables, plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while loading variables : " .. variables)
|
||||
logger:log(ERR, "error while loading variables : " .. variables)
|
||||
return false
|
||||
end
|
||||
local ok, err = datastore:set("variables", variables, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
logger:log(NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local cachestore = require "bunkerweb.cachestore":new(false)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
logger:log(ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
logger:log(ngx.NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = helpers.order_plugins(plugins)
|
||||
logger:log(NOTICE, "saving plugins order into datastore ...")
|
||||
local ok, order = order_plugins(plugins)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't compute plugins order : " .. err)
|
||||
logger:log(ERR, "can't compute plugins order : " .. err)
|
||||
return false
|
||||
end
|
||||
for phase, id_list in pairs(order) do
|
||||
logger:log(ngx.NOTICE, "plugins order for phase " .. phase .. " : " .. cjson.encode(id_list))
|
||||
logger:log(NOTICE, "plugins order for phase " .. phase .. " : " .. encode(id_list))
|
||||
end
|
||||
local ok, err = datastore:set("plugins_order", order, nil, true)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't save plugins order into datastore : " .. err)
|
||||
logger:log(ERR, "can't save plugins order into datastore : " .. err)
|
||||
return false
|
||||
end
|
||||
logger:log(ngx.NOTICE, "saved plugins order into datastore")
|
||||
logger:log(NOTICE, "saved plugins order into datastore")
|
||||
|
||||
-- Call init() method
|
||||
logger:log(ngx.NOTICE, "calling init() methods of plugins ...")
|
||||
logger:log(NOTICE, "calling init() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order["init"]) 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.NOTICE, err)
|
||||
logger:log(NOTICE, err)
|
||||
else
|
||||
-- Check if plugin has init method
|
||||
if plugin_lua.init ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init")
|
||||
local ok, ret = call_plugin(plugin_obj, "init")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":init() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. ":init() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
logger:log(NOTICE, "skipped execution of " .. plugin.id .. " because method init() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.NOTICE, "called init() methods of plugins")
|
||||
logger:log(NOTICE, "called init() methods of plugins")
|
||||
|
||||
logger:log(ngx.NOTICE, "init-stream phase ended")
|
||||
logger:log(NOTICE, "init-stream phase ended")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,16 +5,23 @@ init_worker_by_lua_block {
|
|||
local ready_work = function(premature)
|
||||
-- Libs
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cjson = require "cjson"
|
||||
|
||||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("INIT-WORKER")
|
||||
local datastore = require "bunkerweb.datastore":new()
|
||||
|
||||
local ngx = ngx
|
||||
local INFO = ngx.INFO
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
|
||||
-- Don't go further we are in loading state
|
||||
local is_loading, err = require "bunkerweb.utils".get_variable("IS_LOADING", false)
|
||||
if not is_loading then
|
||||
logger:log(ngx.ERR, "utils.get_variable() failed : " .. err)
|
||||
logger:log(ERR, "utils.get_variable() failed : " .. err)
|
||||
return
|
||||
elseif is_loading == "yes" then
|
||||
return
|
||||
|
|
@ -23,92 +30,92 @@ init_worker_by_lua_block {
|
|||
-- Instantiate lock
|
||||
local lock = require "resty.lock":new("worker_lock", { timeout = 10 })
|
||||
if not lock then
|
||||
logger:log(ngx.ERR, "lock:new() failed : " .. err)
|
||||
logger:log(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)
|
||||
logger:log(ERR, "lock:lock() failed : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if work is done
|
||||
local ok, err = datastore:get("misc_ready")
|
||||
if not ok and err ~= "not found" then
|
||||
logger:log(ngx.ERR, "datastore:get() failed : " .. err)
|
||||
logger:log(ERR, "datastore:get() failed : " .. err)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
if ok then
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
logger:log(ngx.INFO, "init_worker phase started")
|
||||
logger:log(INFO, "init_worker phase started")
|
||||
|
||||
-- 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)
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Call init_worker() methods
|
||||
logger:log(ngx.INFO, "calling init_worker() methods of plugins ...")
|
||||
logger:log(INFO, "calling init_worker() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.init_worker) 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 init_worker method
|
||||
if plugin_lua.init_worker ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "init_worker")
|
||||
local ok, ret = call_plugin(plugin_obj, "init_worker")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":init_worker() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":init_worker() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":init_worker() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":init_worker() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method init_worker() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method init_worker() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called init_worker() methods of plugins")
|
||||
logger:log(INFO, "called init_worker() methods of plugins")
|
||||
|
||||
-- End
|
||||
local ok, err = datastore:set("misc_ready", "ok")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "datastore:set() failed : " .. err)
|
||||
logger:log(ERR, "datastore:set() failed : " .. err)
|
||||
end
|
||||
local ok, err = lock:unlock()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "lock:unlock() failed : " .. err)
|
||||
logger:log(ERR, "lock:unlock() failed : " .. err)
|
||||
end
|
||||
logger:log(ngx.INFO, "init phase ended")
|
||||
logger:log(ngx.NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
|
||||
logger:log(INFO, "init phase ended")
|
||||
logger:log(NOTICE, "BunkerWeb is ready to fool hackers ! 🚀")
|
||||
end
|
||||
|
||||
-- Start timer
|
||||
|
|
|
|||
|
|
@ -7,114 +7,134 @@ access_by_lua_block {
|
|||
local cclusterstore = require "bunkerweb.clusterstore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local is_internal = ngx_req.is_internal
|
||||
local exit = ngx.exit
|
||||
local ngx_redirect = ngx.redirect
|
||||
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 get_deny_status = utils.get_deny_status
|
||||
local tostring = tostring
|
||||
|
||||
-- Don't process internal requests
|
||||
local logger = clogger:new("ACCESS")
|
||||
if ngx.req.is_internal() then
|
||||
logger:log(ngx.INFO, "skipped access phase because request is internal")
|
||||
if is_internal() then
|
||||
logger:log(INFO, "skipped access phase because request is internal")
|
||||
return true
|
||||
end
|
||||
|
||||
-- Start access phase
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "access phase started")
|
||||
logger:log(INFO, "access 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)
|
||||
logger:log(ngx.WARN,
|
||||
ctx.bw.reason = reason
|
||||
save_ctx(ctx)
|
||||
logger:log(WARN,
|
||||
"IP " .. ctx.bw.remote_addr .. " is banned with reason " .. reason .. " (" .. tostring(ttl) .. "s remaining)")
|
||||
return ngx.exit(utils.get_deny_status(ctx))
|
||||
return 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 access() methods
|
||||
logger:log(ngx.INFO, "calling access() methods of plugins ...")
|
||||
logger:log(INFO, "calling access() methods of plugins ...")
|
||||
local status = nil
|
||||
local redirect = nil
|
||||
for i, plugin_id in ipairs(order.access) 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 access method
|
||||
if plugin_lua.access ~= 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, "access")
|
||||
local ok, ret = call_plugin(plugin_obj, "access")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":access() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":access() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":access() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":access() call successful : " .. ret.msg)
|
||||
end
|
||||
if ret.status then
|
||||
if ret.status == utils.get_deny_status(ctx) then
|
||||
if ret.status == get_deny_status() then
|
||||
ctx.bw.reason = plugin_id
|
||||
logger:log(ngx.WARN, "denied access from " .. plugin_id .. " : " .. ret.msg)
|
||||
logger:log(WARN, "denied access 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
|
||||
elseif ret.redirect then
|
||||
logger:log(ngx.NOTICE, plugin_id .. " redirect to " .. ret.redirect .. " : " .. ret.msg)
|
||||
logger:log(NOTICE, plugin_id .. " redirect to " .. ret.redirect .. " : " .. ret.msg)
|
||||
redirect = ret.redirect
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method access() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method access() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called access() methods of plugins")
|
||||
logger:log(INFO, "called access() methods of plugins")
|
||||
|
||||
-- Save ctx
|
||||
helpers.save_ctx(ctx)
|
||||
save_ctx(ctx)
|
||||
|
||||
logger:log(ngx.INFO, "access phase ended")
|
||||
logger:log(INFO, "access phase ended")
|
||||
|
||||
-- Return status if needed
|
||||
if status then
|
||||
return ngx.exit(status)
|
||||
return exit(status)
|
||||
end
|
||||
|
||||
-- Redirect if needed
|
||||
if redirect then
|
||||
return ngx.redirect(redirect)
|
||||
return ngx_redirect(redirect)
|
||||
end
|
||||
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -1,69 +1,77 @@
|
|||
header_filter_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
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 tostring = tostring
|
||||
|
||||
-- Start set phase
|
||||
local logger = clogger:new("HEADER")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "header phase started")
|
||||
logger:log(INFO, "header 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 header() methods
|
||||
logger:log(ngx.INFO, "calling header() methods of plugins ...")
|
||||
logger:log(INFO, "calling header() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.header) 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 header method
|
||||
if plugin_lua.header ~= 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, "header")
|
||||
local ok, ret = call_plugin(plugin_obj, "header")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":header() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":header() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":header() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":header() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method header() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method header() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called header() methods of plugins")
|
||||
logger:log(INFO, "called header() methods of plugins")
|
||||
|
||||
-- Save ctx
|
||||
helpers.save_ctx(ctx)
|
||||
save_ctx(ctx)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,71 +1,81 @@
|
|||
log_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
--log_by_lua_block {
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local cjson = require "cjson"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
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
|
||||
|
||||
-- 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() methods
|
||||
logger:log(ngx.INFO, "calling log() methods of plugins ...")
|
||||
logger:log(INFO, "calling log() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.log) 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 method
|
||||
if plugin_lua.log ~= 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")
|
||||
local ok, ret = call_plugin(plugin_obj, "log")
|
||||
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() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":log() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":log() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":log() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method log() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method log() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called log() methods of plugins")
|
||||
logger:log(INFO, "called log() 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 = get_reason(ctx)
|
||||
if reason then
|
||||
logger:log(INFO, "client was denied with reason : " .. reason)
|
||||
end
|
||||
|
||||
logger:log(ngx.INFO, "log phase ended")
|
||||
logger:log(INFO, "log phase ended")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,46 +1,56 @@
|
|||
set $dummy_set "";
|
||||
set_by_lua_block $dummy_set {
|
||||
local class = require "middleclass"
|
||||
--set $dummy_set "";
|
||||
--set_by_lua_block $dummy_set {
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local ccachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local is_internal = ngx_req.is_internal
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
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 tostring = tostring
|
||||
|
||||
-- Don't process internal requests
|
||||
local logger = clogger:new("SET")
|
||||
if ngx.req.is_internal() then
|
||||
logger:log(ngx.INFO, "skipped set phase because request is internal")
|
||||
if is_internal() then
|
||||
logger:log(INFO, "skipped set phase because request is internal")
|
||||
return true
|
||||
end
|
||||
|
||||
-- Start set phase
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "set phase started")
|
||||
logger:log(INFO, "set phase started")
|
||||
|
||||
-- Update cachestore only once and before any other code
|
||||
local cachestore = ccachestore:new()
|
||||
local cachestore = ccachestore:new(false)
|
||||
local ok, err = cachestore.cache:update()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't update cachestore : " .. err)
|
||||
logger:log(ERR, "can't update cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- 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
|
||||
|
||||
|
|
@ -48,37 +58,37 @@ set_by_lua_block $dummy_set {
|
|||
logger:log(ngx.INFO, "calling set() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.set) 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 set method
|
||||
if plugin_lua.set ~= 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, "set")
|
||||
local ok, ret = call_plugin(plugin_obj, "set")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":set() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":set() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":set() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":set() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method set() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method set() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called set() methods of plugins")
|
||||
logger:log(INFO, "called set() methods of plugins")
|
||||
|
||||
-- Save ctx
|
||||
helpers.save_ctx(ctx)
|
||||
save_ctx(ctx)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,51 +27,63 @@ ssl_certificate_by_lua_block {
|
|||
local cjson = require "cjson"
|
||||
local ssl = require "ngx.ssl"
|
||||
|
||||
local ngx = ngx
|
||||
local ngx_req = ngx.req
|
||||
local is_internal = ngx_req.is_internal
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local set_cert = ssl.set_cert
|
||||
local set_priv_key = ssl.set_priv_key
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local tostring = tostring
|
||||
|
||||
-- Start ssl_certificate phase
|
||||
local logger = clogger:new("SSL-CERTIFICATE")
|
||||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.INFO, "ssl_certificate phase started")
|
||||
logger:log(INFO, "ssl_certificate phase started")
|
||||
|
||||
-- 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 ssl_certificate() methods
|
||||
logger:log(ngx.INFO, "calling ssl_certificate() methods of plugins ...")
|
||||
logger:log(INFO, "calling ssl_certificate() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.ssl_certificate) 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 ssl_certificate method
|
||||
if plugin_lua.ssl_certificate ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = helpers.new_plugin(plugin_lua)
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, plugin_obj)
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = helpers.call_plugin(plugin_obj, "ssl_certificate")
|
||||
local ok, ret = call_plugin(plugin_obj, "ssl_certificate")
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, ret)
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ngx.ERR, plugin_id .. ":ssl_certificate() call failed : " .. ret.msg)
|
||||
logger:log(ERR, plugin_id .. ":ssl_certificate() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, plugin_id .. ":ssl_certificate() call successful : " .. ret.msg)
|
||||
logger:log(INFO, plugin_id .. ":ssl_certificate() call successful : " .. ret.msg)
|
||||
if ret.status then
|
||||
logger:log(ngx.INFO, plugin_id .. " is setting certificate/key : " .. ret.msg)
|
||||
local ok, err = ssl.set_cert(ret.status[1])
|
||||
logger:log(INFO, plugin_id .. " is setting certificate/key : " .. ret.msg)
|
||||
local ok, err = set_cert(ret.status[1])
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while setting certificate : " .. err)
|
||||
logger:log(ERR, "error while setting certificate : " .. err)
|
||||
else
|
||||
local ok, err = ssl.set_priv_key(ret.status[2])
|
||||
local ok, err = set_priv_key(ret.status[2])
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while setting private key : " .. err)
|
||||
logger:log(ERR, "error while setting private key : " .. err)
|
||||
else
|
||||
return true
|
||||
end
|
||||
|
|
@ -80,13 +92,13 @@ ssl_certificate_by_lua_block {
|
|||
end
|
||||
end
|
||||
else
|
||||
logger:log(ngx.INFO, "skipped execution of " .. plugin_id .. " because method ssl_certificate() is not defined")
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method ssl_certificate() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(ngx.INFO, "called ssl_certificate() methods of plugins")
|
||||
logger:log(INFO, "called ssl_certificate() methods of plugins")
|
||||
|
||||
logger:log(ngx.INFO, "ssl_certificate phase ended")
|
||||
logger:log(INFO, "ssl_certificate phase ended")
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,30 @@ local plugin = require "bunkerweb.plugin"
|
|||
local sha256 = require "resty.sha256"
|
||||
local str = require "resty.string"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local ngx = ngx
|
||||
local subsystem = ngx.config.subsystem
|
||||
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
|
||||
local OK = ngx.OK
|
||||
local tonumber = tonumber
|
||||
local tostring = tostring
|
||||
local get_session = utils.get_session
|
||||
local get_session_data = utils.get_session_data
|
||||
local set_session_data = utils.set_session_data
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local rand = utils.rand
|
||||
local now = ngx.now
|
||||
local captcha_new = captcha.new
|
||||
local base64_encode = base64.encode
|
||||
local to_hex = str.to_hex
|
||||
local http_new = http.new
|
||||
local decode = cjson.decode
|
||||
|
||||
local template = nil
|
||||
if ngx.shared.datastore then
|
||||
local render = nil
|
||||
if subsystem == "http" then
|
||||
template = require "resty.template"
|
||||
render = template.render
|
||||
end
|
||||
|
||||
local antibot = class("antibot", plugin)
|
||||
|
|
@ -30,12 +51,12 @@ function antibot:header()
|
|||
end
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot", self.ctx)
|
||||
local session, err = get_session("antibot", self.ctx)
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't get session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session, true, self.ctx)
|
||||
self.session_data = get_session_data(self.session, true, self.ctx)
|
||||
-- Check if session is valid
|
||||
self:check_session()
|
||||
|
||||
|
|
@ -48,7 +69,7 @@ function antibot:header()
|
|||
end
|
||||
|
||||
if self.ctx.bw.uri ~= self.variables["ANTIBOT_URI"] then
|
||||
return self:ret(true, "Not antibot uri")
|
||||
return self:ret(true, "not antibot uri")
|
||||
end
|
||||
|
||||
local header = "Content-Security-Policy"
|
||||
|
|
@ -97,12 +118,12 @@ function antibot:access()
|
|||
end
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot", self.ctx)
|
||||
local session, err = get_session("antibot", self.ctx)
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't get session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session, true, self.ctx)
|
||||
self.session_data = get_session_data(self.session, true, self.ctx)
|
||||
-- Check if session is valid
|
||||
self:check_session()
|
||||
|
||||
|
|
@ -118,7 +139,7 @@ function antibot:access()
|
|||
self:prepare_challenge()
|
||||
local ok, err = self:set_session_data()
|
||||
if not ok then
|
||||
return self:ret(false, "can't save session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't save session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
|
||||
-- Redirect to challenge page
|
||||
|
|
@ -143,10 +164,10 @@ function antibot:access()
|
|||
local ok, err, redirect = self:check_challenge()
|
||||
local set_ok, set_err = self:set_session_data()
|
||||
if not set_ok then
|
||||
return self:ret(false, "can't save session : " .. set_err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't save session : " .. set_err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
if ok == nil then
|
||||
return self:ret(false, "check challenge error : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "check challenge error : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
elseif not ok then
|
||||
self.logger:log(ngx.WARN, "client failed challenge : " .. err)
|
||||
end
|
||||
|
|
@ -156,14 +177,14 @@ function antibot:access()
|
|||
self:prepare_challenge()
|
||||
ok, err = self:set_session_data()
|
||||
if not ok then
|
||||
return self:ret(false, "can't save session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't save session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.ctx.bw.antibot_display_content = true
|
||||
return self:ret(true, "displaying challenge to client", ngx.OK)
|
||||
return self:ret(true, "displaying challenge to client", OK)
|
||||
end
|
||||
|
||||
-- Method is suspicious, let's deny the request
|
||||
return self:ret(true, "unsupported HTTP method for antibot", utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "unsupported HTTP method for antibot", get_deny_status())
|
||||
end
|
||||
|
||||
function antibot:content()
|
||||
|
|
@ -178,12 +199,12 @@ function antibot:content()
|
|||
end
|
||||
|
||||
-- Get session data
|
||||
local session, err = utils.get_session("antibot", self.ctx)
|
||||
local session, err = get_session("antibot", self.ctx)
|
||||
if not session then
|
||||
return self:ret(false, "can't get session : " .. err, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||
return self:ret(false, "can't get session : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.session = session
|
||||
self.session_data = utils.get_session_data(self.session, true, self.ctx)
|
||||
self.session_data = get_session_data(self.session, true, self.ctx)
|
||||
|
||||
-- Direct access without session
|
||||
if not self.session_data.prepared then
|
||||
|
|
@ -228,7 +249,7 @@ end
|
|||
|
||||
function antibot:set_session_data()
|
||||
if self.session_updated then
|
||||
local ok, err = utils.set_session_data(self.session, self.session_data, true, self.ctx)
|
||||
local ok, err = set_session_data(self.session, self.session_data, true, self.ctx)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
|
|
@ -246,18 +267,18 @@ function antibot:prepare_challenge()
|
|||
self.session_data.type = self.variables["USE_ANTIBOT"]
|
||||
self.session_data.resolved = false
|
||||
self.session_data.original_uri = self.ctx.bw.request_uri
|
||||
self.session_data.nonce_script = utils.rand(16)
|
||||
self.session_data.nonce_style = utils.rand(16)
|
||||
self.session_data.nonce_script = rand(16)
|
||||
self.session_data.nonce_style = rand(16)
|
||||
if self.ctx.bw.uri == self.variables["ANTIBOT_URI"] then
|
||||
self.session_data.original_uri = "/"
|
||||
end
|
||||
if self.session_data.type == "cookie" then
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
elseif self.session_data.type == "javascript" then
|
||||
self.session_data.random = utils.rand(20)
|
||||
self.session_data.random = rand(20)
|
||||
elseif self.session_data.type == "captcha" then
|
||||
self.session_data.captcha = utils.rand(6, true)
|
||||
self.session_data.captcha = rand(6, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -282,11 +303,11 @@ function antibot:display_challenge()
|
|||
|
||||
-- Captcha case
|
||||
if self.session_data.type == "captcha" then
|
||||
local chall_captcha = captcha.new()
|
||||
local chall_captcha = captcha_new()
|
||||
chall_captcha:font("/usr/share/bunkerweb/core/antibot/files/font.ttf")
|
||||
chall_captcha:string(self.session_data.captcha)
|
||||
chall_captcha:generate()
|
||||
template_vars.captcha = base64.encode(chall_captcha:jpegStr(70))
|
||||
template_vars.captcha = base64_encode(chall_captcha:jpegStr(70))
|
||||
end
|
||||
|
||||
-- reCAPTCHA case
|
||||
|
|
@ -305,7 +326,7 @@ function antibot:display_challenge()
|
|||
end
|
||||
|
||||
-- Render content
|
||||
template.render(self.session_data.type .. ".html", template_vars)
|
||||
render(self.session_data.type .. ".html", template_vars)
|
||||
|
||||
return true, "displayed challenge"
|
||||
end
|
||||
|
|
@ -317,33 +338,35 @@ function antibot:check_challenge()
|
|||
end
|
||||
|
||||
local resolved
|
||||
|
||||
local ngx_req = ngx.req
|
||||
local read_body = ngx_req.read_body
|
||||
local get_post_args = ngx_req.get_post_args
|
||||
self.session_data.prepared = false
|
||||
self.session_updated = true
|
||||
|
||||
-- Javascript case
|
||||
if self.session_data.type == "javascript" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["challenge"] then
|
||||
return nil, "missing challenge arg"
|
||||
end
|
||||
local hash = sha256:new()
|
||||
hash:update(self.session_data.random .. args["challenge"])
|
||||
local digest = hash:final()
|
||||
resolved = str.to_hex(digest):find("^0000") ~= nil
|
||||
resolved = to_hex(digest):find("^0000") ~= nil
|
||||
if not resolved then
|
||||
return false, "wrong value"
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- Captcha case
|
||||
if self.session_data.type == "captcha" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["captcha"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
|
|
@ -351,18 +374,18 @@ function antibot:check_challenge()
|
|||
return false, "wrong value", nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- reCAPTCHA case
|
||||
if self.session_data.type == "recaptcha" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return nil, "can't instantiate http object : " .. err, nil, nil
|
||||
end
|
||||
|
|
@ -382,7 +405,7 @@ function antibot:check_challenge()
|
|||
if not res then
|
||||
return nil, "can't send request to reCAPTCHA API : " .. err, nil
|
||||
end
|
||||
local ok, rdata = pcall(cjson.decode, res.body)
|
||||
local ok, rdata = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from reCAPTCHA API : " .. rdata, nil
|
||||
end
|
||||
|
|
@ -390,18 +413,18 @@ function antibot:check_challenge()
|
|||
return false, "client failed challenge with score " .. tostring(rdata.score), nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- hCaptcha case
|
||||
if self.session_data.type == "hcaptcha" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return nil, "can't instantiate http object : " .. err, nil, nil
|
||||
end
|
||||
|
|
@ -421,7 +444,7 @@ function antibot:check_challenge()
|
|||
if not res then
|
||||
return nil, "can't send request to hCaptcha API : " .. err, nil
|
||||
end
|
||||
local ok, hdata = pcall(cjson.decode, res.body)
|
||||
local ok, hdata = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from hCaptcha API : " .. hdata, nil
|
||||
end
|
||||
|
|
@ -429,18 +452,18 @@ function antibot:check_challenge()
|
|||
return false, "client failed challenge", nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
-- Turnstile case
|
||||
if self.session_data.type == "turnstile" then
|
||||
ngx.req.read_body()
|
||||
local args, err = ngx.req.get_post_args(1)
|
||||
read_body()
|
||||
local args, err = get_post_args(1)
|
||||
if err == "truncated" or not args or not args["token"] then
|
||||
return nil, "missing challenge arg", nil
|
||||
end
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return nil, "can't instantiate http object : " .. err, nil, nil
|
||||
end
|
||||
|
|
@ -460,7 +483,7 @@ function antibot:check_challenge()
|
|||
if not res then
|
||||
return nil, "can't send request to Turnstile API : " .. err, nil
|
||||
end
|
||||
local ok, tdata = pcall(cjson.decode, res.body)
|
||||
local ok, tdata = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return nil, "error while decoding JSON from Turnstile API : " .. tdata, nil
|
||||
end
|
||||
|
|
@ -468,7 +491,7 @@ function antibot:check_challenge()
|
|||
return false, "client failed challenge", nil
|
||||
end
|
||||
self.session_data.resolved = true
|
||||
self.session_data.time_valid = ngx.now()
|
||||
self.session_data.time_valid = now()
|
||||
return true, "resolved", self.session_data.original_uri
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -5,22 +5,30 @@ location {{ ANTIBOT_URI }} {
|
|||
content_by_lua_block {
|
||||
local logger = require "bunkerweb.logger":new("ANTIBOT")
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local ok, ret, errors, ctx = helpers.fill_ctx()
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local save_ctx = helpers.save_ctx
|
||||
local tostring = tostring
|
||||
|
||||
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
|
||||
local antibot = require "antibot.antibot":new(ctx)
|
||||
local ret = antibot:content()
|
||||
if not ret.ret then
|
||||
logger:log(ngx.ERR, "antibot:content() failed : " .. ret.msg)
|
||||
logger:log(ERR, "antibot:content() failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(ngx.INFO, "antibot:content() success : " .. ret.msg)
|
||||
logger:log(INFO, "antibot:content() success : " .. ret.msg)
|
||||
end
|
||||
ngx.ctx = ctx
|
||||
save_ctx(ctx)
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local badbehavior = class("badbehavior", plugin)
|
||||
|
||||
local ngx
|
||||
local ERR = ngx.ERR
|
||||
local WARN = ngx.WARN
|
||||
local NOTICE = ngx.NOTICE
|
||||
local timer_at = ngx.timer.at
|
||||
local add_ban = utils.add_ban
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local is_banned = utils.is_banned
|
||||
local tostring = tostring
|
||||
|
||||
function badbehavior:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "badbehavior", ctx)
|
||||
|
|
@ -11,7 +21,7 @@ end
|
|||
|
||||
function badbehavior:log()
|
||||
-- Check if we are whitelisted
|
||||
if self.ctx.bw.is_whitelisted == "yes" then
|
||||
if is_whitelisted(self.ctx) == "yes" then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Check if bad behavior is activated
|
||||
|
|
@ -23,11 +33,11 @@ function badbehavior:log()
|
|||
return self:ret(true, "not increasing counter")
|
||||
end
|
||||
-- Check if we are already banned
|
||||
if self.ctx.bw.is_banned then
|
||||
if is_banned(self.ctx.bw.remote_addr) then
|
||||
return self:ret(true, "already banned")
|
||||
end
|
||||
-- Call increase function later and with cosocket enabled
|
||||
local ok, err = ngx.timer.at(
|
||||
local ok, err = timer_at(
|
||||
0,
|
||||
badbehavior.increase,
|
||||
self.ctx.bw.remote_addr,
|
||||
|
|
@ -55,13 +65,14 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("badbehavior")
|
||||
local datastore = require "bunkerweb.datastore":new()
|
||||
|
||||
-- Declare counter
|
||||
local counter = false
|
||||
-- Redis case
|
||||
if use_redis then
|
||||
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)
|
||||
logger:log(ERR, "(increase) redis_increase failed, falling back to local : " .. err)
|
||||
else
|
||||
counter = redis_counter
|
||||
end
|
||||
|
|
@ -70,7 +81,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
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(ERR, "(increase) can't get counts from the datastore : " .. err)
|
||||
end
|
||||
if local_counter == nil then
|
||||
local_counter = 0
|
||||
|
|
@ -78,25 +89,25 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
counter = local_counter + 1
|
||||
end
|
||||
-- Call decrease later
|
||||
local ok, err = ngx.timer.at(count_time, badbehavior.decrease, ip, count_time, threshold, use_redis)
|
||||
local ok, err = timer_at(count_time, badbehavior.decrease, ip, count_time, threshold, use_redis)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(increase) can't create decrease timer : " .. err)
|
||||
logger:log(ERR, "(increase) can't create decrease timer : " .. err)
|
||||
end
|
||||
-- Store local counter
|
||||
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(ERR, "(increase) can't save counts to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
-- Store local ban
|
||||
if counter > threshold then
|
||||
ok, err = utils.add_ban(ip, "bad behavior", ban_time)
|
||||
ok, err = add_ban(ip, "bad behavior", ban_time)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(increase) can't save ban : " .. err)
|
||||
logger:log(ERR, "(increase) can't save ban : " .. err)
|
||||
return
|
||||
end
|
||||
logger:log(
|
||||
ngx.WARN,
|
||||
WARN,
|
||||
"IP "
|
||||
.. ip
|
||||
.. " is banned for "
|
||||
|
|
@ -109,7 +120,7 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
|||
)
|
||||
end
|
||||
logger:log(
|
||||
ngx.NOTICE,
|
||||
NOTICE,
|
||||
"increased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||
)
|
||||
end
|
||||
|
|
@ -124,7 +135,7 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
if use_redis then
|
||||
local redis_counter, err = badbehavior.redis_decrease(ip, count_time)
|
||||
if not redis_counter then
|
||||
logger:log(ngx.ERR, "(decrease) redis_decrease failed, falling back to local : " .. err)
|
||||
logger:log(ERR, "(decrease) redis_decrease failed, falling back to local : " .. err)
|
||||
else
|
||||
counter = redis_counter
|
||||
end
|
||||
|
|
@ -133,7 +144,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, "(decrease) can't get counts from the datastore : " .. err)
|
||||
logger:log(ERR, "(decrease) can't get counts from the datastore : " .. err)
|
||||
end
|
||||
if local_counter == nil or local_counter <= 1 then
|
||||
counter = 0
|
||||
|
|
@ -148,19 +159,19 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
|||
else
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "(decrease) can't save counts to the datastore : " .. err)
|
||||
logger:log(ERR, "(decrease) can't save counts to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
end
|
||||
logger:log(
|
||||
ngx.NOTICE,
|
||||
NOTICE,
|
||||
"decreased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||
)
|
||||
end
|
||||
|
||||
function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_incr = redis.pcall("INCR", KEYS[1])
|
||||
|
|
@ -201,7 +212,7 @@ end
|
|||
|
||||
function badbehavior.redis_decrease(ip, count_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_decr = redis.pcall("DECR", KEYS[1])
|
||||
|
|
|
|||
|
|
@ -5,14 +5,26 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local blacklist = class("blacklist", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local get_rdns = utils.get_rdns
|
||||
local get_asn = utils.get_asn
|
||||
local regex_match = utils.regex_match
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
|
||||
function blacklist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "blacklist", ctx)
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_blacklist_lists", true)
|
||||
if not lists then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
self.lists = {}
|
||||
else
|
||||
self.lists = lists
|
||||
|
|
@ -50,9 +62,9 @@ function blacklist:is_needed()
|
|||
return self.variables["USE_BLACKLIST"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_BLACKLIST", "yes")
|
||||
local is_needed, err = has_variable("USE_BLACKLIST", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_BLACKLIST variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_BLACKLIST variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -78,7 +90,7 @@ function blacklist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(blacklists) do
|
||||
local f, _ = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||
local f, _ = open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(blacklists[kind], line)
|
||||
|
|
@ -118,12 +130,12 @@ function blacklist:access()
|
|||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
self.logger:log(ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return self:ret(
|
||||
true,
|
||||
k .. " is in cached blacklist (info : " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
if ok and cached then
|
||||
|
|
@ -139,18 +151,18 @@ function blacklist:access()
|
|||
if not already_cached[k] then
|
||||
local ok, blacklisted = self:is_blacklisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
|
||||
self.logger:log(ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
|
||||
else
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), blacklisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
self.logger:log(ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if blacklisted ~= "ok" then
|
||||
return self:ret(
|
||||
true,
|
||||
k .. " is blacklisted (info : " .. blacklisted .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -204,7 +216,7 @@ end
|
|||
|
||||
function blacklist:is_blacklisted_ip()
|
||||
-- Check if IP is in ignore list
|
||||
local ipm, err = ipmatcher.new(self.lists["IGNORE_IP"])
|
||||
local ipm, err = ipmatcher_new(self.lists["IGNORE_IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -235,7 +247,7 @@ function blacklist:is_blacklisted_ip()
|
|||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
local rdns_list, err = get_rdns(self.ctx.bw.remote_addr, self.ctx, true)
|
||||
if rdns_list then
|
||||
-- Check if rDNS is in ignore list
|
||||
local ignore = false
|
||||
|
|
@ -258,13 +270,13 @@ function blacklist:is_blacklisted_ip()
|
|||
end
|
||||
end
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in ignore list
|
||||
if self.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(self.ctx.bw.remote_addr)
|
||||
local asn, err = get_asn(self.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
|
|
@ -294,7 +306,7 @@ function blacklist:is_blacklisted_uri()
|
|||
-- Check if URI is in ignore list
|
||||
local ignore = false
|
||||
for _, ignore_uri in ipairs(self.lists["IGNORE_URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, ignore_uri) then
|
||||
if regex_match(self.ctx.bw.uri, ignore_uri) then
|
||||
ignore = true
|
||||
break
|
||||
end
|
||||
|
|
@ -302,7 +314,7 @@ function blacklist:is_blacklisted_uri()
|
|||
-- Check if URI is in blacklist
|
||||
if not ignore then
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
if regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
|
|
@ -315,7 +327,7 @@ function blacklist:is_blacklisted_ua()
|
|||
-- Check if UA is in ignore list
|
||||
local ignore = false
|
||||
for _, ignore_ua in ipairs(self.lists["IGNORE_USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ignore_ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ignore_ua) then
|
||||
ignore = true
|
||||
break
|
||||
end
|
||||
|
|
@ -323,7 +335,7 @@ function blacklist:is_blacklisted_ua()
|
|||
-- Check if UA is in blacklist
|
||||
if not ignore then
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,18 +6,41 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local bunkernet = class("bunkernet", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local WARN = ngx.WARN
|
||||
local timer_at = ngx.timer.at
|
||||
local get_phase = ngx.get_phase
|
||||
local get_version = utils.get_version
|
||||
local get_integration = utils.get_integration
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local is_ipv4 = utils.is_ipv4
|
||||
local is_ipv6 = utils.is_ipv6
|
||||
local ip_is_global = utils.ip_is_global
|
||||
local has_variable = utils.has_variable
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local is_ip_in_networks = utils.is_ip_in_networks
|
||||
local get_reason = utils.get_reason
|
||||
local get_variable = utils.get_variable
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
local encode = cjson.encode
|
||||
local decode = cjson.decode
|
||||
local http_new = http.new
|
||||
|
||||
function bunkernet:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "bunkernet", ctx)
|
||||
-- Get BunkerNet ID and save info
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local id, err = self.datastore:get("plugin_bunkernet_id", true)
|
||||
if id then
|
||||
self.bunkernet_id = id
|
||||
self.version = (self.ctx and self.ctx.bw.version) or utils.get_version()
|
||||
self.integration = (self.ctx and self.ctx.bw.integration) or utils.get_integration()
|
||||
self.version = (self.ctx and self.ctx.bw.version) or get_version()
|
||||
self.integration = (self.ctx and self.ctx.bw.integration) or get_integration()
|
||||
else
|
||||
self.logger:log(ngx.ERR, "can't get BunkerNet ID from datastore : " .. err)
|
||||
self.logger:log(ERR, "can't get BunkerNet ID from datastore : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -32,9 +55,9 @@ function bunkernet:is_needed()
|
|||
return self.variables["USE_BUNKERNET"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_BUNKERNET", "yes")
|
||||
local is_needed, err = has_variable("USE_BUNKERNET", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_BUNKERNET variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_BUNKERNET variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -59,7 +82,7 @@ function bunkernet:init_worker()
|
|||
"received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id
|
||||
)
|
||||
end
|
||||
self.logger:log(ngx.NOTICE, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
self.logger:log(NOTICE, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
return self:ret(true, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||
end
|
||||
|
||||
|
|
@ -69,7 +92,7 @@ function bunkernet:init()
|
|||
return self:ret(true, "no service uses BunkerNet, skipping init")
|
||||
end
|
||||
-- Check if instance ID is present
|
||||
local f, err = io.open("/var/cache/bunkerweb/bunkernet/instance.id", "r")
|
||||
local f, err = open("/var/cache/bunkerweb/bunkernet/instance.id", "r")
|
||||
if not f then
|
||||
return self:ret(false, "can't read instance id : " .. err)
|
||||
end
|
||||
|
|
@ -87,12 +110,12 @@ function bunkernet:init()
|
|||
local db = {
|
||||
ip = {},
|
||||
}
|
||||
local f, err = io.open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
||||
local f, err = open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
||||
if not f then
|
||||
ret = false
|
||||
else
|
||||
for line in f:lines() do
|
||||
if (utils.is_ipv4(line) or utils.is_ipv6(line)) and utils.ip_is_global(line) then
|
||||
if (is_ipv4(line) or is_ipv6(line)) and ip_is_global(line) then
|
||||
table.insert(db.ip, line)
|
||||
i = i + 1
|
||||
end
|
||||
|
|
@ -123,7 +146,7 @@ function bunkernet:access()
|
|||
return self:ret(true, "IP is not global")
|
||||
end
|
||||
-- Check if whitelisted
|
||||
if self.ctx.bw.is_whitelisted == "yes" then
|
||||
if is_whitelisted(self.ctx) then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Extract DB
|
||||
|
|
@ -132,12 +155,12 @@ function bunkernet:access()
|
|||
-- Check if is IP is present
|
||||
if #db.ip > 0 then
|
||||
-- luacheck: ignore 421
|
||||
local present, err = utils.is_ip_in_networks(self.ctx.bw.remote_addr, db.ip)
|
||||
local present, err = is_ip_in_networks(self.ctx.bw.remote_addr, db.ip)
|
||||
if present == nil then
|
||||
return self:ret(false, "can't check if ip is in db : " .. err)
|
||||
end
|
||||
if present then
|
||||
return self:ret(true, "ip is in db", utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "ip is in db", get_deny_status())
|
||||
end
|
||||
end
|
||||
else
|
||||
|
|
@ -158,7 +181,7 @@ function bunkernet:log(bypass_checks)
|
|||
end
|
||||
end
|
||||
-- Check if IP has been blocked
|
||||
local reason = utils.get_reason(self.ctx)
|
||||
local reason = get_reason(self.ctx)
|
||||
if not reason then
|
||||
return self:ret(true, "ip is not blocked")
|
||||
end
|
||||
|
|
@ -169,20 +192,30 @@ function bunkernet:log(bypass_checks)
|
|||
if not self.ctx.bw.ip_is_global then
|
||||
return self:ret(true, "IP is not global")
|
||||
end
|
||||
-- TODO : check if IP has been reported recently
|
||||
-- Check if IP has been reported recently
|
||||
local ok, data = self.cachestore:get("plugin_bunkernet_" .. self.ctx.bw.remote_addr .. "_" .. reason)
|
||||
if not ok then
|
||||
self.logger:log(ERR, "can't check cachestore : " .. data)
|
||||
elseif data then
|
||||
return self:ret(true, "already reported recently")
|
||||
end
|
||||
-- luacheck: ignore 212 431
|
||||
local function report_callback(premature, obj, ip, reason, method, url, headers)
|
||||
local function report_callback(premature, obj, ip, reason, method, url, headers, use_redis)
|
||||
local ok, err, status, _ = obj:report(ip, reason, method, url, headers)
|
||||
if status == 429 then
|
||||
obj.logger:log(ngx.WARN, "bunkernet API is rate limiting us")
|
||||
obj.logger:log(WARN, "bunkernet API is rate limiting us")
|
||||
elseif not ok then
|
||||
obj.logger:log(ngx.ERR, "can't report IP : " .. err)
|
||||
obj.logger:log(ERR, "can't report IP : " .. err)
|
||||
else
|
||||
obj.logger:log(ngx.NOTICE, "successfully reported IP " .. ip .. " (reason : " .. reason .. ")")
|
||||
obj.logger:log(NOTICE, "successfully reported IP " .. ip .. " (reason : " .. reason .. ")")
|
||||
local cachestore = require "bunkerweb.cachestore":new(use_redis, nil, true)
|
||||
local ok, err = cachestore:set("plugin_bunkernet_" .. ip .. "_" .. reason)
|
||||
if not ok then
|
||||
obj.logger:log(ERR, "error from cachestore : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local hdr, err = ngx.timer.at(
|
||||
local hdr, err = timer_at(
|
||||
0,
|
||||
report_callback,
|
||||
self,
|
||||
|
|
@ -208,7 +241,7 @@ function bunkernet:log_default()
|
|||
return self:ret(false, "missing instance ID")
|
||||
end
|
||||
-- Check if default server is disabled
|
||||
local check, err = utils.get_variable("DISABLE_DEFAULT_SERVER", false)
|
||||
local check, err = get_variable("DISABLE_DEFAULT_SERVER", false)
|
||||
if check == nil then
|
||||
return self:ret(false, "error while getting variable DISABLE_DEFAULT_SERVER : " .. err)
|
||||
end
|
||||
|
|
@ -224,7 +257,7 @@ function bunkernet:log_stream()
|
|||
end
|
||||
|
||||
function bunkernet:request(method, url, data)
|
||||
local httpc, err = http.new()
|
||||
local httpc, err = http_new()
|
||||
if not httpc then
|
||||
return false, "can't instantiate http object : " .. err
|
||||
end
|
||||
|
|
@ -240,7 +273,7 @@ function bunkernet:request(method, url, data)
|
|||
end
|
||||
local res, err = httpc:request_uri(self.variables["BUNKERNET_SERVER"] .. url, {
|
||||
method = method,
|
||||
body = cjson.encode(all_data),
|
||||
body = encode(all_data),
|
||||
headers = {
|
||||
["Content-Type"] = "application/json",
|
||||
["User-Agent"] = "BunkerWeb/" .. self.version,
|
||||
|
|
@ -253,7 +286,7 @@ function bunkernet:request(method, url, data)
|
|||
if res.status ~= 200 then
|
||||
return false, "status code != 200", res.status, nil
|
||||
end
|
||||
local ok, ret = pcall(cjson.decode, res.body)
|
||||
local ok, ret = pcall(decode, res.body)
|
||||
if not ok then
|
||||
return false, "error while decoding json : " .. ret
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,12 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local cors = class("cors", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local HTTP_NO_CONTENT = ngx.HTTP_NO_CONTENT
|
||||
local WARN = ngx.WARN
|
||||
local regex_match = utils.regex_match
|
||||
local get_deny_status = utils.get_deny_status
|
||||
|
||||
function cors:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "cors", ctx)
|
||||
|
|
@ -31,50 +37,51 @@ function cors:header()
|
|||
return self:ret(true, "origin header not present")
|
||||
end
|
||||
-- Always include Vary header to prevent caching
|
||||
local vary = ngx.header.Vary
|
||||
local ngx_header = ngx.header
|
||||
local vary = ngx_header.Vary
|
||||
if vary then
|
||||
if type(vary) == "string" then
|
||||
ngx.header.Vary = { vary, "Origin" }
|
||||
ngx_header.Vary = { vary, "Origin" }
|
||||
else
|
||||
table.insert(vary, "Origin")
|
||||
ngx.header.Vary = vary
|
||||
ngx_header.Vary = vary
|
||||
end
|
||||
else
|
||||
ngx.header.Vary = "Origin"
|
||||
ngx_header.Vary = "Origin"
|
||||
end
|
||||
-- Check if Origin is allowed
|
||||
if
|
||||
self.ctx.bw.http_origin
|
||||
and self.variables["CORS_DENY_REQUEST"] == "yes"
|
||||
and self.variables["CORS_ALLOW_ORIGIN"] ~= "*"
|
||||
and not utils.regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
and not regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
then
|
||||
self.logger:log(ngx.WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
self.logger:log(WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||
end
|
||||
-- Set headers
|
||||
if self.variables["CORS_ALLOW_ORIGIN"] == "*" then
|
||||
ngx.header["Access-Control-Allow-Origin"] = "*"
|
||||
ngx_header["Access-Control-Allow-Origin"] = "*"
|
||||
else
|
||||
ngx.header["Access-Control-Allow-Origin"] = self.ctx.bw.http_origin
|
||||
ngx_header["Access-Control-Allow-Origin"] = self.ctx.bw.http_origin
|
||||
end
|
||||
for variable, header in pairs(self.all_headers) do
|
||||
if self.variables[variable] ~= "" then
|
||||
ngx.header[header] = self.variables[variable]
|
||||
ngx_header[header] = self.variables[variable]
|
||||
end
|
||||
end
|
||||
if self.ctx.bw.request_method == "OPTIONS" then
|
||||
for variable, header in pairs(self.preflight_headers) do
|
||||
if variable == "CORS_ALLOW_CREDENTIALS" then
|
||||
if self.variables["CORS_ALLOW_CREDENTIALS"] == "yes" then
|
||||
ngx.header[header] = "true"
|
||||
ngx_header[header] = "true"
|
||||
end
|
||||
elseif self.variables[variable] ~= "" then
|
||||
ngx.header[header] = self.variables[variable]
|
||||
ngx_header[header] = self.variables[variable]
|
||||
end
|
||||
end
|
||||
ngx.header["Content-Type"] = "text/html; charset=UTF-8"
|
||||
ngx.header["Content-Length"] = "0"
|
||||
ngx_header["Content-Type"] = "text/html; charset=UTF-8"
|
||||
ngx_header["Content-Length"] = "0"
|
||||
return self:ret(true, "edited headers for preflight request")
|
||||
end
|
||||
return self:ret(true, "edited headers for standard request")
|
||||
|
|
@ -90,17 +97,17 @@ function cors:access()
|
|||
self.ctx.bw.http_origin
|
||||
and self.variables["CORS_DENY_REQUEST"] == "yes"
|
||||
and self.variables["CORS_ALLOW_ORIGIN"] ~= "*"
|
||||
and not utils.regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
and not regex_match(self.ctx.bw.http_origin, self.variables["CORS_ALLOW_ORIGIN"])
|
||||
then
|
||||
return self:ret(
|
||||
true,
|
||||
"origin " .. self.ctx.bw.http_origin .. " is not allowed, denying access",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
-- Send CORS policy with a 204 (no content) status
|
||||
if self.ctx.bw.request_method == "OPTIONS" and self.ctx.bw.http_origin then
|
||||
return self:ret(true, "preflight request", ngx.HTTP_NO_CONTENT)
|
||||
return self:ret(true, "preflight request", HTTP_NO_CONTENT)
|
||||
end
|
||||
return self:ret(true, "standard request")
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local country = class("country", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local decode = cjson.decode
|
||||
local get_country = cjson.get_country
|
||||
|
||||
function country:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "country", ctx)
|
||||
|
|
@ -18,7 +23,7 @@ function country:access()
|
|||
-- Check if IP is in cache
|
||||
local _, data = self:is_in_cache(self.ctx.bw.remote_addr)
|
||||
if data then
|
||||
data = cjson.decode(data)
|
||||
data = decode(data)
|
||||
if data.result == "ok" then
|
||||
return self:ret(
|
||||
true,
|
||||
|
|
@ -36,7 +41,7 @@ function country:access()
|
|||
.. " is in country cache (blacklisted, country = "
|
||||
.. data.country
|
||||
.. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -50,7 +55,7 @@ function country:access()
|
|||
end
|
||||
|
||||
-- Get the country of client
|
||||
local country_data, err = utils.get_country(self.ctx.bw.remote_addr)
|
||||
local country_data, err = get_country(self.ctx.bw.remote_addr)
|
||||
if not country_data then
|
||||
return self:ret(false, "can't get country of client IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
end
|
||||
|
|
@ -78,7 +83,7 @@ function country:access()
|
|||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country_data .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
|
||||
|
|
@ -93,7 +98,7 @@ function country:access()
|
|||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country_data .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -125,7 +130,7 @@ end
|
|||
function country:add_to_cache(ip, country_data, result)
|
||||
local ok, err = self.cachestore:set(
|
||||
"plugin_country_" .. self.ctx.bw.server_name .. ip,
|
||||
cjson.encode { country = country_data, result = result },
|
||||
encode { country = country_data, result = result },
|
||||
86400
|
||||
)
|
||||
if not ok then
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@ local ssl = require "ngx.ssl"
|
|||
|
||||
local customcert = class("customcert", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local open = io.open
|
||||
|
||||
function customcert:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "customcert", ctx)
|
||||
|
|
@ -12,13 +22,13 @@ end
|
|||
|
||||
function customcert:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if utils.has_variable("USE_CUSTOM_SSL", "yes") then
|
||||
local multisite, err = utils.get_variable("MULTISITE", false)
|
||||
if has_variable("USE_CUSTOM_SSL", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars, err = utils.get_multiple_variables({"USE_CUSTOM_SSL", "SERVER_NAME"})
|
||||
local vars, err = get_multiple_variables({"USE_CUSTOM_SSL", "SERVER_NAME"})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get USE_CUSTOM_SSL variables : " .. err)
|
||||
end
|
||||
|
|
@ -26,13 +36,13 @@ function customcert:init()
|
|||
if multisite_vars["USE_CUSTOM_SSL"] == "yes" and server_name ~= "global" then
|
||||
local check, data = self:read_files(server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -40,19 +50,19 @@ function customcert:init()
|
|||
end
|
||||
end
|
||||
else
|
||||
local server_name, err = utils.get_variable("SERVER_NAME", false)
|
||||
local server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -65,7 +75,7 @@ function customcert:init()
|
|||
end
|
||||
|
||||
function customcert:ssl_certificate()
|
||||
local server_name, err = ssl.server_name()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
|
|
@ -86,7 +96,7 @@ function customcert:read_files(server_name)
|
|||
}
|
||||
local data = {}
|
||||
for i, file in ipairs(files) do
|
||||
local f, err = io.open(file, "r")
|
||||
local f, err = open(file, "r")
|
||||
if not f then
|
||||
return false, file .. " = " .. err
|
||||
end
|
||||
|
|
@ -98,12 +108,12 @@ end
|
|||
|
||||
function customcert:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = ssl.parse_pem_cert(data[1])
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key, err = ssl.parse_pem_priv_key(data[2])
|
||||
local priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,9 +5,20 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local dnsbl = class("dnsbl", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local spawn = ngx.thread.spawn
|
||||
local wait = ngx.thread.wait
|
||||
local arpa_str = resolver.arpa_str
|
||||
local get_ips = utils.get_ips
|
||||
local has_variable = utils.has_variable
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local kill_all_threads = utils.kill_all_threads
|
||||
|
||||
local is_in_dnsbl = function(addr, server)
|
||||
local request = resolver.arpa_str(addr):gsub("%.in%-addr%.arpa", ""):gsub("%.ip6%.arpa", "") .. "." .. server
|
||||
local ips, err = utils.get_ips(request, false)
|
||||
local request = arpa_str(addr):gsub("%.in%-addr%.arpa", ""):gsub("%.ip6%.arpa", "") .. "." .. server
|
||||
local ips, err = get_ips(request, false, nil, true)
|
||||
if not ips then
|
||||
return nil, server, err
|
||||
end
|
||||
|
|
@ -30,7 +41,7 @@ function dnsbl:init_worker()
|
|||
return self:ret(false, "BW is loading")
|
||||
end
|
||||
-- Check if at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_DNSBL", "yes")
|
||||
local is_needed, err = has_variable("USE_DNSBL", "yes")
|
||||
if is_needed == nil then
|
||||
return self:ret(false, "can't check USE_DNSBL variable : " .. err)
|
||||
elseif not is_needed then
|
||||
|
|
@ -40,21 +51,21 @@ function dnsbl:init_worker()
|
|||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(is_in_dnsbl, "127.0.0.2", server)
|
||||
local thread = spawn(is_in_dnsbl, "127.0.0.2", server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
for data, thread in pairs(threads) do
|
||||
-- luacheck: ignore 421
|
||||
local ok, result, server, err = ngx.thread.wait(thread)
|
||||
local ok, result, server, err = wait(thread)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while waiting thread of " .. data .. " check : " .. result)
|
||||
self.logger:log(ERR, "error while waiting thread of " .. data .. " check : " .. result)
|
||||
elseif result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
self.logger:log(ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
elseif not result then
|
||||
self.logger:log(ngx.ERR, "dnsbl check for " .. server .. " failed")
|
||||
self.logger:log(ERR, "dnsbl check for " .. server .. " failed")
|
||||
else
|
||||
self.logger:log(ngx.NOTICE, "dnsbl check for " .. server .. " is successful")
|
||||
self.logger:log(NOTICE, "dnsbl check for " .. server .. " is successful")
|
||||
end
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
|
|
@ -83,14 +94,14 @@ function dnsbl:access()
|
|||
return self:ret(
|
||||
true,
|
||||
"client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")",
|
||||
utils.get_deny_status(self.ctx)
|
||||
get_deny_status()
|
||||
)
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(is_in_dnsbl, self.ctx.bw.remote_addr, server)
|
||||
local thread = spawn(is_in_dnsbl, self.ctx.bw.remote_addr, server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
|
|
@ -109,7 +120,7 @@ function dnsbl:access()
|
|||
end
|
||||
-- Wait for first thread
|
||||
-- luacheck: ignore 421
|
||||
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
||||
local ok, result, server, err = wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
|
|
@ -120,7 +131,7 @@ function dnsbl:access()
|
|||
threads[server] = nil
|
||||
-- DNS error
|
||||
if result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
self.logger:log(ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
end
|
||||
-- IP is in DNSBL
|
||||
if result then
|
||||
|
|
@ -137,7 +148,7 @@ function dnsbl:access()
|
|||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Blacklisted by a server : add to cache and deny access
|
||||
if ret_threads then
|
||||
|
|
@ -145,7 +156,7 @@ function dnsbl:access()
|
|||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "IP is blacklisted by " .. ret_server, utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "IP is blacklisted by " .. ret_server, get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
|
||||
local ngx = ngx
|
||||
local subsystem = ngx.config.subsystem
|
||||
|
||||
local template = nil
|
||||
if ngx.shared.datastore then
|
||||
local render = nil
|
||||
if subsystem == "http" then
|
||||
template = require "resty.template"
|
||||
render = template.render
|
||||
end
|
||||
|
||||
local errors = class("errors", plugin)
|
||||
|
|
@ -65,7 +71,7 @@ end
|
|||
|
||||
function errors:render_template(code)
|
||||
-- Render template
|
||||
template.render("error.html", {
|
||||
render("error.html", {
|
||||
title = code .. " - " .. self.default_errors[code].title,
|
||||
error_title = self.default_errors[code].title,
|
||||
error_code = code,
|
||||
|
|
|
|||
|
|
@ -5,14 +5,26 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local greylist = class("greylist", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local get_rdns = utils.get_rdns
|
||||
local get_asn = utils.get_asn
|
||||
local regex_match = utils.regex_match
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
|
||||
function greylist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "greylist", ctx)
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_greylist_lists", true)
|
||||
if not lists then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
self.lists = {}
|
||||
else
|
||||
self.lists = lists
|
||||
|
|
@ -45,9 +57,9 @@ function greylist:is_needed()
|
|||
return self.variables["USE_GREYLIST"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_GREYLIST", "yes")
|
||||
local is_needed, err = has_variable("USE_GREYLIST", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_GREYLIST variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_GREYLIST variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -67,7 +79,7 @@ function greylist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(greylists) do
|
||||
local f, _ = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||
local f, _ = open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(greylists[kind], line)
|
||||
|
|
@ -107,7 +119,7 @@ function greylist:access()
|
|||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
self.logger:log(ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ko" then
|
||||
return self:ret(true, k .. " is in cached greylist (info : " .. cached .. ")")
|
||||
end
|
||||
|
|
@ -124,12 +136,12 @@ function greylist:access()
|
|||
if not already_cached[k] then
|
||||
local ok, greylisted = self:is_greylisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is greylisted : " .. greylisted)
|
||||
self.logger:log(ERR, "error while checking if " .. k .. " is greylisted : " .. greylisted)
|
||||
else
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), greylisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
self.logger:log(ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if greylisted ~= "ko" then
|
||||
return self:ret(true, k .. " is in greylist")
|
||||
|
|
@ -139,7 +151,7 @@ function greylist:access()
|
|||
end
|
||||
|
||||
-- Return
|
||||
return self:ret(true, "not in greylist", utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, "not in greylist", get_deny_status())
|
||||
end
|
||||
|
||||
function greylist:preread()
|
||||
|
|
@ -169,7 +181,7 @@ end
|
|||
|
||||
function greylist:is_greylisted_ip()
|
||||
-- Check if IP is in greylist
|
||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
local ipm, err = ipmatcher_new(self.lists["IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -189,7 +201,7 @@ function greylist:is_greylisted_ip()
|
|||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
local rdns_list, err = get_rdns(self.ctx.bw.remote_addr, self.ctx, true)
|
||||
-- Check if rDNS is in greylist
|
||||
if rdns_list then
|
||||
for _, rdns in ipairs(rdns_list) do
|
||||
|
|
@ -200,15 +212,15 @@ function greylist:is_greylisted_ip()
|
|||
end
|
||||
end
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in greylist
|
||||
if self.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(self.ctx.bw.remote_addr)
|
||||
local asn, err = get_asn(self.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
self.logger:log(ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
|
|
@ -225,7 +237,7 @@ end
|
|||
function greylist:is_greylisted_uri()
|
||||
-- Check if URI is in greylist
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
if regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
|
|
@ -236,7 +248,7 @@ end
|
|||
function greylist:is_greylisted_ua()
|
||||
-- Check if UA is in greylist
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,13 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local headers = class("headers", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_phase = ngx.get_phase
|
||||
local regex_match = utils.regex_match
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local tostring = tostring
|
||||
|
||||
function headers:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "headers", ctx)
|
||||
|
|
@ -18,11 +25,11 @@ function headers:initialize(ctx)
|
|||
["X_XSS_PROTECTION"] = "X-XSS-Protection",
|
||||
}
|
||||
-- Load data from datastore if needed
|
||||
if ngx.get_phase() ~= "init" then
|
||||
if get_phase() ~= "init" then
|
||||
-- Get custom headers from datastore
|
||||
local custom_headers, err = self.datastore:get("plugin_headers_custom_headers", true)
|
||||
if not custom_headers then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
return
|
||||
end
|
||||
self.custom_headers = {}
|
||||
|
|
@ -43,7 +50,7 @@ end
|
|||
|
||||
function headers:init()
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({ "CUSTOM_HEADER" })
|
||||
local variables, err = get_multiple_variables({ "CUSTOM_HEADER" })
|
||||
if variables == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
|
|
@ -55,7 +62,7 @@ function headers:init()
|
|||
if data[srv] == nil then
|
||||
data[srv] = {}
|
||||
end
|
||||
local m = utils.regex_match(value, "([\\w-]+): ([^,]+)")
|
||||
local m = regex_match(value, "([\\w-]+): ([^,]+)")
|
||||
if m then
|
||||
data[srv][m[1]] = m[2]
|
||||
end
|
||||
|
|
@ -72,14 +79,15 @@ end
|
|||
|
||||
function headers:header()
|
||||
-- Override upstream headers if needed
|
||||
local ngx_header = ngx.header
|
||||
local ssl = self.ctx.bw.scheme == "https"
|
||||
for variable, header in pairs(self.all_headers) do
|
||||
if
|
||||
ngx.header[header] == nil
|
||||
ngx_header[header] == nil
|
||||
or (
|
||||
self.variables[variable] ~= ""
|
||||
and self.variables["KEEP_UPSTREAM_HEADERS"] ~= "*"
|
||||
and utils.regex_match(self.variables["KEEP_UPSTREAM_HEADERS"], "(^| )" .. header .. "($| )") == nil
|
||||
and regex_match(self.variables["KEEP_UPSTREAM_HEADERS"], "(^| )" .. header .. "($| )") == nil
|
||||
)
|
||||
then
|
||||
if header ~= "Strict-Transport-Security" or ssl then
|
||||
|
|
@ -87,21 +95,21 @@ function headers:header()
|
|||
header == "Content-Security-Policy"
|
||||
and self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes"
|
||||
then
|
||||
ngx.header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||
ngx_header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||
else
|
||||
ngx.header[header] = self.variables[variable]
|
||||
ngx_header[header] = self.variables[variable]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Add custom headers
|
||||
for header, value in pairs(self.custom_headers) do
|
||||
ngx.header[header] = value
|
||||
ngx_header[header] = value
|
||||
end
|
||||
-- Remove headers
|
||||
if self.variables["REMOVE_HEADERS"] ~= "" then
|
||||
for header in self.variables["REMOVE_HEADERS"]:gmatch("%S+") do
|
||||
ngx.header[header] = nil
|
||||
ngx_header[header] = nil
|
||||
end
|
||||
end
|
||||
return self:ret(true, "edited headers for request")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,27 @@ local ssl = require "ngx.ssl"
|
|||
|
||||
local letsencrypt = class("letsencrypt", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local NOTICE = ngx.NOTICE
|
||||
local OK = ngx.OK
|
||||
local HTTP_NOT_FOUND = ngx.HTTP_NOT_FOUND
|
||||
local HTTP_OK = ngx.HTTP_OK
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local HTTP_INTERNAL_SERVER_ERROR = ngx.HTTP_INTERNAL_SERVER_ERROR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local open = io.open
|
||||
local sub = string.sub
|
||||
local match = string.match
|
||||
local decode = cjson.decode
|
||||
local execute = os.execute
|
||||
local remove = os.remove
|
||||
|
||||
function letsencrypt:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "letsencrypt", ctx)
|
||||
|
|
@ -13,13 +34,13 @@ end
|
|||
|
||||
function letsencrypt:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if utils.has_variable("AUTO_LETS_ENCRYPT", "yes") then
|
||||
local multisite, err = utils.get_variable("MULTISITE", false)
|
||||
if has_variable("AUTO_LETS_ENCRYPT", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars, err = utils.get_multiple_variables({"AUTO_LETS_ENCRYPT", "SERVER_NAME"})
|
||||
local vars, err = get_multiple_variables({"AUTO_LETS_ENCRYPT", "SERVER_NAME"})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get AUTO_LETS_ENCRYPT variables : " .. err)
|
||||
end
|
||||
|
|
@ -27,13 +48,13 @@ function letsencrypt:init()
|
|||
if multisite_vars["AUTO_LETS_ENCRYPT"] == "yes" and server_name ~= "global" then
|
||||
local check, data = self:read_files(server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -41,19 +62,19 @@ function letsencrypt:init()
|
|||
end
|
||||
end
|
||||
else
|
||||
local server_name, err = utils.get_variable("SERVER_NAME", false)
|
||||
local server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -66,7 +87,7 @@ function letsencrypt:init()
|
|||
end
|
||||
|
||||
function letsencrypt:ssl_certificate()
|
||||
local server_name, err = ssl.server_name()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
|
|
@ -87,7 +108,7 @@ function letsencrypt:read_files(server_name)
|
|||
}
|
||||
local data = {}
|
||||
for i, file in ipairs(files) do
|
||||
local f, err = io.open(file, "r")
|
||||
local f, err = open(file, "r")
|
||||
if not f then
|
||||
return false, file .. " = " .. err
|
||||
end
|
||||
|
|
@ -99,12 +120,12 @@ end
|
|||
|
||||
function letsencrypt:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = ssl.parse_pem_cert(data[1])
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key, err = ssl.parse_pem_priv_key(data[2])
|
||||
local priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
|
|
@ -120,48 +141,45 @@ function letsencrypt:load_data(data, server_name)
|
|||
end
|
||||
|
||||
function letsencrypt:access()
|
||||
if string.sub(self.ctx.bw.uri, 1, string.len("/.well-known/acme-challenge/")) == "/.well-known/acme-challenge/" then
|
||||
self.logger:log(ngx.NOTICE, "got a visit from Let's Encrypt, let's whitelist it")
|
||||
return self:ret(true, "visit from LE", ngx.OK)
|
||||
if sub(self.ctx.bw.uri, 1, string.len("/.well-known/acme-challenge/")) == "/.well-known/acme-challenge/" then
|
||||
self.logger:log(NOTICE, "got a visit from Let's Encrypt, let's whitelist it")
|
||||
return self:ret(true, "visit from LE", OK)
|
||||
end
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
-- luacheck: ignore 212
|
||||
function letsencrypt:api(ctx)
|
||||
function letsencrypt:api()
|
||||
if
|
||||
not string.match(ctx.bw.uri, "^/lets%-encrypt/challenge$")
|
||||
or (ctx.bw.request_method ~= "POST" and ctx.bw.request_method ~= "DELETE")
|
||||
not match(self.ctx.bw.uri, "^/lets%-encrypt/challenge$")
|
||||
or (self.ctx.bw.request_method ~= "POST" and self.ctx.bw.request_method ~= "DELETE")
|
||||
then
|
||||
return false, nil, nil
|
||||
return self:ret(false, "success")
|
||||
end
|
||||
local acme_folder = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
|
||||
ngx.req.read_body()
|
||||
local ret, data = pcall(cjson.decode, ngx.req.get_body_data())
|
||||
local ngx_req = ngx.req
|
||||
ngx_req.read_body()
|
||||
local ret, data = pcall(decode, ngx_req.get_body_data())
|
||||
if not ret then
|
||||
return true, ngx.HTTP_BAD_REQUEST, { status = "error", msg = "json body decoding failed" }
|
||||
return self:ret(true, "json body decoding failed", HTTP_BAD_REQUEST)
|
||||
end
|
||||
os.execute("mkdir -p " .. acme_folder)
|
||||
if ctx.bw.request_method == "POST" then
|
||||
local file, err = io.open(acme_folder .. data.token, "w+")
|
||||
execute("mkdir -p " .. acme_folder)
|
||||
if self.ctx.bw.request_method == "POST" then
|
||||
local file, err = open(acme_folder .. data.token, "w+")
|
||||
if not file then
|
||||
return true,
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
{ status = "error", msg = "can't write validation token : " .. err }
|
||||
return self:ret(true, "can't write validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
file:write(data.validation)
|
||||
file:close()
|
||||
return true, ngx.HTTP_OK, { status = "success", msg = "validation token written" }
|
||||
return self:ret(true, "validation token written", HTTP_OK)
|
||||
elseif ctx.bw.request_method == "DELETE" then
|
||||
local ok, err = os.remove(acme_folder .. data.token)
|
||||
local ok, err = remove(acme_folder .. data.token)
|
||||
if not ok then
|
||||
return true,
|
||||
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||
{ status = "error", msg = "can't remove validation token : " .. err }
|
||||
return self:ret(true, "can't remove validation token : " .. err, HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
return true, ngx.HTTP_OK, { status = "success", msg = "validation token removed" }
|
||||
return true, HTTP_OK, { status = "success", msg = "validation token removed" }
|
||||
end
|
||||
return true, ngx.HTTP_NOT_FOUND, { status = "error", msg = "unknown request" }
|
||||
return true, HTTP_NOT_FOUND, { status = "error", msg = "unknown request" }
|
||||
end
|
||||
|
||||
return letsencrypt
|
||||
|
|
|
|||
|
|
@ -5,11 +5,24 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local limit = class("limit", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local HTTP_TOO_MANY_REQUESTS = ngx.HTTP_TOO_MANY_REQUESTS
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local is_whitelisted = utils.is_whitelisted
|
||||
local regex_match = utils.regex_match
|
||||
local time = os.time
|
||||
local date = os.date
|
||||
local encode = cjson.encode
|
||||
local decode = cjson.decode
|
||||
|
||||
local limit_req_timestamps = function(rate_max, rate_time, timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated = false
|
||||
local new_timestamps = {}
|
||||
local current_timestamp = os.time(os.date "!*t")
|
||||
local current_timestamp = time(date("!*t"))
|
||||
local delay = 0
|
||||
if rate_time == "s" then
|
||||
delay = 1
|
||||
|
|
@ -40,11 +53,11 @@ function limit:initialize(ctx)
|
|||
-- Call parent initialize
|
||||
plugin.initialize(self, "limit", ctx)
|
||||
-- Load rules if needed
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
-- Get all rules from datastore
|
||||
local all_rules, err = self.datastore:get("plugin_limit_rules", true)
|
||||
if not all_rules then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
return
|
||||
end
|
||||
self.rules = {}
|
||||
|
|
@ -73,7 +86,7 @@ function limit:is_needed()
|
|||
return self.variables["USE_LIMIT_REQ"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_LIMIT_REQ", "yes")
|
||||
local is_needed, err = has_variable("USE_LIMIT_REQ", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_LIMIT_REQ variable : " .. err)
|
||||
end
|
||||
|
|
@ -86,7 +99,7 @@ function limit:init()
|
|||
return self:ret(true, "no service uses limit for requests, skipping init")
|
||||
end
|
||||
-- Get variables
|
||||
local variables, err = utils.get_multiple_variables({ "LIMIT_REQ_URL", "LIMIT_REQ_RATE" })
|
||||
local variables, err = get_multiple_variables({ "LIMIT_REQ_URL", "LIMIT_REQ_RATE" })
|
||||
if variables == nil then
|
||||
return self:ret(false, err)
|
||||
end
|
||||
|
|
@ -95,7 +108,7 @@ function limit:init()
|
|||
local i = 0
|
||||
for srv, vars in pairs(variables) do
|
||||
for var, value in pairs(vars) do
|
||||
if utils.regex_match(var, "LIMIT_REQ_URL") then
|
||||
if regex_match(var, "LIMIT_REQ_URL") then
|
||||
local url = value
|
||||
local rate = vars[var:gsub("URL", "RATE")]
|
||||
if data[srv] == nil then
|
||||
|
|
@ -115,7 +128,7 @@ end
|
|||
|
||||
function limit:access()
|
||||
-- Check if we are whitelisted
|
||||
if self.ctx.bw.is_whitelisted == "yes" then
|
||||
if is_whitelisted(self.ctx) then
|
||||
return self:ret(true, "client is whitelisted")
|
||||
end
|
||||
-- Check if access is needed
|
||||
|
|
@ -125,7 +138,7 @@ function limit:access()
|
|||
-- Check if URI is limited
|
||||
local rate
|
||||
for k, v in pairs(self.rules) do
|
||||
if k ~= "/" and utils.regex_match(self.ctx.bw.uri, k) then
|
||||
if k ~= "/" and regex_match(self.ctx.bw.uri, k) then
|
||||
rate = v
|
||||
break
|
||||
end
|
||||
|
|
@ -158,7 +171,7 @@ function limit:access()
|
|||
.. " and max rate = "
|
||||
.. rate
|
||||
.. ")",
|
||||
ngx.HTTP_TOO_MANY_REQUESTS
|
||||
HTTP_TOO_MANY_REQUESTS
|
||||
)
|
||||
end
|
||||
-- Limit not reached
|
||||
|
|
@ -184,14 +197,14 @@ function limit:limit_req(rate_max, rate_time)
|
|||
if self.use_redis then
|
||||
local redis_timestamps, err = self:limit_req_redis(rate_max, rate_time)
|
||||
if redis_timestamps == nil then
|
||||
self.logger:log(ngx.ERR, "limit_req_redis failed, falling back to local : " .. err)
|
||||
self.logger:log(ERR, "limit_req_redis failed, falling back to local : " .. err)
|
||||
else
|
||||
timestamps = redis_timestamps
|
||||
-- Save the new timestamps
|
||||
-- luacheck: ignore 421
|
||||
local ok, err = self.datastore:set(
|
||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||
cjson.encode(timestamps),
|
||||
encode(timestamps),
|
||||
delay
|
||||
)
|
||||
if not ok then
|
||||
|
|
@ -222,7 +235,7 @@ function limit:limit_req_local(rate_max, rate_time)
|
|||
elseif err == "not found" then
|
||||
timestamps = "{}"
|
||||
end
|
||||
timestamps = cjson.decode(timestamps)
|
||||
timestamps = decode(timestamps)
|
||||
-- Compute new timestamps
|
||||
local updated, new_timestamps, delay = limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||
-- Save new timestamps if needed
|
||||
|
|
@ -230,7 +243,7 @@ function limit:limit_req_local(rate_max, rate_time)
|
|||
-- luacheck: ignore 421
|
||||
local ok, err = self.datastore:set(
|
||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||
cjson.encode(new_timestamps),
|
||||
encode(new_timestamps),
|
||||
delay
|
||||
)
|
||||
if not ok then
|
||||
|
|
@ -303,7 +316,7 @@ function limit:limit_req_redis(rate_max, rate_time)
|
|||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||
rate_max,
|
||||
rate_time,
|
||||
os.time(os.date("!*t"))
|
||||
time(date("!*t"))
|
||||
)
|
||||
if not timestamps then
|
||||
self.clusterstore:close()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local misc = class("misc", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local HTTP_NOT_ALLOWED = ngx.HTTP_NOT_ALLOWED
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local regex_match = utils.regex_match
|
||||
|
||||
function misc:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "misc", ctx)
|
||||
|
|
@ -12,8 +17,8 @@ end
|
|||
function misc:access()
|
||||
-- Check if method is valid
|
||||
local method = self.ctx.bw.request_method
|
||||
if not method or not utils.regex_match(method, "^[A-Z]+$") then
|
||||
return self:ret(true, "method is not valid", ngx.HTTP_BAD_REQUEST)
|
||||
if not method or not regex_match(method, "^[A-Z]+$") then
|
||||
return self:ret(true, "method is not valid", HTTP_BAD_REQUEST)
|
||||
end
|
||||
-- Check if method is allowed
|
||||
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
||||
|
|
@ -21,7 +26,7 @@ function misc:access()
|
|||
return self:ret(true, "method " .. method .. " is allowed")
|
||||
end
|
||||
end
|
||||
return self:ret(true, "method " .. method .. " is not allowed", ngx.HTTP_NOT_ALLOWED)
|
||||
return self:ret(true, "method " .. method .. " is not allowed", HTTP_NOT_ALLOWED)
|
||||
end
|
||||
|
||||
return misc
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ local plugin = require "bunkerweb.plugin"
|
|||
|
||||
local redis = class("redis", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local NOTICE = ngx.NOTICE
|
||||
|
||||
function redis:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "redis", ctx)
|
||||
|
|
@ -27,7 +30,7 @@ function redis:init_worker()
|
|||
if not ok then
|
||||
return self:ret(false, "redis ping command failed")
|
||||
end
|
||||
self.logger:log(ngx.NOTICE, "connectivity with redis server " .. self.variables["REDIS_HOST"] .. " is successful")
|
||||
self.logger:log(NOTICE, "connectivity with redis server " .. self.variables["REDIS_HOST"] .. " is successful")
|
||||
return self:ret(true, "success")
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local reversescan = class("reversescan", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local spawn = ngx.thread.spawn
|
||||
local wait = ngx.thread.wait
|
||||
local ngx_socket = ngx.socket
|
||||
local kill_all_threads = utils.kill_all_threads
|
||||
local get_deny_status = utils.get_deny_status
|
||||
local tonumber = tonumber
|
||||
|
||||
function reversescan:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "reversescan", ctx)
|
||||
|
|
@ -32,7 +40,7 @@ function reversescan:access()
|
|||
break
|
||||
-- Perform scan in a thread
|
||||
elseif not cached then
|
||||
local thread = ngx.thread.spawn(
|
||||
local thread = spawn(
|
||||
self.scan,
|
||||
self.ctx.bw.remote_addr,
|
||||
tonumber(port),
|
||||
|
|
@ -47,11 +55,11 @@ function reversescan:access()
|
|||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, ret_err, get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
|
|
@ -71,7 +79,7 @@ function reversescan:access()
|
|||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
||||
local ok, open, port = wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
|
|
@ -100,7 +108,7 @@ function reversescan:access()
|
|||
for _, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Cache results
|
||||
for port, result in pairs(results) do
|
||||
|
|
@ -112,7 +120,7 @@ function reversescan:access()
|
|||
if ret_threads ~= nil then
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||
return self:ret(true, ret_err, get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
|
|
@ -126,7 +134,7 @@ function reversescan:preread()
|
|||
end
|
||||
|
||||
function reversescan.scan(ip, port, timeout)
|
||||
local tcpsock = ngx.socket.tcp()
|
||||
local tcpsock = ngx_socket.tcp()
|
||||
tcpsock:settimeout(timeout)
|
||||
local ok, _ = tcpsock:connect(ip, port)
|
||||
tcpsock:close()
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@ local ssl = require "ngx.ssl"
|
|||
|
||||
local selfsigned = class("selfsigned", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local parse_pem_cert = ssl.parse_pem_cert
|
||||
local parse_pem_priv_key = ssl.parse_pem_priv_key
|
||||
local ssl_server_name = ssl.server_name
|
||||
local get_variable = utils.get_variable
|
||||
local get_multiple_variables = utils.get_multiple_variables
|
||||
local has_variable = utils.has_variable
|
||||
local open = io.open
|
||||
|
||||
function selfsigned:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "selfsigned", ctx)
|
||||
|
|
@ -12,13 +22,13 @@ end
|
|||
|
||||
function selfsigned:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if utils.has_variable("GENERATE_SELF_SIGNED_SSL", "yes") then
|
||||
local multisite, err = utils.get_variable("MULTISITE", false)
|
||||
if has_variable("GENERATE_SELF_SIGNED_SSL", "yes") then
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
return self:ret(false, "can't get MULTISITE variable : " .. err)
|
||||
end
|
||||
if multisite == "yes" then
|
||||
local vars, err = utils.get_multiple_variables({"GENERATE_SELF_SIGNED_SSL", "SERVER_NAME"})
|
||||
local vars, err = get_multiple_variables({"GENERATE_SELF_SIGNED_SSL", "SERVER_NAME"})
|
||||
if not vars then
|
||||
return self:ret(false, "can't get GENERATE_SELF_SIGNED_SSL variables : " .. err)
|
||||
end
|
||||
|
|
@ -26,13 +36,13 @@ function selfsigned:init()
|
|||
if multisite_vars["GENERATE_SELF_SIGNED_SSL"] == "yes" and server_name ~= "global" then
|
||||
local check, data = self:read_files(server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, multisite_vars["SERVER_NAME"])
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -40,19 +50,19 @@ function selfsigned:init()
|
|||
end
|
||||
end
|
||||
else
|
||||
local server_name, err = utils.get_variable("SERVER_NAME", false)
|
||||
local server_name, err = get_variable("SERVER_NAME", false)
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get SERVER_NAME variable : " .. err)
|
||||
end
|
||||
local check, data = self:read_files(server_name:match("%S+"))
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while reading files : " .. data)
|
||||
self.logger:log(ERR, "error while reading files : " .. data)
|
||||
ret_ok = false
|
||||
ret_err = "error reading files"
|
||||
else
|
||||
local check, err = self:load_data(data, server_name)
|
||||
if not check then
|
||||
self.logger:log(ngx.ERR, "error while loading data : " .. err)
|
||||
self.logger:log(ERR, "error while loading data : " .. err)
|
||||
ret_ok = false
|
||||
ret_err = "error loading data"
|
||||
end
|
||||
|
|
@ -65,7 +75,7 @@ function selfsigned:init()
|
|||
end
|
||||
|
||||
function selfsigned:ssl_certificate()
|
||||
local server_name, err = ssl.server_name()
|
||||
local server_name, err = ssl_server_name()
|
||||
if not server_name then
|
||||
return self:ret(false, "can't get server_name : " .. err)
|
||||
end
|
||||
|
|
@ -86,7 +96,7 @@ function selfsigned:read_files(server_name)
|
|||
}
|
||||
local data = {}
|
||||
for i, file in ipairs(files) do
|
||||
local f, err = io.open(file, "r")
|
||||
local f, err = open(file, "r")
|
||||
if not f then
|
||||
return false, file .. " = " .. err
|
||||
end
|
||||
|
|
@ -98,12 +108,12 @@ end
|
|||
|
||||
function selfsigned:load_data(data, server_name)
|
||||
-- Load certificate
|
||||
local cert_chain, err = ssl.parse_pem_cert(data[1])
|
||||
local cert_chain, err = parse_pem_cert(data[1])
|
||||
if not cert_chain then
|
||||
return false, "error while parsing pem cert : " .. err
|
||||
end
|
||||
-- Load key
|
||||
local priv_key, err = ssl.parse_pem_priv_key(data[2])
|
||||
local priv_key, err = parse_pem_priv_key(data[2])
|
||||
if not priv_key then
|
||||
return false, "error while parsing pem priv key : " .. err
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local sessions = class("sessions", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local get_variable = utils.get_variable
|
||||
local session_init = session.init
|
||||
local tonumber = tonumber
|
||||
|
||||
function sessions:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "sessions", ctx)
|
||||
|
|
@ -57,7 +63,7 @@ function sessions:init()
|
|||
["REDIS_KEEPALIVE_POOL"] = "",
|
||||
}
|
||||
for k, _ in pairs(redis_vars) do
|
||||
local value, err = utils.get_variable(k, false)
|
||||
local value, err = get_variable(k, false)
|
||||
if value == nil then
|
||||
return self:ret(false, "can't get " .. k .. " variable : " .. err)
|
||||
end
|
||||
|
|
@ -78,7 +84,7 @@ function sessions:init()
|
|||
config.secret = utils.rand(16)
|
||||
local ok, err = self.datastore:set("storage_sessions_SESSIONS_SECRET", config.secret)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
||||
self.logger:log(ERR, "error from datastore:set : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -89,7 +95,7 @@ function sessions:init()
|
|||
config.cookie_name = utils.rand(16)
|
||||
local ok, err = self.datastore:set("storage_sessions_SESSIONS_NAME", config.cookie_name)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
||||
self.logger:log(ERR, "error from datastore:set : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -111,7 +117,7 @@ function sessions:init()
|
|||
database = tonumber(redis_vars["REDIS_DATABASE"]),
|
||||
}
|
||||
end
|
||||
session.init(config)
|
||||
session_init(config)
|
||||
return self:ret(true, "sessions init successful")
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,29 @@ local utils = require "bunkerweb.utils"
|
|||
|
||||
local whitelist = class("whitelist", plugin)
|
||||
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local OK = ngx.OK
|
||||
local WARN = ngx.WARN
|
||||
local get_phase = ngx.get_phase
|
||||
local has_variable = utils.has_variable
|
||||
local get_ips = utils.get_ips
|
||||
local get_rdns = utils.get_rdns
|
||||
local get_asn = utils.get_asn
|
||||
local regex_match = utils.regex_match
|
||||
local ipmatcher_new = ipmatcher.new
|
||||
local tostring = tostring
|
||||
local open = io.open
|
||||
local env_set = env.set
|
||||
|
||||
function whitelist:initialize(ctx)
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "whitelist", ctx)
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
if get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_whitelist_lists", true)
|
||||
if not lists then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
self.logger:log(ERR, err)
|
||||
self.lists = {}
|
||||
else
|
||||
self.lists = lists
|
||||
|
|
@ -46,9 +61,9 @@ function whitelist:is_needed()
|
|||
return self.variables["USE_WHITELIST"] == "yes"
|
||||
end
|
||||
-- Other cases : at least one service uses it
|
||||
local is_needed, err = utils.has_variable("USE_WHITELIST", "yes")
|
||||
local is_needed, err = has_variable("USE_WHITELIST", "yes")
|
||||
if is_needed == nil then
|
||||
self.logger:log(ngx.ERR, "can't check USE_WHITELIST variable : " .. err)
|
||||
self.logger:log(ERR, "can't check USE_WHITELIST variable : " .. err)
|
||||
end
|
||||
return is_needed
|
||||
end
|
||||
|
|
@ -68,7 +83,7 @@ function whitelist:init()
|
|||
}
|
||||
local i = 0
|
||||
for kind, _ in pairs(whitelists) do
|
||||
local f, _ = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||
local f, _ = open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||
if f then
|
||||
for line in f:lines() do
|
||||
table.insert(whitelists[kind], line)
|
||||
|
|
@ -86,10 +101,11 @@ function whitelist:init()
|
|||
end
|
||||
|
||||
function whitelist:set()
|
||||
local ngx_var = ngx.var
|
||||
-- Set default value
|
||||
ngx.var.is_whitelisted = "no"
|
||||
ngx_var.is_whitelisted = "no"
|
||||
self.ctx.bw.is_whitelisted = "no"
|
||||
env.set("is_whitelisted", "no")
|
||||
env_set("is_whitelisted", "no")
|
||||
-- Check if set is needed
|
||||
if not self:is_needed() then
|
||||
return self:ret(true, "whitelist not activated")
|
||||
|
|
@ -99,9 +115,9 @@ function whitelist:set()
|
|||
if whitelisted == nil then
|
||||
return self:ret(false, err)
|
||||
elseif whitelisted then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
ngx_var.is_whitelisted = "yes"
|
||||
self.ctx.bw.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
env_set("is_whitelisted", "yes")
|
||||
return self:ret(true, err)
|
||||
end
|
||||
return self:ret(true, "not in whitelist cache")
|
||||
|
|
@ -113,14 +129,15 @@ function whitelist:access()
|
|||
return self:ret(true, "whitelist not activated")
|
||||
end
|
||||
-- Check cache
|
||||
local ngx_var = ngx.var
|
||||
local whitelisted, err, already_cached = self:check_cache()
|
||||
if whitelisted == nil then
|
||||
return self:ret(false, err)
|
||||
elseif whitelisted then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
ngx_var.is_whitelisted = "yes"
|
||||
self.ctx.bw.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
return self:ret(true, err, ngx.OK)
|
||||
env_set("is_whitelisted", "yes")
|
||||
return self:ret(true, err, OK)
|
||||
end
|
||||
-- Perform checks
|
||||
local ok
|
||||
|
|
@ -128,16 +145,16 @@ function whitelist:access()
|
|||
if not already_cached[k] then
|
||||
ok, whitelisted = self:is_whitelisted(k)
|
||||
if ok == nil then
|
||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
||||
self.logger:log(ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
||||
else
|
||||
ok, err = self:add_to_cache(self:kind_to_ele(k), whitelisted)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||
self.logger:log(ERR, "error while adding element to cache : " .. err)
|
||||
end
|
||||
if whitelisted ~= "ok" then
|
||||
ngx.var.is_whitelisted = "yes"
|
||||
ngx_var.is_whitelisted = "yes"
|
||||
self.ctx.bw.is_whitelisted = "yes"
|
||||
env.set("is_whitelisted", "yes")
|
||||
env_set("is_whitelisted", "yes")
|
||||
return self:ret(true, k .. " is whitelisted (info : " .. whitelisted .. ")", ngx.OK)
|
||||
end
|
||||
end
|
||||
|
|
@ -179,7 +196,7 @@ function whitelist:check_cache()
|
|||
for k, v in pairs(checks) do
|
||||
local ok, cached = self:is_in_cache(v)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||
self.logger:log(ERR, "error while checking cache : " .. cached)
|
||||
elseif cached and cached ~= "ok" then
|
||||
return true, k .. " is in cached whitelist (info : " .. cached .. ")"
|
||||
end
|
||||
|
|
@ -224,7 +241,7 @@ end
|
|||
|
||||
function whitelist:is_whitelisted_ip()
|
||||
-- Check if IP is in whitelist
|
||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
||||
local ipm, err = ipmatcher_new(self.lists["IP"])
|
||||
if not ipm then
|
||||
return nil, err
|
||||
end
|
||||
|
|
@ -244,7 +261,7 @@ function whitelist:is_whitelisted_ip()
|
|||
if check_rdns then
|
||||
-- Get rDNS
|
||||
-- luacheck: ignore 421
|
||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||
local rdns_list, err = get_rdns(self.ctx.bw.remote_addr, self.ctx, true)
|
||||
-- Check if rDNS is in whitelist
|
||||
if rdns_list then
|
||||
local forward_check = nil
|
||||
|
|
@ -262,7 +279,7 @@ function whitelist:is_whitelisted_ip()
|
|||
end
|
||||
end
|
||||
if forward_check then
|
||||
local ip_list, err = utils.get_ips(forward_check)
|
||||
local ip_list, err = get_ips(forward_check, nil, self.ctx, true)
|
||||
if ip_list then
|
||||
for _, ip in ipairs(ip_list) do
|
||||
if ip == self.ctx.bw.remote_addr then
|
||||
|
|
@ -270,23 +287,23 @@ function whitelist:is_whitelisted_ip()
|
|||
end
|
||||
end
|
||||
self.logger:log(
|
||||
ngx.WARN,
|
||||
WARN,
|
||||
"IP " .. self.ctx.bw.remote_addr .. " may spoof reverse DNS " .. forward_check
|
||||
)
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns (forward check) : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns (forward check) : " .. err)
|
||||
end
|
||||
end
|
||||
else
|
||||
self.logger:log(ngx.ERR, "error while getting rdns : " .. err)
|
||||
self.logger:log(ERR, "error while getting rdns : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if ASN is in whitelist
|
||||
if self.ctx.bw.ip_is_global then
|
||||
local asn, err = utils.get_asn(self.ctx.bw.remote_addr)
|
||||
local asn, err = get_asn(self.ctx.bw.remote_addr)
|
||||
if not asn then
|
||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
self.logger:log(ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||
else
|
||||
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||
if bl_asn == tostring(asn) then
|
||||
|
|
@ -303,7 +320,7 @@ end
|
|||
function whitelist:is_whitelisted_uri()
|
||||
-- Check if URI is in whitelist
|
||||
for _, uri in ipairs(self.lists["URI"]) do
|
||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||
if regex_match(self.ctx.bw.uri, uri) then
|
||||
return true, "URI " .. uri
|
||||
end
|
||||
end
|
||||
|
|
@ -314,7 +331,7 @@ end
|
|||
function whitelist:is_whitelisted_ua()
|
||||
-- Check if UA is in whitelist
|
||||
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
if regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||
return true, "UA " .. ua
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue