Add StyLua and luacheck to precommit config file and apply it
This commit is contained in:
parent
cd1f87b9a2
commit
77bfe2697f
|
@ -0,0 +1,2 @@
|
||||||
|
globals = {"ngx", "delay", "unpack"}
|
||||||
|
ignore = {"411"}
|
|
@ -26,6 +26,19 @@ repos:
|
||||||
- id: prettier
|
- id: prettier
|
||||||
name: Prettier Code Formatter
|
name: Prettier Code Formatter
|
||||||
|
|
||||||
|
- repo: https://github.com/JohnnyMorganz/StyLua
|
||||||
|
rev: 27e6b388796604181e810ef05c9fb15a9f7a7769 # frozen: v0.18.2
|
||||||
|
hooks:
|
||||||
|
- id: stylua-github
|
||||||
|
exclude: ^src/(bw/lua/middleclass.lua|common/core/antibot/captcha.lua)$
|
||||||
|
|
||||||
|
- repo: https://github.com/lunarmodules/luacheck
|
||||||
|
rev: ababb6d403d634eb74d2c541035e9ede966e710d # frozen: v1.1.1
|
||||||
|
hooks:
|
||||||
|
- id: luacheck
|
||||||
|
exclude: ^src/(bw/lua/middleclass.lua|common/core/antibot/captcha.lua)$
|
||||||
|
args: ["--std", "min", "--codes", "--ranges", "--no-cache"]
|
||||||
|
|
||||||
- repo: https://github.com/pycqa/flake8
|
- repo: https://github.com/pycqa/flake8
|
||||||
rev: 10f4af6dbcf93456ba7df762278ae61ba3120dc6 # frozen: 6.1.0
|
rev: 10f4af6dbcf93456ba7df762278ae61ba3120dc6 # frozen: 6.1.0
|
||||||
hooks:
|
hooks:
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
local class = require "middleclass"
|
local cjson = require "cjson"
|
||||||
|
local class = require "middleclass"
|
||||||
local datastore = require "bunkerweb.datastore"
|
local datastore = require "bunkerweb.datastore"
|
||||||
local utils = require "bunkerweb.utils"
|
local logger = require "bunkerweb.logger"
|
||||||
local logger = require "bunkerweb.logger"
|
local process = require "ngx.process"
|
||||||
local cjson = require "cjson"
|
local rsignal = require "resty.signal"
|
||||||
local upload = require "resty.upload"
|
local upload = require "resty.upload"
|
||||||
local rsignal = require "resty.signal"
|
local utils = require "bunkerweb.utils"
|
||||||
local process = require "ngx.process"
|
|
||||||
|
|
||||||
local api = class("api")
|
local api = class("api")
|
||||||
|
|
||||||
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
api.global = { GET = {}, POST = {}, PUT = {}, DELETE = {} }
|
||||||
|
|
||||||
function api:initialize()
|
function api:initialize()
|
||||||
self.datastore = datastore:new()
|
self.datastore = datastore:new()
|
||||||
|
@ -26,6 +26,7 @@ function api:initialize()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore 212
|
||||||
function api:log_cmd(cmd, status, stdout, stderr)
|
function api:log_cmd(cmd, status, stdout, stderr)
|
||||||
local level = ngx.NOTICE
|
local level = ngx.NOTICE
|
||||||
local prefix = "success"
|
local prefix = "success"
|
||||||
|
@ -33,7 +34,7 @@ function api:log_cmd(cmd, status, stdout, stderr)
|
||||||
level = ngx.ERR
|
level = ngx.ERR
|
||||||
prefix = "error"
|
prefix = "error"
|
||||||
end
|
end
|
||||||
self.logger:log(level, prefix .. " while running command " .. command)
|
self.logger:log(level, prefix .. " while running command " .. cmd)
|
||||||
self.logger:log(level, "stdout = " .. stdout)
|
self.logger:log(level, "stdout = " .. stdout)
|
||||||
self.logger:log(level, "stdout = " .. stderr)
|
self.logger:log(level, "stdout = " .. stderr)
|
||||||
end
|
end
|
||||||
|
@ -41,6 +42,7 @@ end
|
||||||
-- TODO : use this if we switch to OpenResty
|
-- TODO : use this if we switch to OpenResty
|
||||||
function api:cmd(cmd)
|
function api:cmd(cmd)
|
||||||
-- Non-blocking command
|
-- Non-blocking command
|
||||||
|
-- luacheck: ignore 113
|
||||||
local ok, stdout, stderr, reason, status = shell.run(cmd, nil, 10000)
|
local ok, stdout, stderr, reason, status = shell.run(cmd, nil, 10000)
|
||||||
self.logger:log_cmd(cmd, status, stdout, stderr)
|
self.logger:log_cmd(cmd, status, stdout, stderr)
|
||||||
-- Timeout
|
-- Timeout
|
||||||
|
@ -51,6 +53,7 @@ function api:cmd(cmd)
|
||||||
return status == 0, reason, status
|
return status == 0, reason, status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore 212
|
||||||
function api:response(http_status, api_status, msg)
|
function api:response(http_status, api_status, msg)
|
||||||
local resp = {}
|
local resp = {}
|
||||||
resp["status"] = api_status
|
resp["status"] = api_status
|
||||||
|
@ -101,6 +104,7 @@ api.global.POST["^/confs$"] = function(self)
|
||||||
form:set_timeout(1000)
|
form:set_timeout(1000)
|
||||||
local file = io.open(tmp, "w+")
|
local file = io.open(tmp, "w+")
|
||||||
while true do
|
while true do
|
||||||
|
-- luacheck: ignore 421
|
||||||
local typ, res, err = form:read()
|
local typ, res, err = form:read()
|
||||||
if not typ then
|
if not typ then
|
||||||
file:close()
|
file:close()
|
||||||
|
@ -117,9 +121,9 @@ api.global.POST["^/confs$"] = function(self)
|
||||||
file:close()
|
file:close()
|
||||||
local cmds = {
|
local cmds = {
|
||||||
"rm -rf " .. destination .. "/*",
|
"rm -rf " .. destination .. "/*",
|
||||||
"tar xzf " .. tmp .. " -C " .. destination
|
"tar xzf " .. tmp .. " -C " .. destination,
|
||||||
}
|
}
|
||||||
for i, cmd in ipairs(cmds) do
|
for _, cmd in ipairs(cmds) do
|
||||||
local status = os.execute(cmd)
|
local status = os.execute(cmd)
|
||||||
if status ~= 0 then
|
if status ~= 0 then
|
||||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "exit status = " .. tostring(status))
|
||||||
|
@ -176,17 +180,23 @@ end
|
||||||
|
|
||||||
api.global.GET["^/bans$"] = function(self)
|
api.global.GET["^/bans$"] = function(self)
|
||||||
local data = {}
|
local data = {}
|
||||||
for i, k in ipairs(self.datastore:keys()) do
|
for _, k in ipairs(self.datastore:keys()) do
|
||||||
if k:find("^bans_ip_") then
|
if k:find("^bans_ip_") then
|
||||||
local reason, err = self.datastore:get(k)
|
local reason, err = self.datastore:get(k)
|
||||||
if err then
|
if err then
|
||||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
|
return self:response(
|
||||||
"can't access " .. k .. " from datastore : " .. reason)
|
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
"error",
|
||||||
|
"can't access " .. k .. " from datastore : " .. reason
|
||||||
|
)
|
||||||
end
|
end
|
||||||
local ok, ttl = self.datastore:ttl(k)
|
local ok, ttl = self.datastore:ttl(k)
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error",
|
return self:response(
|
||||||
"can't access ttl " .. k .. " from datastore : " .. ttl)
|
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
"error",
|
||||||
|
"can't access ttl " .. k .. " from datastore : " .. ttl
|
||||||
|
)
|
||||||
end
|
end
|
||||||
local ban = { ip = k:sub(9, #k), reason = reason, exp = math.floor(ttl) }
|
local ban = { ip = k:sub(9, #k), reason = reason, exp = math.floor(ttl) }
|
||||||
table.insert(data, ban)
|
table.insert(data, ban)
|
||||||
|
@ -196,7 +206,7 @@ api.global.GET["^/bans$"] = function(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
api.global.GET["^/variables$"] = function(self)
|
api.global.GET["^/variables$"] = function(self)
|
||||||
local variables, err = datastore:get('variables', true)
|
local variables, err = datastore:get("variables", true)
|
||||||
if not variables then
|
if not variables then
|
||||||
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't access variables from datastore : " .. err)
|
return self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't access variables from datastore : " .. err)
|
||||||
end
|
end
|
||||||
|
@ -219,9 +229,9 @@ function api:do_api_call()
|
||||||
if status ~= ngx.HTTP_OK then
|
if status ~= ngx.HTTP_OK then
|
||||||
ret = false
|
ret = false
|
||||||
end
|
end
|
||||||
if (#resp["msg"] == 0) then
|
if #resp["msg"] == 0 then
|
||||||
resp["msg"] = ""
|
resp["msg"] = ""
|
||||||
elseif (type(resp["msg"]) == "table") then
|
elseif type(resp["msg"]) == "table" then
|
||||||
resp["data"] = resp["msg"]
|
resp["data"] = resp["msg"]
|
||||||
resp["msg"] = resp["status"]
|
resp["msg"] = resp["status"]
|
||||||
end
|
end
|
||||||
|
@ -231,10 +241,10 @@ function api:do_api_call()
|
||||||
end
|
end
|
||||||
local list, err = self.datastore:get("plugins", true)
|
local list, err = self.datastore:get("plugins", true)
|
||||||
if not list then
|
if not list then
|
||||||
local status, resp = self:response(ngx.HTTP_INTERNAL_SERVER_ERROR, "error", "can't list loaded plugins : " .. err)
|
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)
|
return false, resp["msg"], ngx.HTTP_INTERNAL_SERVER_ERROR, cjson.encode(resp)
|
||||||
end
|
end
|
||||||
for i, plugin in ipairs(list) do
|
for _, plugin in ipairs(list) do
|
||||||
if pcall(require, plugin.id .. "/" .. plugin.id) then
|
if pcall(require, plugin.id .. "/" .. plugin.id) then
|
||||||
local plugin_lua = require(plugin.id .. "/" .. plugin.id)
|
local plugin_lua = require(plugin.id .. "/" .. plugin.id)
|
||||||
if plugin_lua.api ~= nil then
|
if plugin_lua.api ~= nil then
|
||||||
|
|
|
@ -1,42 +1,38 @@
|
||||||
local mlcache = require "resty.mlcache"
|
local class = require "middleclass"
|
||||||
local clusterstore = require "bunkerweb.clusterstore"
|
local clusterstore = require "bunkerweb.clusterstore"
|
||||||
local logger = require "bunkerweb.logger"
|
local logger = require "bunkerweb.logger"
|
||||||
local utils = require "bunkerweb.utils"
|
local mlcache = require "resty.mlcache"
|
||||||
local class = require "middleclass"
|
local utils = require "bunkerweb.utils"
|
||||||
local cachestore = class("cachestore")
|
local cachestore = class("cachestore")
|
||||||
|
|
||||||
-- Instantiate mlcache object at module level (which will be cached when running init phase)
|
-- Instantiate mlcache object at module level (which will be cached when running init phase)
|
||||||
-- TODO : custom settings
|
-- TODO : custom settings
|
||||||
local shm = "cachestore"
|
local shm = "cachestore"
|
||||||
local ipc_shm = "cachestore_ipc"
|
local ipc_shm = "cachestore_ipc"
|
||||||
local shm_miss = "cachestore_miss"
|
local shm_miss = "cachestore_miss"
|
||||||
local shm_locks = "cachestore_locks"
|
local shm_locks = "cachestore_locks"
|
||||||
if not ngx.shared.cachestore then
|
if not ngx.shared.cachestore then
|
||||||
shm = "cachestore_stream"
|
shm = "cachestore_stream"
|
||||||
ipc_shm = "cachestore_ipc_stream"
|
ipc_shm = "cachestore_ipc_stream"
|
||||||
shm_miss = "cachestore_miss_stream"
|
shm_miss = "cachestore_miss_stream"
|
||||||
shm_locks = "cachestore_locks_stream"
|
shm_locks = "cachestore_locks_stream"
|
||||||
end
|
end
|
||||||
local cache, err = mlcache.new(
|
local cache, err = mlcache.new("cachestore", shm, {
|
||||||
"cachestore",
|
lru_size = 100,
|
||||||
shm,
|
ttl = 30,
|
||||||
{
|
neg_ttl = 0.1,
|
||||||
lru_size = 100,
|
shm_set_tries = 3,
|
||||||
ttl = 30,
|
shm_miss = shm_miss,
|
||||||
neg_ttl = 0.1,
|
shm_locks = shm_locks,
|
||||||
shm_set_tries = 3,
|
resty_lock_opts = {
|
||||||
shm_miss = shm_miss,
|
exptime = 30,
|
||||||
shm_locks = shm_locks,
|
timeout = 5,
|
||||||
resty_lock_opts = {
|
step = 0.001,
|
||||||
exptime = 30,
|
ratio = 2,
|
||||||
timeout = 5,
|
max_step = 0.5,
|
||||||
step = 0.001,
|
},
|
||||||
ratio = 2,
|
ipc_shm = ipc_shm,
|
||||||
max_step = 0.5
|
})
|
||||||
},
|
|
||||||
ipc_shm = ipc_shm
|
|
||||||
}
|
|
||||||
)
|
|
||||||
local module_logger = logger:new("CACHESTORE")
|
local module_logger = logger:new("CACHESTORE")
|
||||||
if not cache then
|
if not cache then
|
||||||
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
||||||
|
@ -57,10 +53,12 @@ function cachestore:initialize(use_redis, new_cs, ctx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cachestore:get(key)
|
function cachestore:get(key)
|
||||||
|
-- luacheck: ignore 432
|
||||||
local callback = function(key, cs)
|
local callback = function(key, cs)
|
||||||
-- Connect to redis
|
-- Connect to redis
|
||||||
|
-- luacheck: ignore 431
|
||||||
local clusterstore = cs or require "bunkerweb.clusterstore":new(false)
|
local clusterstore = cs or require "bunkerweb.clusterstore":new(false)
|
||||||
local ok, err, reused = clusterstore:connect()
|
local ok, err, _ = clusterstore:connect()
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, "can't connect to redis : " .. err, nil
|
return nil, "can't connect to redis : " .. err, nil
|
||||||
end
|
end
|
||||||
|
@ -96,6 +94,7 @@ function cachestore:get(key)
|
||||||
local callback_no_miss = function()
|
local callback_no_miss = function()
|
||||||
return nil, nil, -1
|
return nil, nil, -1
|
||||||
end
|
end
|
||||||
|
-- luacheck: ignore 431
|
||||||
local value, err, hit_level
|
local value, err, hit_level
|
||||||
if self.use_redis and utils.is_cosocket_available() then
|
if self.use_redis and utils.is_cosocket_available() then
|
||||||
local cs = nil
|
local cs = nil
|
||||||
|
@ -114,13 +113,14 @@ function cachestore:get(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
function cachestore:set(key, value, ex)
|
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 utils.is_cosocket_available() then
|
||||||
local ok, err = self:set_redis(key, value, ex)
|
ok, err = self:set_redis(key, value, ex)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, err)
|
self.logger:log(ngx.ERR, err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local ok, err
|
|
||||||
if ex then
|
if ex then
|
||||||
ok, err = self.cache:set(key, { ttl = ex }, value)
|
ok, err = self.cache:set(key, { ttl = ex }, value)
|
||||||
else
|
else
|
||||||
|
@ -134,13 +134,14 @@ end
|
||||||
|
|
||||||
function cachestore:set_redis(key, value, ex)
|
function cachestore:set_redis(key, value, ex)
|
||||||
-- Connect to redis
|
-- Connect to redis
|
||||||
local ok, err, reused = self.clusterstore:connect()
|
-- luacheck: ignore 431
|
||||||
|
local ok, err, _ = self.clusterstore:connect()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "can't connect to redis : " .. err
|
return false, "can't connect to redis : " .. err
|
||||||
end
|
end
|
||||||
-- Set value with ttl
|
-- Set value with ttl
|
||||||
local default_ex = ex or 30
|
local default_ex = ex or 30
|
||||||
local ok, err = self.clusterstore:call("set", key, value, "EX", default_ex)
|
local _, err = self.clusterstore:call("set", key, value, "EX", default_ex)
|
||||||
if err then
|
if err then
|
||||||
self.clusterstore:close()
|
self.clusterstore:close()
|
||||||
return false, "SET failed : " .. err
|
return false, "SET failed : " .. err
|
||||||
|
@ -149,14 +150,16 @@ function cachestore:set_redis(key, value, ex)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function cachestore:delete(key, value, ex)
|
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 utils.is_cosocket_available() then
|
||||||
local ok, err = self.del_redis(key)
|
ok, err = self:del_redis(key)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, err)
|
self.logger:log(ngx.ERR, err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local ok, err = self.cache:delete(key)
|
ok, err = self.cache:delete(key)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
@ -165,12 +168,13 @@ end
|
||||||
|
|
||||||
function cachestore:del_redis(key)
|
function cachestore:del_redis(key)
|
||||||
-- Connect to redis
|
-- Connect to redis
|
||||||
|
-- luacheck: ignore 431
|
||||||
local ok, err = self.clusterstore:connect()
|
local ok, err = self.clusterstore:connect()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "can't connect to redis : " .. err
|
return false, "can't connect to redis : " .. err
|
||||||
end
|
end
|
||||||
-- Set value with ttl
|
-- Set value with ttl
|
||||||
local ok, err = self.clusterstore:del(key)
|
local _, err = self.clusterstore:del(key)
|
||||||
if err then
|
if err then
|
||||||
self.clusterstore:close()
|
self.clusterstore:close()
|
||||||
return false, "DEL failed : " .. err
|
return false, "DEL failed : " .. err
|
||||||
|
|
|
@ -1,135 +1,138 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local utils = require "bunkerweb.utils"
|
local logger = require "bunkerweb.logger"
|
||||||
local logger = require "bunkerweb.logger"
|
local redis = require "resty.redis"
|
||||||
local redis = require "resty.redis"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local clusterstore = class("clusterstore")
|
local clusterstore = class("clusterstore")
|
||||||
|
|
||||||
function clusterstore:initialize(pool)
|
function clusterstore:initialize(pool)
|
||||||
-- Instantiate logger
|
-- Instantiate logger
|
||||||
self.logger = logger:new("CLUSTERSTORE")
|
self.logger = logger:new("CLUSTERSTORE")
|
||||||
-- Get variables
|
-- Get variables
|
||||||
local variables = {
|
local variables = {
|
||||||
["REDIS_HOST"] = "",
|
["REDIS_HOST"] = "",
|
||||||
["REDIS_PORT"] = "",
|
["REDIS_PORT"] = "",
|
||||||
["REDIS_DATABASE"] = "",
|
["REDIS_DATABASE"] = "",
|
||||||
["REDIS_SSL"] = "",
|
["REDIS_SSL"] = "",
|
||||||
["REDIS_TIMEOUT"] = "",
|
["REDIS_TIMEOUT"] = "",
|
||||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||||
["REDIS_KEEPALIVE_POOL"] = ""
|
["REDIS_KEEPALIVE_POOL"] = "",
|
||||||
}
|
}
|
||||||
-- Set them for later user
|
-- Set them for later user
|
||||||
self.variables = {}
|
self.variables = {}
|
||||||
for k, v in pairs(variables) do
|
for k, _ in pairs(variables) do
|
||||||
local value, err = utils.get_variable(k, false)
|
local value, err = utils.get_variable(k, false)
|
||||||
if value == nil then
|
if value == nil then
|
||||||
self.logger:log(ngx.ERR, err)
|
self.logger:log(ngx.ERR, err)
|
||||||
end
|
end
|
||||||
self.variables[k] = value
|
self.variables[k] = value
|
||||||
end
|
end
|
||||||
-- Don't instantiate a redis object for now
|
-- Don't instantiate a redis object for now
|
||||||
self.redis_client = nil
|
self.redis_client = nil
|
||||||
self.pool = pool == nil or pool
|
self.pool = pool == nil or pool
|
||||||
end
|
end
|
||||||
|
|
||||||
function clusterstore:connect()
|
function clusterstore:connect()
|
||||||
-- Check if we are already connected
|
-- Check if we are already connected
|
||||||
if self.redis_client then
|
if self.redis_client then
|
||||||
return true, "already connected", self.redis_client:get_reused_times()
|
return true, "already connected", self.redis_client:get_reused_times()
|
||||||
end
|
end
|
||||||
-- Instantiate object
|
-- Instantiate object
|
||||||
local redis_client, err = redis:new()
|
local redis_client, err = redis:new()
|
||||||
if redis_client == nil then
|
if redis_client == nil then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
-- Set timeouts
|
-- Set timeouts
|
||||||
redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||||
-- Connect
|
-- Connect
|
||||||
local options = {
|
local options = {
|
||||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||||
}
|
}
|
||||||
if self.pool then
|
if self.pool then
|
||||||
options.pool = "bw-redis"
|
options.pool = "bw-redis"
|
||||||
options.pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
options.pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||||
end
|
end
|
||||||
local ok, err = redis_client:connect(self.variables["REDIS_HOST"], tonumber(self.variables["REDIS_PORT"]), options)
|
local ok, err = redis_client:connect(self.variables["REDIS_HOST"], tonumber(self.variables["REDIS_PORT"]), options)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
self.redis_client = redis_client
|
self.redis_client = redis_client
|
||||||
-- Select database if needed
|
-- Select database if needed
|
||||||
local times, err = self.redis_client:get_reused_times()
|
local times, err = self.redis_client:get_reused_times()
|
||||||
if err then
|
if err then
|
||||||
self:close()
|
self:close()
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
if times == 0 then
|
if times == 0 then
|
||||||
local select, err = self.redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
-- luacheck: ignore 421
|
||||||
if err then
|
local _, err = self.redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||||
self:close()
|
if err then
|
||||||
return false, err
|
self:close()
|
||||||
end
|
return false, err
|
||||||
end
|
end
|
||||||
return true, "success", times
|
end
|
||||||
|
return true, "success", times
|
||||||
end
|
end
|
||||||
|
|
||||||
function clusterstore:close()
|
function clusterstore:close()
|
||||||
if self.redis_client then
|
if self.redis_client then
|
||||||
-- Equivalent to close but keep a pool of connections
|
-- Equivalent to close but keep a pool of connections
|
||||||
if self.pool then
|
if self.pool then
|
||||||
local ok, err = self.redis_client:set_keepalive(tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
local ok, err = self.redis_client:set_keepalive(
|
||||||
tonumber(self.variables["REDIS_KEEPALIVE_POOL"]))
|
tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
||||||
self.redis_client = nil
|
tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||||
if not ok then
|
)
|
||||||
require "bunkerweb.logger":new("clusterstore-close"):log(ngx.ERR, err)
|
self.redis_client = nil
|
||||||
end
|
if not ok then
|
||||||
return ok, err
|
require("bunkerweb.logger"):new("clusterstore-close"):log(ngx.ERR, err)
|
||||||
end
|
end
|
||||||
-- Close
|
return ok, err
|
||||||
local ok, err = self.redis_client:close()
|
end
|
||||||
self.redis_client.redis_client = nil
|
-- Close
|
||||||
return ok, err
|
local ok, err = self.redis_client:close()
|
||||||
end
|
self.redis_client.redis_client = nil
|
||||||
return false, "not connected"
|
return ok, err
|
||||||
|
end
|
||||||
|
return false, "not connected"
|
||||||
end
|
end
|
||||||
|
|
||||||
function clusterstore:call(method, ...)
|
function clusterstore:call(method, ...)
|
||||||
-- Check if we are connected
|
-- Check if we are connected
|
||||||
if not self.redis_client then
|
if not self.redis_client then
|
||||||
return false, "not connected"
|
return false, "not connected"
|
||||||
end
|
end
|
||||||
-- Call method
|
-- Call method
|
||||||
return self.redis_client[method](self.redis_client, ...)
|
return self.redis_client[method](self.redis_client, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function clusterstore:multi(calls)
|
function clusterstore:multi(calls)
|
||||||
-- Check if we are connected
|
-- Check if we are connected
|
||||||
if not self.redis_client then
|
if not self.redis_client then
|
||||||
return false, "not connected"
|
return false, "not connected"
|
||||||
end
|
end
|
||||||
-- Start transaction
|
-- Start transaction
|
||||||
local ok, err = self.redis_client:multi()
|
local ok, err = self.redis_client:multi()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "multi() failed : " .. err
|
return false, "multi() failed : " .. err
|
||||||
end
|
end
|
||||||
-- Loop on calls
|
-- Loop on calls
|
||||||
for i, call in ipairs(calls) do
|
for _, call in ipairs(calls) do
|
||||||
local method = call[1]
|
local method = call[1]
|
||||||
local args = unpack(call[2])
|
local args = unpack(call[2])
|
||||||
local ok, err = self.redis_client[method](self.redis_client, args)
|
ok, err = self.redis_client[method](self.redis_client, args)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, method + "() failed : " .. err
|
return false, method + "() failed : " .. err
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Exec transaction
|
-- Exec transaction
|
||||||
local exec, err = self.redis_client:exec()
|
local exec, err = self.redis_client:exec()
|
||||||
if not exec then
|
if not exec then
|
||||||
return false, "exec() failed : " .. err
|
return false, "exec() failed : " .. err
|
||||||
end
|
end
|
||||||
if type(exec) ~= "table" then
|
if type(exec) ~= "table" then
|
||||||
return false, "exec() result is not a table"
|
return false, "exec() result is not a table"
|
||||||
end
|
end
|
||||||
return true, "success", exec
|
return true, "success", exec
|
||||||
end
|
end
|
||||||
|
|
||||||
return clusterstore
|
return clusterstore
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local lrucache = require "resty.lrucache"
|
local lrucache = require "resty.lrucache"
|
||||||
local datastore = class("datastore")
|
local datastore = class("datastore")
|
||||||
|
|
||||||
local lru, err = lrucache.new(100000)
|
local lru, err = lrucache.new(100000)
|
||||||
if not lru then
|
if not lru then
|
||||||
require "bunkerweb.logger":new("DATASTORE"):log(ngx.ERR,
|
require "bunkerweb.logger"
|
||||||
"failed to instantiate LRU cache : " .. (err or "unknown error"))
|
:new("DATASTORE")
|
||||||
|
:log(ngx.ERR, "failed to instantiate LRU cache : " .. (err or "unknown error"))
|
||||||
end
|
end
|
||||||
|
|
||||||
function datastore:initialize()
|
function datastore:initialize()
|
||||||
|
@ -16,11 +17,13 @@ function datastore:initialize()
|
||||||
end
|
end
|
||||||
|
|
||||||
function datastore:get(key, worker)
|
function datastore:get(key, worker)
|
||||||
|
-- luacheck: ignore 431
|
||||||
|
local value, err
|
||||||
if worker then
|
if worker then
|
||||||
local value, err = lru:get(key)
|
value, err = lru:get(key)
|
||||||
return value, err or "not found"
|
return value, err or "not found"
|
||||||
end
|
end
|
||||||
local value, err = self.dict:get(key)
|
value, err = self.dict:get(key)
|
||||||
if not value and not err then
|
if not value and not err then
|
||||||
err = "not found"
|
err = "not found"
|
||||||
end
|
end
|
||||||
|
@ -52,10 +55,11 @@ function datastore:keys(worker)
|
||||||
return self.dict:get_keys(0)
|
return self.dict:get_keys(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function datastore:ttl(key)
|
function datastore:ttl(key, worker)
|
||||||
if worker then
|
if worker then
|
||||||
return false, "not supported by LRU"
|
return false, "not supported by LRU"
|
||||||
end
|
end
|
||||||
|
-- luacheck: ignore 431
|
||||||
local ttl, err = self.dict:ttl(key)
|
local ttl, err = self.dict:ttl(key)
|
||||||
if not ttl then
|
if not ttl then
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -64,13 +68,13 @@ function datastore:ttl(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
function datastore:delete_all(pattern, worker)
|
function datastore:delete_all(pattern, worker)
|
||||||
local keys = {}
|
local keys
|
||||||
if worker then
|
if worker then
|
||||||
keys = lru:keys(0)
|
keys = lru:keys(0)
|
||||||
else
|
else
|
||||||
keys = self.dict:get_keys(0)
|
keys = self.dict:get_keys(0)
|
||||||
end
|
end
|
||||||
for i, key in ipairs(keys) do
|
for _, key in ipairs(keys) do
|
||||||
if key:match(pattern) then
|
if key:match(pattern) then
|
||||||
self.dict:delete(key)
|
self.dict:delete(key)
|
||||||
end
|
end
|
||||||
|
@ -78,6 +82,7 @@ function datastore:delete_all(pattern, worker)
|
||||||
return true, "success"
|
return true, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore 212
|
||||||
function datastore:flush_lru()
|
function datastore:flush_lru()
|
||||||
lru:flush_all()
|
lru:flush_all()
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,263 +1,263 @@
|
||||||
local utils = require "bunkerweb.utils"
|
local cjson = require "cjson"
|
||||||
local cjson = require "cjson"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local helpers = {}
|
local helpers = {}
|
||||||
|
|
||||||
helpers.load_plugin = function(json)
|
helpers.load_plugin = function(json)
|
||||||
-- Open file
|
-- Open file
|
||||||
local file, err, nb = io.open(json, "r")
|
local file, err, nb = io.open(json, "r")
|
||||||
if not file then
|
if not file then
|
||||||
return false, "can't load JSON at " .. json .. " : " .. err .. " (nb = " .. tostring(nb) .. ")"
|
return false, "can't load JSON at " .. json .. " : " .. err .. " (nb = " .. tostring(nb) .. ")"
|
||||||
end
|
end
|
||||||
-- Decode JSON
|
-- Decode JSON
|
||||||
local ok, plugin = pcall(cjson.decode, file:read("*a"))
|
local ok, plugin = pcall(cjson.decode, file:read("*a"))
|
||||||
file:close()
|
file:close()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "invalid JSON at " .. json .. " : " .. err
|
return false, "invalid JSON at " .. json .. " : " .. err
|
||||||
end
|
end
|
||||||
-- Check fields
|
-- Check fields
|
||||||
local missing_fields = {}
|
local missing_fields = {}
|
||||||
local required_fields = { "id", "name", "description", "version", "settings", "stream" }
|
local required_fields = { "id", "name", "description", "version", "settings", "stream" }
|
||||||
for i, field in ipairs(required_fields) do
|
for _, field in ipairs(required_fields) do
|
||||||
if plugin[field] == nil then
|
if plugin[field] == nil then
|
||||||
table.insert(missing_fields, field)
|
table.insert(missing_fields, field)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #missing_fields > 0 then
|
if #missing_fields > 0 then
|
||||||
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
|
return false, "missing field(s) " .. cjson.encode(missing_fields) .. " for JSON at " .. json
|
||||||
end
|
end
|
||||||
-- Try require
|
-- Try require
|
||||||
local plugin_lua, err = helpers.require_plugin(plugin.id)
|
local plugin_lua, err = helpers.require_plugin(plugin.id)
|
||||||
if plugin_lua == false then
|
if plugin_lua == false then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
-- Fill phases
|
-- Fill phases
|
||||||
local phases = utils.get_phases()
|
local phases = utils.get_phases()
|
||||||
plugin.phases = {}
|
plugin.phases = {}
|
||||||
if plugin_lua then
|
if plugin_lua then
|
||||||
for i, phase in ipairs(phases) do
|
for _, phase in ipairs(phases) do
|
||||||
if plugin_lua[phase] ~= nil then
|
if plugin_lua[phase] ~= nil then
|
||||||
table.insert(plugin.phases, phase)
|
table.insert(plugin.phases, phase)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Return plugin
|
-- Return plugin
|
||||||
return true, plugin
|
return true, plugin
|
||||||
end
|
end
|
||||||
|
|
||||||
helpers.order_plugins = function(plugins)
|
helpers.order_plugins = function(plugins)
|
||||||
-- Extract orders
|
-- Extract orders
|
||||||
local file, err, nb = io.open("/usr/share/bunkerweb/core/order.json", "r")
|
local file, err, nb = io.open("/usr/share/bunkerweb/core/order.json", "r")
|
||||||
if not file then
|
if not file then
|
||||||
return false, err .. " (nb = " .. tostring(nb) .. ")"
|
return false, err .. " (nb = " .. tostring(nb) .. ")"
|
||||||
end
|
end
|
||||||
local ok, orders = pcall(cjson.decode, file:read("*a"))
|
local ok, orders = pcall(cjson.decode, file:read("*a"))
|
||||||
file:close()
|
file:close()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "invalid order.json : " .. err
|
return false, "invalid order.json : " .. err
|
||||||
end
|
end
|
||||||
-- Compute plugins/id/phases table
|
-- Compute plugins/id/phases table
|
||||||
local plugins_phases = {}
|
local plugins_phases = {}
|
||||||
for i, plugin in ipairs(plugins) do
|
for _, plugin in ipairs(plugins) do
|
||||||
plugins_phases[plugin.id] = {}
|
plugins_phases[plugin.id] = {}
|
||||||
for j, phase in ipairs(plugin.phases) do
|
for _, phase in ipairs(plugin.phases) do
|
||||||
plugins_phases[plugin.id][phase] = true
|
plugins_phases[plugin.id][phase] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Order result
|
-- Order result
|
||||||
local result_orders = {}
|
local result_orders = {}
|
||||||
for i, phase in ipairs(utils.get_phases()) do
|
for _, phase in ipairs(utils.get_phases()) do
|
||||||
result_orders[phase] = {}
|
result_orders[phase] = {}
|
||||||
end
|
end
|
||||||
-- Fill order first
|
-- Fill order first
|
||||||
for phase, order in pairs(orders) do
|
for phase, order in pairs(orders) do
|
||||||
for i, id in ipairs(order) do
|
for _, id in ipairs(order) do
|
||||||
local plugin = plugins_phases[id]
|
local plugin = plugins_phases[id]
|
||||||
if plugin and plugin[phase] then
|
if plugin and plugin[phase] then
|
||||||
table.insert(result_orders[phase], id)
|
table.insert(result_orders[phase], id)
|
||||||
plugin[phase] = nil
|
plugin[phase] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Then append missing plugins to the end
|
-- Then append missing plugins to the end
|
||||||
for i, phase in ipairs(utils.get_phases()) do
|
for _, phase in ipairs(utils.get_phases()) do
|
||||||
for id, plugin in pairs(plugins_phases) do
|
for id, plugin in pairs(plugins_phases) do
|
||||||
if plugin[phase] then
|
if plugin[phase] then
|
||||||
table.insert(result_orders[phase], id)
|
table.insert(result_orders[phase], id)
|
||||||
plugin[phase] = nil
|
plugin[phase] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true, result_orders
|
return true, result_orders
|
||||||
end
|
end
|
||||||
|
|
||||||
helpers.require_plugin = function(id)
|
helpers.require_plugin = function(id)
|
||||||
-- Require call
|
-- Require call
|
||||||
local ok, plugin_lua = pcall(require, id .. "/" .. id)
|
local ok, plugin_lua = pcall(require, id .. "/" .. id)
|
||||||
if not ok then
|
if not ok then
|
||||||
if plugin_lua:match("not found") then
|
if plugin_lua:match("not found") then
|
||||||
return nil, "plugin " .. id .. " doesn't have LUA code"
|
return nil, "plugin " .. id .. " doesn't have LUA code"
|
||||||
end
|
end
|
||||||
return false, "require error for plugin " .. id .. " : " .. plugin_lua
|
return false, "require error for plugin " .. id .. " : " .. plugin_lua
|
||||||
end
|
end
|
||||||
-- New call
|
-- New call
|
||||||
if plugin_lua.new == nil then
|
if plugin_lua.new == nil then
|
||||||
return false, "missing new() method for plugin " .. id
|
return false, "missing new() method for plugin " .. id
|
||||||
end
|
end
|
||||||
-- Return plugin
|
-- Return plugin
|
||||||
return plugin_lua, "require() call successful for plugin " .. id
|
return plugin_lua, "require() call successful for plugin " .. id
|
||||||
end
|
end
|
||||||
|
|
||||||
helpers.new_plugin = function(plugin_lua, ctx)
|
helpers.new_plugin = function(plugin_lua, ctx)
|
||||||
-- Require call
|
-- Require call
|
||||||
local ok, plugin_obj = pcall(plugin_lua.new, plugin_lua, ctx)
|
local ok, plugin_obj = pcall(plugin_lua.new, plugin_lua, ctx)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "new error for plugin " .. plugin_lua.name .. " : " .. plugin_obj
|
return false, "new error for plugin " .. plugin_lua.name .. " : " .. plugin_obj
|
||||||
end
|
end
|
||||||
return true, plugin_obj
|
return true, plugin_obj
|
||||||
end
|
end
|
||||||
|
|
||||||
helpers.call_plugin = function(plugin, method)
|
helpers.call_plugin = function(plugin, method)
|
||||||
-- Check if method is present
|
-- Check if method is present
|
||||||
if plugin[method] == nil then
|
if plugin[method] == nil then
|
||||||
return nil, "missing " .. method .. "() method for plugin " .. plugin:get_id()
|
return nil, "missing " .. method .. "() method for plugin " .. plugin:get_id()
|
||||||
end
|
end
|
||||||
-- Call method
|
-- Call method
|
||||||
local ok, ret = pcall(plugin[method], plugin)
|
local ok, ret = pcall(plugin[method], plugin)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, plugin:get_id() .. ":" .. method .. "() failed : " .. ret
|
return false, plugin:get_id() .. ":" .. method .. "() failed : " .. ret
|
||||||
end
|
end
|
||||||
if ret == nil then
|
if ret == nil then
|
||||||
return false, plugin:get_id() .. ":" .. method .. "() returned nil value"
|
return false, plugin:get_id() .. ":" .. method .. "() returned nil value"
|
||||||
end
|
end
|
||||||
-- Check values
|
-- Check values
|
||||||
local missing_values = {}
|
local missing_values = {}
|
||||||
local required_values = { "ret", "msg" }
|
local required_values = { "ret", "msg" }
|
||||||
for i, value in ipairs(required_values) do
|
for _, value in ipairs(required_values) do
|
||||||
if ret[value] == nil then
|
if ret[value] == nil then
|
||||||
table.insert(missing_values, value)
|
table.insert(missing_values, value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if #missing_values > 0 then
|
if #missing_values > 0 then
|
||||||
return false, "missing required return value(s) : " .. cjson.encode(missing_values)
|
return false, "missing required return value(s) : " .. cjson.encode(missing_values)
|
||||||
end
|
end
|
||||||
-- Return
|
-- Return
|
||||||
return true, ret
|
return true, ret
|
||||||
end
|
end
|
||||||
|
|
||||||
helpers.fill_ctx = function()
|
helpers.fill_ctx = function()
|
||||||
-- Return errors as table
|
-- Return errors as table
|
||||||
local errors = {}
|
local errors = {}
|
||||||
local ctx = ngx.ctx
|
local ctx = ngx.ctx
|
||||||
-- Check if ctx is already filled
|
-- Check if ctx is already filled
|
||||||
if not ctx.bw then
|
if not ctx.bw then
|
||||||
-- Instantiate bw table
|
-- Instantiate bw table
|
||||||
local data = {}
|
local data = {}
|
||||||
-- Common vars
|
-- Common vars
|
||||||
data.kind = "http"
|
data.kind = "http"
|
||||||
if ngx.shared.datastore_stream then
|
if ngx.shared.datastore_stream then
|
||||||
data.kind = "stream"
|
data.kind = "stream"
|
||||||
end
|
end
|
||||||
data.remote_addr = ngx.var.remote_addr
|
data.remote_addr = ngx.var.remote_addr
|
||||||
data.server_name = ngx.var.server_name
|
data.server_name = ngx.var.server_name
|
||||||
if data.kind == "http" then
|
if data.kind == "http" then
|
||||||
data.uri = ngx.var.uri
|
data.uri = ngx.var.uri
|
||||||
data.request_uri = ngx.var.request_uri
|
data.request_uri = ngx.var.request_uri
|
||||||
data.request_method = ngx.var.request_method
|
data.request_method = ngx.var.request_method
|
||||||
data.http_user_agent = ngx.var.http_user_agent
|
data.http_user_agent = ngx.var.http_user_agent
|
||||||
data.http_host = ngx.var.http_host
|
data.http_host = ngx.var.http_host
|
||||||
data.server_name = ngx.var.server_name
|
data.server_name = ngx.var.server_name
|
||||||
data.http_content_type = ngx.var.http_content_type
|
data.http_content_type = ngx.var.http_content_type
|
||||||
data.http_content_length = ngx.var.http_content_length
|
data.http_content_length = ngx.var.http_content_length
|
||||||
data.http_origin = ngx.var.http_origin
|
data.http_origin = ngx.var.http_origin
|
||||||
data.http_version = ngx.req.http_version()
|
data.http_version = ngx.req.http_version()
|
||||||
data.scheme = ngx.var.scheme
|
data.scheme = ngx.var.scheme
|
||||||
end
|
end
|
||||||
-- IP data : global
|
-- IP data : global
|
||||||
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
|
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
|
||||||
if ip_is_global == nil then
|
if ip_is_global == nil then
|
||||||
table.insert(errors, "can't check if IP is global : " .. err)
|
table.insert(errors, "can't check if IP is global : " .. err)
|
||||||
else
|
else
|
||||||
data.ip_is_global = ip_is_global
|
data.ip_is_global = ip_is_global
|
||||||
end
|
end
|
||||||
-- IP data : v4 / v6
|
-- IP data : v4 / v6
|
||||||
data.ip_is_ipv4 = utils.is_ipv4(data.ip)
|
data.ip_is_ipv4 = utils.is_ipv4(data.ip)
|
||||||
data.ip_is_ipv6 = utils.is_ipv6(data.ip)
|
data.ip_is_ipv6 = utils.is_ipv6(data.ip)
|
||||||
-- Misc info
|
-- Misc info
|
||||||
data.integration = utils.get_integration()
|
data.integration = utils.get_integration()
|
||||||
data.version = utils.get_version()
|
data.version = utils.get_version()
|
||||||
-- Fill ctx
|
-- Fill ctx
|
||||||
ctx.bw = data
|
ctx.bw = data
|
||||||
end
|
end
|
||||||
-- Always create new objects for current phases in case of cosockets
|
-- 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 = utils.get_variable("USE_REDIS", false)
|
||||||
if not use_redis then
|
if not use_redis then
|
||||||
table.insert(errors, "can't get variable from datastore : " .. err)
|
table.insert(errors, "can't get variable from datastore : " .. err)
|
||||||
end
|
end
|
||||||
ctx.bw.datastore = require "bunkerweb.datastore":new()
|
ctx.bw.datastore = require "bunkerweb.datastore":new()
|
||||||
ctx.bw.clusterstore = require "bunkerweb.clusterstore":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")
|
||||||
return true, "ctx filled", errors, ctx
|
return true, "ctx filled", errors, ctx
|
||||||
end
|
end
|
||||||
|
|
||||||
function helpers.load_variables(all_variables, plugins)
|
function helpers.load_variables(all_variables, plugins)
|
||||||
-- Extract settings from plugins and global ones
|
-- Extract settings from plugins and global ones
|
||||||
local all_settings = {}
|
local all_settings = {}
|
||||||
for i, plugin in ipairs(plugins) do
|
for _, plugin in ipairs(plugins) do
|
||||||
if plugin.settings then
|
if plugin.settings then
|
||||||
for setting, data in pairs(plugin.settings) do
|
for setting, data in pairs(plugin.settings) do
|
||||||
all_settings[setting] = data
|
all_settings[setting] = data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local file = io.open("/usr/share/bunkerweb/settings.json")
|
local file = io.open("/usr/share/bunkerweb/settings.json")
|
||||||
if not file then
|
if not file then
|
||||||
return false, "can't open settings.json"
|
return false, "can't open settings.json"
|
||||||
end
|
end
|
||||||
local ok, settings = pcall(cjson.decode, file:read("*a"))
|
local ok, settings = pcall(cjson.decode, file:read("*a"))
|
||||||
file:close()
|
file:close()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "invalid settings.json : " .. err
|
return false, "invalid settings.json : " .. settings
|
||||||
end
|
end
|
||||||
for setting, data in pairs(settings) do
|
for setting, data in pairs(settings) do
|
||||||
all_settings[setting] = data
|
all_settings[setting] = data
|
||||||
end
|
end
|
||||||
-- Extract vars
|
-- Extract vars
|
||||||
local variables = { ["global"] = {} }
|
local variables = { ["global"] = {} }
|
||||||
local multisite = all_variables["MULTISITE"] == "yes"
|
local multisite = all_variables["MULTISITE"] == "yes"
|
||||||
local server_names = {}
|
local server_names = {}
|
||||||
if multisite then
|
if multisite then
|
||||||
for server_name in all_variables["SERVER_NAME"]:gmatch("%S+") do
|
for server_name in all_variables["SERVER_NAME"]:gmatch("%S+") do
|
||||||
variables[server_name] = {}
|
variables[server_name] = {}
|
||||||
table.insert(server_names, server_name)
|
table.insert(server_names, server_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
for setting, data in pairs(all_settings) do
|
for setting, data in pairs(all_settings) do
|
||||||
if all_variables[setting] then
|
if all_variables[setting] then
|
||||||
variables["global"][setting] = all_variables[setting]
|
variables["global"][setting] = all_variables[setting]
|
||||||
end
|
end
|
||||||
if data.multiple then
|
if data.multiple then
|
||||||
for variable, value in pairs(all_variables) do
|
for variable, value in pairs(all_variables) do
|
||||||
local _, server_name, multiple_setting = variable:match("((%S*_?)(" .. setting .. "_%d+))")
|
local _, server_name, multiple_setting = variable:match("((%S*_?)(" .. setting .. "_%d+))")
|
||||||
if multiple_setting then
|
if multiple_setting then
|
||||||
if multisite and server_name and server_name:match("%S+_$") then
|
if multisite and server_name and server_name:match("%S+_$") then
|
||||||
variables[server_name:sub(1, -2)][multiple_setting] = value
|
variables[server_name:sub(1, -2)][multiple_setting] = value
|
||||||
else
|
else
|
||||||
variables["global"][multiple_setting] = value
|
variables["global"][multiple_setting] = value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if multisite then
|
if multisite then
|
||||||
for i, server_name in ipairs(server_names) do
|
for _, server_name in ipairs(server_names) do
|
||||||
local key = server_name .. "_" .. setting
|
local key = server_name .. "_" .. setting
|
||||||
if all_variables[key] then
|
if all_variables[key] then
|
||||||
variables[server_name][setting] = all_variables[key]
|
variables[server_name][setting] = all_variables[key]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true, variables
|
return true, variables
|
||||||
end
|
end
|
||||||
|
|
||||||
return helpers
|
return helpers
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
local class = require "middleclass"
|
||||||
local errlog = require "ngx.errlog"
|
local errlog = require "ngx.errlog"
|
||||||
local class = require "middleclass"
|
|
||||||
local logger = class("logger")
|
local logger = class("logger")
|
||||||
|
|
||||||
function logger:initialize(prefix)
|
function logger:initialize(prefix)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
local geoip = require "geoip.mmdb"
|
local geoip = require "geoip.mmdb"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
country_db = geoip.load_database("/var/cache/bunkerweb/country.mmdb"),
|
country_db = geoip.load_database "/var/cache/bunkerweb/country.mmdb",
|
||||||
asn_db = geoip.load_database("/var/cache/bunkerweb/asn.mmdb")
|
asn_db = geoip.load_database "/var/cache/bunkerweb/asn.mmdb",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,87 +1,88 @@
|
||||||
local class = require "middleclass"
|
local cachestore = require "bunkerweb.cachestore"
|
||||||
local logger = require "bunkerweb.logger"
|
local class = require "middleclass"
|
||||||
local datastore = require "bunkerweb.datastore"
|
|
||||||
local cachestore = require "bunkerweb.cachestore"
|
|
||||||
local clusterstore = require "bunkerweb.clusterstore"
|
local clusterstore = require "bunkerweb.clusterstore"
|
||||||
local utils = require "bunkerweb.utils"
|
local datastore = require "bunkerweb.datastore"
|
||||||
local cjson = require "cjson"
|
local logger = require "bunkerweb.logger"
|
||||||
local plugin = class("plugin")
|
local utils = require "bunkerweb.utils"
|
||||||
|
local plugin = class("plugin")
|
||||||
|
|
||||||
function plugin:initialize(id, ctx)
|
function plugin:initialize(id, ctx)
|
||||||
-- Store common, values
|
-- Store common, values
|
||||||
self.id = id
|
self.id = id
|
||||||
local multisite = false
|
local multisite = false
|
||||||
local current_phase = ngx.get_phase()
|
local current_phase = ngx.get_phase()
|
||||||
for i, check_phase in ipairs({ "set", "access", "content", "header_filter", "log", "preread", "log_stream",
|
for _, check_phase in ipairs {
|
||||||
"log_default" }) do
|
"set",
|
||||||
if current_phase == check_phase then
|
"access",
|
||||||
multisite = true
|
"content",
|
||||||
break
|
"header_filter",
|
||||||
end
|
"log",
|
||||||
end
|
"preread",
|
||||||
self.is_request = multisite
|
"log_stream",
|
||||||
-- Store common objects
|
"log_default",
|
||||||
self.logger = logger:new(self.id)
|
} do
|
||||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
if current_phase == check_phase then
|
||||||
if not use_redis then
|
multisite = true
|
||||||
self.logger:log(ngx.ERR, err)
|
break
|
||||||
end
|
end
|
||||||
self.use_redis = use_redis == "yes"
|
end
|
||||||
if self.is_request then
|
self.is_request = multisite
|
||||||
-- Store ctx
|
-- Store common objects
|
||||||
self.ctx = ctx or ngx.ctx
|
self.logger = logger:new(self.id)
|
||||||
self.datastore = utils.get_ctx_obj("datastore", self.ctx) or datastore:new()
|
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||||
self.cachestore = utils.get_ctx_obj("cachestore", self.ctx) or cachestore:new(use_redis == "yes", true, self.ctx)
|
if not use_redis then
|
||||||
self.clusterstore = utils.get_ctx_obj("clusterstore", self.ctx) or clusterstore:new(false)
|
self.logger:log(ngx.ERR, err)
|
||||||
else
|
end
|
||||||
self.datastore = datastore:new()
|
self.use_redis = use_redis == "yes"
|
||||||
self.cachestore = cachestore:new(use_redis == "yes", true)
|
if self.is_request then
|
||||||
self.clusterstore = clusterstore:new(false)
|
-- Store ctx
|
||||||
end
|
self.ctx = ctx or ngx.ctx
|
||||||
-- Get metadata
|
self.datastore = utils.get_ctx_obj("datastore", self.ctx) or datastore:new()
|
||||||
local metadata, err = self.datastore:get("plugin_" .. id, true)
|
self.cachestore = utils.get_ctx_obj("cachestore", self.ctx)
|
||||||
if not metadata then
|
or cachestore:new(use_redis == "yes", true, self.ctx)
|
||||||
self.logger:log(ngx.ERR, err)
|
self.clusterstore = utils.get_ctx_obj("clusterstore", self.ctx) or clusterstore:new(false)
|
||||||
return
|
else
|
||||||
end
|
self.datastore = datastore:new()
|
||||||
-- Store variables
|
self.cachestore = cachestore:new(use_redis == "yes", true)
|
||||||
self.variables = {}
|
self.clusterstore = clusterstore:new(false)
|
||||||
self.multiples = {}
|
end
|
||||||
for k, v in pairs(metadata.settings) do
|
-- Get metadata
|
||||||
local value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
local metadata, err = self.datastore:get("plugin_" .. id, true)
|
||||||
if value == nil then
|
if not metadata then
|
||||||
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
|
self.logger:log(ngx.ERR, err)
|
||||||
end
|
return
|
||||||
self.variables[k] = value
|
end
|
||||||
-- if v.multiple then
|
-- Store variables
|
||||||
-- local multiples, err = utils.get_multiple_variables(k)
|
self.variables = {}
|
||||||
-- if not multiples then
|
self.multiples = {}
|
||||||
-- self.logger:log(ngx.ERR, "can't get " .. k .. " multiple variable : " .. err)
|
local value
|
||||||
-- self.multiples[k] = {}
|
for k, v in pairs(metadata.settings) do
|
||||||
-- else
|
value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||||
-- self.multiples[k] = multiples
|
if value == nil then
|
||||||
-- end
|
self.logger:log(ngx.ERR, "can't get " .. k .. " variable : " .. err)
|
||||||
-- end
|
end
|
||||||
end
|
self.variables[k] = value
|
||||||
-- Is loading
|
end
|
||||||
local is_loading, err = utils.get_variable("IS_LOADING", false)
|
-- Is loading
|
||||||
if is_loading == nil then
|
local is_loading, err = utils.get_variable("IS_LOADING", false)
|
||||||
self.logger:log(ngx.ERR, "can't get IS_LOADING variable : " .. err)
|
if is_loading == nil then
|
||||||
end
|
self.logger:log(ngx.ERR, "can't get IS_LOADING variable : " .. err)
|
||||||
self.is_loading = is_loading == "yes"
|
end
|
||||||
-- Kind of server
|
self.is_loading = is_loading == "yes"
|
||||||
self.kind = "http"
|
-- Kind of server
|
||||||
if ngx.shared.datastore_stream then
|
self.kind = "http"
|
||||||
self.kind = "stream"
|
if ngx.shared.datastore_stream then
|
||||||
end
|
self.kind = "stream"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function plugin:get_id()
|
function plugin:get_id()
|
||||||
return self.id
|
return self.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore 212
|
||||||
function plugin:ret(ret, msg, status, redirect)
|
function plugin:ret(ret, msg, status, redirect)
|
||||||
return { ret = ret, msg = msg, status = status, redirect = redirect }
|
return { ret = ret, msg = msg, status = status, redirect = redirect }
|
||||||
end
|
end
|
||||||
|
|
||||||
return plugin
|
return plugin
|
||||||
|
|
|
@ -1,26 +1,26 @@
|
||||||
local cdatastore = require "bunkerweb.datastore"
|
local cdatastore = require "bunkerweb.datastore"
|
||||||
local mmdb = require "bunkerweb.mmdb"
|
local clogger = require "bunkerweb.logger"
|
||||||
local clogger = require "bunkerweb.logger"
|
local mmdb = require "bunkerweb.mmdb"
|
||||||
|
|
||||||
local ipmatcher = require "resty.ipmatcher"
|
local cjson = require "cjson"
|
||||||
local resolver = require "resty.dns.resolver"
|
local ipmatcher = require "resty.ipmatcher"
|
||||||
local session = require "resty.session"
|
local resolver = require "resty.dns.resolver"
|
||||||
local cjson = require "cjson"
|
local session = require "resty.session"
|
||||||
|
|
||||||
local logger = clogger:new("UTILS")
|
local logger = clogger:new("UTILS")
|
||||||
local datastore = cdatastore:new()
|
local datastore = cdatastore:new()
|
||||||
|
|
||||||
local utils = {}
|
local utils = {}
|
||||||
|
|
||||||
math.randomseed(os.time())
|
math.randomseed(os.time())
|
||||||
|
|
||||||
utils.get_variable = function(var, site_search)
|
utils.get_variable = function(var, site_search)
|
||||||
-- Default site search to true
|
-- Default site search to true
|
||||||
if site_search == nil then
|
if site_search == nil then
|
||||||
site_search = true
|
site_search = true
|
||||||
end
|
end
|
||||||
-- Get global value
|
-- Get global value
|
||||||
local variables, err = datastore:get('variables', true)
|
local variables, err = datastore:get("variables", true)
|
||||||
if not variables then
|
if not variables then
|
||||||
return nil, "can't access variables from datastore : " .. err
|
return nil, "can't access variables from datastore : " .. err
|
||||||
end
|
end
|
||||||
|
@ -33,9 +33,9 @@ utils.get_variable = function(var, site_search)
|
||||||
return value, "success"
|
return value, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.has_variable = function(var, value)
|
utils.has_variable = function(var, value)
|
||||||
-- Get global variable
|
-- Get global variable
|
||||||
local variables, err = datastore:get('variables', true)
|
local variables, err = datastore:get("variables", true)
|
||||||
if not variables then
|
if not variables then
|
||||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||||
end
|
end
|
||||||
|
@ -56,9 +56,9 @@ utils.has_variable = function(var, value)
|
||||||
return variables["global"][var] == value, "success"
|
return variables["global"][var] == value, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.has_not_variable = function(var, value)
|
utils.has_not_variable = function(var, value)
|
||||||
-- Get global variable
|
-- Get global variable
|
||||||
local variables, err = datastore:get('variables', true)
|
local variables, err = datastore:get("variables", true)
|
||||||
if not variables then
|
if not variables then
|
||||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
||||||
end
|
end
|
||||||
|
@ -80,9 +80,9 @@ utils.has_not_variable = function(var, value)
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_multiple_variables = function(vars)
|
utils.get_multiple_variables = function(vars)
|
||||||
local variables, err = datastore:get('variables', true)
|
local variables, err = datastore:get("variables", true)
|
||||||
if not variables then
|
if not variables then
|
||||||
return nil, "can't access variables " .. var .. " from datastore : " .. err
|
return nil, "can't access variables " .. vars .. " from datastore : " .. err
|
||||||
end
|
end
|
||||||
local result = {}
|
local result = {}
|
||||||
-- Loop on scoped vars
|
-- Loop on scoped vars
|
||||||
|
@ -90,7 +90,7 @@ utils.get_multiple_variables = function(vars)
|
||||||
result[scope] = {}
|
result[scope] = {}
|
||||||
-- Loop on vars
|
-- Loop on vars
|
||||||
for variable, value in pairs(scoped_vars) do
|
for variable, value in pairs(scoped_vars) do
|
||||||
for i, var in ipairs(vars) do
|
for _, var in ipairs(vars) do
|
||||||
if variable:find("^" .. var .. "_?[0-9]*$") then
|
if variable:find("^" .. var .. "_?[0-9]*$") then
|
||||||
result[scope][variable] = value
|
result[scope][variable] = value
|
||||||
end
|
end
|
||||||
|
@ -100,7 +100,7 @@ utils.get_multiple_variables = function(vars)
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.is_ip_in_networks = function(ip, networks)
|
utils.is_ip_in_networks = function(ip, networks)
|
||||||
-- Instantiate ipmatcher
|
-- Instantiate ipmatcher
|
||||||
local ipm, err = ipmatcher.new(networks)
|
local ipm, err = ipmatcher.new(networks)
|
||||||
if not ipm then
|
if not ipm then
|
||||||
|
@ -114,15 +114,15 @@ utils.is_ip_in_networks = function(ip, networks)
|
||||||
return matched
|
return matched
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.is_ipv4 = function(ip)
|
utils.is_ipv4 = function(ip)
|
||||||
return ipmatcher.parse_ipv4(ip)
|
return ipmatcher.parse_ipv4(ip)
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.is_ipv6 = function(ip)
|
utils.is_ipv6 = function(ip)
|
||||||
return ipmatcher.parse_ipv6(ip)
|
return ipmatcher.parse_ipv6(ip)
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.ip_is_global = function(ip)
|
utils.ip_is_global = function(ip)
|
||||||
-- Reserved, non public IPs
|
-- Reserved, non public IPs
|
||||||
local reserved_ips = {
|
local reserved_ips = {
|
||||||
"0.0.0.0/8",
|
"0.0.0.0/8",
|
||||||
|
@ -154,7 +154,7 @@ utils.ip_is_global = function(ip)
|
||||||
"2002::/16",
|
"2002::/16",
|
||||||
"fc00::/7",
|
"fc00::/7",
|
||||||
"fe80::/10",
|
"fe80::/10",
|
||||||
"ff00::/8"
|
"ff00::/8",
|
||||||
}
|
}
|
||||||
-- Instantiate ipmatcher
|
-- Instantiate ipmatcher
|
||||||
local ipm, err = ipmatcher.new(reserved_ips)
|
local ipm, err = ipmatcher.new(reserved_ips)
|
||||||
|
@ -169,9 +169,9 @@ utils.ip_is_global = function(ip)
|
||||||
return not matched, "success"
|
return not matched, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_integration = function()
|
utils.get_integration = function()
|
||||||
-- Check if already in datastore
|
-- Check if already in datastore
|
||||||
local integration, err = datastore:get("misc_integration", true)
|
local integration, _ = datastore:get("misc_integration", true)
|
||||||
if integration then
|
if integration then
|
||||||
return integration
|
return integration
|
||||||
end
|
end
|
||||||
|
@ -193,12 +193,12 @@ utils.get_integration = function()
|
||||||
integration = "autoconf"
|
integration = "autoconf"
|
||||||
else
|
else
|
||||||
-- Already present (e.g. : linux)
|
-- Already present (e.g. : linux)
|
||||||
local f, err = io.open("/usr/share/bunkerweb/INTEGRATION", "r")
|
local f, _ = io.open("/usr/share/bunkerweb/INTEGRATION", "r")
|
||||||
if f then
|
if f then
|
||||||
integration = f:read("*a"):gsub("[\n\r]", "")
|
integration = f:read("*a"):gsub("[\n\r]", "")
|
||||||
f:close()
|
f:close()
|
||||||
else
|
else
|
||||||
local f, err = io.open("/etc/os-release", "r")
|
f, _ = io.open("/etc/os-release", "r")
|
||||||
if f then
|
if f then
|
||||||
local data = f:read("*a")
|
local data = f:read("*a")
|
||||||
f:close()
|
f:close()
|
||||||
|
@ -222,9 +222,9 @@ utils.get_integration = function()
|
||||||
return integration
|
return integration
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_version = function()
|
utils.get_version = function()
|
||||||
-- Check if already in datastore
|
-- Check if already in datastore
|
||||||
local version, err = datastore:get("misc_version", true)
|
local version, _ = datastore:get("misc_version", true)
|
||||||
if version then
|
if version then
|
||||||
return version
|
return version
|
||||||
end
|
end
|
||||||
|
@ -244,7 +244,7 @@ utils.get_version = function()
|
||||||
return version
|
return version
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_reason = function(ctx)
|
utils.get_reason = function(ctx)
|
||||||
-- ngx.ctx
|
-- ngx.ctx
|
||||||
if ctx.bw.reason then
|
if ctx.bw.reason then
|
||||||
return ctx.bw.reason
|
return ctx.bw.reason
|
||||||
|
@ -258,7 +258,7 @@ utils.get_reason = function(ctx)
|
||||||
return "modsecurity"
|
return "modsecurity"
|
||||||
end
|
end
|
||||||
-- datastore ban
|
-- datastore ban
|
||||||
local banned, err = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
local banned, _ = datastore:get("bans_ip_" .. ngx.var.remote_addr)
|
||||||
if banned then
|
if banned then
|
||||||
return banned
|
return banned
|
||||||
end
|
end
|
||||||
|
@ -269,9 +269,9 @@ utils.get_reason = function(ctx)
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_resolvers = function()
|
utils.get_resolvers = function()
|
||||||
-- Get resolvers from datastore if existing
|
-- Get resolvers from datastore if existing
|
||||||
local resolvers, err = datastore:get("misc_resolvers", true)
|
local resolvers, _ = datastore:get("misc_resolvers", true)
|
||||||
if resolvers then
|
if resolvers then
|
||||||
return resolvers
|
return resolvers
|
||||||
end
|
end
|
||||||
|
@ -282,7 +282,7 @@ utils.get_resolvers = function()
|
||||||
return "unknown"
|
return "unknown"
|
||||||
end
|
end
|
||||||
-- Make table for resolver1 resolver2 ... string
|
-- Make table for resolver1 resolver2 ... string
|
||||||
local resolvers = {}
|
resolvers = {}
|
||||||
for str_resolver in variables["global"]["DNS_RESOLVERS"]:gmatch("%S+") do
|
for str_resolver in variables["global"]["DNS_RESOLVERS"]:gmatch("%S+") do
|
||||||
table.insert(resolvers, str_resolver)
|
table.insert(resolvers, str_resolver)
|
||||||
end
|
end
|
||||||
|
@ -294,7 +294,7 @@ utils.get_resolvers = function()
|
||||||
return resolvers
|
return resolvers
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_rdns = function(ip)
|
utils.get_rdns = function(ip)
|
||||||
-- Check cache
|
-- Check cache
|
||||||
local cachestore = utils.new_cachestore()
|
local cachestore = utils.new_cachestore()
|
||||||
local ok, value = cachestore:get("rdns_" .. ip)
|
local ok, value = cachestore:get("rdns_" .. ip)
|
||||||
|
@ -312,7 +312,7 @@ utils.get_rdns = function(ip)
|
||||||
local rdns, err = resolver:new {
|
local rdns, err = resolver:new {
|
||||||
nameservers = resolvers,
|
nameservers = resolvers,
|
||||||
retrans = 1,
|
retrans = 1,
|
||||||
timeout = 1000
|
timeout = 1000,
|
||||||
}
|
}
|
||||||
if not rdns then
|
if not rdns then
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -330,21 +330,21 @@ utils.get_rdns = function(ip)
|
||||||
ret_err = answers.errstr
|
ret_err = answers.errstr
|
||||||
end
|
end
|
||||||
-- Extract all PTR
|
-- Extract all PTR
|
||||||
for i, answer in ipairs(answers) do
|
for _, answer in ipairs(answers) do
|
||||||
if answer.ptrdname then
|
if answer.ptrdname then
|
||||||
table.insert(ptrs, answer.ptrdname)
|
table.insert(ptrs, answer.ptrdname)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Save to cache
|
-- Save to cache
|
||||||
local ok, err = cachestore:set("rdns_" .. ip, cjson.encode(ptrs), 3600)
|
ok, err = cachestore:set("rdns_" .. ip, cjson.encode(ptrs), 3600)
|
||||||
if not ok then
|
if not ok then
|
||||||
logger:log(ngx.ERR, "can't set rdns into cachestore : " .. err)
|
logger:log(ngx.ERR, "can't set rdns into cachestore : " .. err)
|
||||||
end
|
end
|
||||||
return ptrs, ret_err
|
return ptrs, ret_err
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_ips = function(fqdn, ipv6)
|
utils.get_ips = function(fqdn, ipv6)
|
||||||
-- Check cache
|
-- Check cache
|
||||||
local cachestore = utils.new_cachestore()
|
local cachestore = utils.new_cachestore()
|
||||||
local ok, value = cachestore:get("dns_" .. fqdn)
|
local ok, value = cachestore:get("dns_" .. fqdn)
|
||||||
|
@ -366,7 +366,7 @@ utils.get_ips = function(fqdn, ipv6)
|
||||||
local res, err = resolver:new {
|
local res, err = resolver:new {
|
||||||
nameservers = resolvers,
|
nameservers = resolvers,
|
||||||
retrans = 1,
|
retrans = 1,
|
||||||
timeout = 1000
|
timeout = 1000,
|
||||||
}
|
}
|
||||||
if not res then
|
if not res then
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -374,6 +374,7 @@ utils.get_ips = function(fqdn, ipv6)
|
||||||
-- Get query types : AAAA and A if using IPv6 / only A if not using IPv6
|
-- Get query types : AAAA and A if using IPv6 / only A if not using IPv6
|
||||||
local qtypes = {}
|
local qtypes = {}
|
||||||
if ipv6 then
|
if ipv6 then
|
||||||
|
-- luacheck: ignore 421
|
||||||
local use_ipv6, err = utils.get_variable("USE_IPV6", false)
|
local use_ipv6, err = utils.get_variable("USE_IPV6", false)
|
||||||
if not use_ipv6 then
|
if not use_ipv6 then
|
||||||
logger:log(ngx.ERR, "can't get USE_IPV6 variable " .. err)
|
logger:log(ngx.ERR, "can't get USE_IPV6 variable " .. err)
|
||||||
|
@ -386,9 +387,10 @@ utils.get_ips = function(fqdn, ipv6)
|
||||||
local res_answers = {}
|
local res_answers = {}
|
||||||
local res_errors = {}
|
local res_errors = {}
|
||||||
local ans_errors = {}
|
local ans_errors = {}
|
||||||
for i, qtype in ipairs(qtypes) do
|
local answers
|
||||||
|
for _, qtype in ipairs(qtypes) do
|
||||||
-- Query FQDN
|
-- Query FQDN
|
||||||
local answers, err = res:query(fqdn, { qtype = qtype }, {})
|
answers, err = res:query(fqdn, { qtype = qtype }, {})
|
||||||
local qtype_str = qtype == res.TYPE_AAAA and "AAAA" or "A"
|
local qtype_str = qtype == res.TYPE_AAAA and "AAAA" or "A"
|
||||||
if not answers then
|
if not answers then
|
||||||
res_errors[qtype_str] = err
|
res_errors[qtype_str] = err
|
||||||
|
@ -403,22 +405,23 @@ utils.get_ips = function(fqdn, ipv6)
|
||||||
end
|
end
|
||||||
-- Extract all IPs
|
-- Extract all IPs
|
||||||
local ips = {}
|
local ips = {}
|
||||||
for i, answers in ipairs(res_answers) do
|
-- luacheck: ignore 421
|
||||||
for j, answer in ipairs(answers) do
|
for _, answers in ipairs(res_answers) do
|
||||||
|
for _, answer in ipairs(answers) do
|
||||||
if answer.address then
|
if answer.address then
|
||||||
table.insert(ips, answer.address)
|
table.insert(ips, answer.address)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Save to cache
|
-- Save to cache
|
||||||
local ok, err = cachestore:set("dns_" .. fqdn, cjson.encode(ips), 3600)
|
ok, err = cachestore:set("dns_" .. fqdn, cjson.encode(ips), 3600)
|
||||||
if not ok then
|
if not ok then
|
||||||
logger:log(ngx.ERR, "can't set dns into cachestore : " .. err)
|
logger:log(ngx.ERR, "can't set dns into cachestore : " .. err)
|
||||||
end
|
end
|
||||||
return ips, cjson.encode(res_errors) .. " " .. cjson.encode(ans_errors)
|
return ips, cjson.encode(res_errors) .. " " .. cjson.encode(ans_errors)
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_country = function(ip)
|
utils.get_country = function(ip)
|
||||||
-- Check if mmdb is loaded
|
-- Check if mmdb is loaded
|
||||||
if not mmdb.country_db then
|
if not mmdb.country_db then
|
||||||
return false, "mmdb country not loaded"
|
return false, "mmdb country not loaded"
|
||||||
|
@ -434,7 +437,7 @@ utils.get_country = function(ip)
|
||||||
return result.country.iso_code, "success"
|
return result.country.iso_code, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_asn = function(ip)
|
utils.get_asn = function(ip)
|
||||||
-- Check if mmdp is loaded
|
-- Check if mmdp is loaded
|
||||||
if not mmdb.asn_db then
|
if not mmdb.asn_db then
|
||||||
return false, "mmdb asn not loaded"
|
return false, "mmdb asn not loaded"
|
||||||
|
@ -450,22 +453,28 @@ utils.get_asn = function(ip)
|
||||||
return result.autonomous_system_number, "success"
|
return result.autonomous_system_number, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.rand = function(nb, no_numbers)
|
utils.rand = function(nb, no_numbers)
|
||||||
local charset = {}
|
local charset = {}
|
||||||
-- lowers, uppers and numbers
|
-- lowers, uppers and numbers
|
||||||
if not no_numbers then
|
if not no_numbers then
|
||||||
for i = 48, 57 do table.insert(charset, string.char(i)) end
|
for i = 48, 57 do
|
||||||
|
table.insert(charset, string.char(i))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for i = 65, 90 do
|
||||||
|
table.insert(charset, string.char(i))
|
||||||
|
end
|
||||||
|
for i = 97, 122 do
|
||||||
|
table.insert(charset, string.char(i))
|
||||||
end
|
end
|
||||||
for i = 65, 90 do table.insert(charset, string.char(i)) end
|
|
||||||
for i = 97, 122 do table.insert(charset, string.char(i)) end
|
|
||||||
local result = ""
|
local result = ""
|
||||||
for i = 1, nb do
|
for _ = 1, nb do
|
||||||
result = result .. charset[math.random(1, #charset)]
|
result = result .. charset[math.random(1, #charset)]
|
||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_deny_status = function(ctx)
|
utils.get_deny_status = function(ctx)
|
||||||
-- Stream case
|
-- Stream case
|
||||||
if ctx.bw and ctx.bw.kind == "stream" then
|
if ctx.bw and ctx.bw.kind == "stream" then
|
||||||
return 444
|
return 444
|
||||||
|
@ -479,10 +488,10 @@ utils.get_deny_status = function(ctx)
|
||||||
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
return tonumber(variables["global"]["DENY_HTTP_STATUS"])
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.check_session = function(ctx)
|
utils.check_session = function(ctx)
|
||||||
local _session, err, exists, refreshed = session.start({ audience = "metadata" })
|
local _session, _, exists, _ = session.start({ audience = "metadata" })
|
||||||
if exists then
|
if exists then
|
||||||
for i, check in ipairs(ctx.bw.sessions_checks) do
|
for _, check in ipairs(ctx.bw.sessions_checks) do
|
||||||
local key = check[1]
|
local key = check[1]
|
||||||
local value = check[2]
|
local value = check[2]
|
||||||
if _session:get(key) ~= value then
|
if _session:get(key) ~= value then
|
||||||
|
@ -496,7 +505,7 @@ utils.check_session = function(ctx)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for i, check in ipairs(ctx.bw.sessions_checks) do
|
for _, check in ipairs(ctx.bw.sessions_checks) do
|
||||||
_session:set(check[1], check[2])
|
_session:set(check[1], check[2])
|
||||||
end
|
end
|
||||||
local ok, err = _session:save()
|
local ok, err = _session:save()
|
||||||
|
@ -509,7 +518,7 @@ utils.check_session = function(ctx)
|
||||||
return true, exists
|
return true, exists
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_session = function(audience, ctx)
|
utils.get_session = function(audience, ctx)
|
||||||
-- Check session
|
-- Check session
|
||||||
if not ctx.bw.sessions_is_checked then
|
if not ctx.bw.sessions_is_checked then
|
||||||
local ok, err = utils.check_session(ctx)
|
local ok, err = utils.check_session(ctx)
|
||||||
|
@ -518,14 +527,15 @@ utils.get_session = function(audience, ctx)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Open session with specific audience
|
-- Open session with specific audience
|
||||||
local _session, err, exists = session.open({ audience = audience })
|
local _session, err, _ = session.open({ audience = audience })
|
||||||
if err then
|
if err then
|
||||||
logger:log(ngx.INFO, "session:open() error : " .. err)
|
logger:log(ngx.INFO, "session:open() error : " .. err)
|
||||||
end
|
end
|
||||||
return _session
|
return _session
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_session_data = function(_session, site, ctx)
|
-- luacheck: ignore 214
|
||||||
|
utils.get_session_data = function(_session, site, ctx)
|
||||||
local site_only = site == nil or site
|
local site_only = site == nil or site
|
||||||
local data = _session:get_data()
|
local data = _session:get_data()
|
||||||
if site_only then
|
if site_only then
|
||||||
|
@ -534,7 +544,8 @@ utils.get_session_data = function(_session, site, ctx)
|
||||||
return data
|
return data
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.set_session_data = function(_session, data, site, ctx)
|
-- luacheck: ignore 214
|
||||||
|
utils.set_session_data = function(_session, data, site, ctx)
|
||||||
local site_only = site == nil or site
|
local site_only = site == nil or site
|
||||||
if site_only then
|
if site_only then
|
||||||
local all_data = _session:get_data()
|
local all_data = _session:get_data()
|
||||||
|
@ -546,7 +557,7 @@ utils.set_session_data = function(_session, data, site, ctx)
|
||||||
return _session:save()
|
return _session:save()
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.is_banned = function(ip)
|
utils.is_banned = function(ip)
|
||||||
-- Check on local datastore
|
-- Check on local datastore
|
||||||
local reason, err = datastore:get("bans_ip_" .. ip)
|
local reason, err = datastore:get("bans_ip_" .. ip)
|
||||||
if not reason and err ~= "not found" then
|
if not reason and err ~= "not found" then
|
||||||
|
@ -599,7 +610,7 @@ utils.is_banned = function(ip)
|
||||||
elseif data[1] ~= ngx.null then
|
elseif data[1] ~= ngx.null then
|
||||||
clusterstore:close()
|
clusterstore:close()
|
||||||
-- Update local cache
|
-- Update local cache
|
||||||
local ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
|
ok, err = datastore:set("bans_ip_" .. ip, data[1], data[2])
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, "datastore:set() error : " .. err
|
return nil, "datastore:set() error : " .. err
|
||||||
end
|
end
|
||||||
|
@ -609,7 +620,7 @@ utils.is_banned = function(ip)
|
||||||
return false, "not banned"
|
return false, "not banned"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.add_ban = function(ip, reason, ttl)
|
utils.add_ban = function(ip, reason, ttl)
|
||||||
-- Set on local datastore
|
-- Set on local datastore
|
||||||
local ok, err = datastore:set("bans_ip_" .. ip, reason, ttl)
|
local ok, err = datastore:set("bans_ip_" .. ip, reason, ttl)
|
||||||
if not ok then
|
if not ok then
|
||||||
|
@ -624,12 +635,12 @@ utils.add_ban = function(ip, reason, ttl)
|
||||||
end
|
end
|
||||||
-- Connect
|
-- Connect
|
||||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||||
local ok, err = clusterstore:connect()
|
ok, err = clusterstore:connect()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, "can't connect to redis server : " .. err
|
return false, "can't connect to redis server : " .. err
|
||||||
end
|
end
|
||||||
-- SET call
|
-- SET call
|
||||||
local ok, err = clusterstore:call("set", "bans_ip_" .. ip, reason, "EX", ttl)
|
ok, err = clusterstore:call("set", "bans_ip_" .. ip, reason, "EX", ttl)
|
||||||
if not ok then
|
if not ok then
|
||||||
clusterstore:close()
|
clusterstore:close()
|
||||||
return false, "redis SET failed : " .. err
|
return false, "redis SET failed : " .. err
|
||||||
|
@ -638,7 +649,7 @@ utils.add_ban = function(ip, reason, ttl)
|
||||||
return true, "success"
|
return true, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.new_cachestore = function()
|
utils.new_cachestore = function()
|
||||||
-- Check if redis is used
|
-- Check if redis is used
|
||||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||||
if not use_redis then
|
if not use_redis then
|
||||||
|
@ -650,7 +661,7 @@ utils.new_cachestore = function()
|
||||||
return require "bunkerweb.cachestore":new(use_redis, true)
|
return require "bunkerweb.cachestore":new(use_redis, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.regex_match = function(str, regex, options)
|
utils.regex_match = function(str, regex, options)
|
||||||
local all_options = "o"
|
local all_options = "o"
|
||||||
if options then
|
if options then
|
||||||
all_options = all_options .. options
|
all_options = all_options .. options
|
||||||
|
@ -663,7 +674,7 @@ utils.regex_match = function(str, regex, options)
|
||||||
return match
|
return match
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_phases = function()
|
utils.get_phases = function()
|
||||||
return {
|
return {
|
||||||
"init",
|
"init",
|
||||||
"init_worker",
|
"init_worker",
|
||||||
|
@ -673,18 +684,18 @@ utils.get_phases = function()
|
||||||
"log",
|
"log",
|
||||||
"preread",
|
"preread",
|
||||||
"log_stream",
|
"log_stream",
|
||||||
"log_default"
|
"log_default",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.is_cosocket_available = function()
|
utils.is_cosocket_available = function()
|
||||||
local phases = {
|
local phases = {
|
||||||
"timer",
|
"timer",
|
||||||
"access",
|
"access",
|
||||||
"preread"
|
"preread",
|
||||||
}
|
}
|
||||||
local current_phase = ngx.get_phase()
|
local current_phase = ngx.get_phase()
|
||||||
for i, phase in ipairs(phases) do
|
for _, phase in ipairs(phases) do
|
||||||
if current_phase == phase then
|
if current_phase == phase then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -692,8 +703,8 @@ utils.is_cosocket_available = function()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.kill_all_threads = function(threads)
|
utils.kill_all_threads = function(threads)
|
||||||
for i, thread in ipairs(threads) do
|
for _, thread in ipairs(threads) do
|
||||||
local ok, err = ngx.thread.kill(thread)
|
local ok, err = ngx.thread.kill(thread)
|
||||||
if not ok then
|
if not ok then
|
||||||
logger:log(ngx.ERR, "error while killing thread : " .. err)
|
logger:log(ngx.ERR, "error while killing thread : " .. err)
|
||||||
|
@ -701,7 +712,7 @@ utils.kill_all_threads = function(threads)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
utils.get_ctx_obj = function(obj)
|
utils.get_ctx_obj = function(obj)
|
||||||
if ngx.ctx and ngx.ctx.bw then
|
if ngx.ctx and ngx.ctx.bw then
|
||||||
return ngx.ctx.bw[obj]
|
return ngx.ctx.bw[obj]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
local class = require "middleclass"
|
local base64 = require "base64"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local captcha = require "antibot.captcha"
|
||||||
local utils = require "bunkerweb.utils"
|
local cjson = require "cjson"
|
||||||
local cjson = require "cjson"
|
local class = require "middleclass"
|
||||||
local captcha = require "antibot.captcha"
|
local http = require "resty.http"
|
||||||
local base64 = require "base64"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local sha256 = require "resty.sha256"
|
local sha256 = require "resty.sha256"
|
||||||
local str = require "resty.string"
|
local str = require "resty.string"
|
||||||
local http = require "resty.http"
|
local utils = require "bunkerweb.utils"
|
||||||
local template = nil
|
local template = nil
|
||||||
if ngx.shared.datastore then
|
if ngx.shared.datastore then
|
||||||
template = require "resty.template"
|
template = require "resty.template"
|
||||||
|
@ -51,40 +51,41 @@ function antibot:header()
|
||||||
return self:ret(true, "Not antibot uri")
|
return self:ret(true, "Not antibot uri")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
local header = "Content-Security-Policy"
|
local header = "Content-Security-Policy"
|
||||||
if self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes" then
|
if self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes" then
|
||||||
header = header .. "-Report-Only"
|
header = header .. "-Report-Only"
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.session_data.type == "recaptcha" then
|
if self.session_data.type == "recaptcha" then
|
||||||
ngx.header[header] =
|
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
.. self.session_data.nonce_script
|
||||||
self.session_data.nonce_script ..
|
.. "' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'unsafe-inline' http: https:;"
|
||||||
"' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ 'unsafe-inline' http: https:; img-src https://www.gstatic.com/recaptcha/ 'self' data:; frame-src https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/; style-src 'self' 'nonce-" ..
|
.. " img-src https://www.gstatic.com/recaptcha/ 'self' data:; "
|
||||||
self.session_data.nonce_style ..
|
.. " frame-src https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/;"
|
||||||
"'; font-src 'self' https://fonts.gstatic.com data:; base-uri 'self';"
|
.. " style-src 'self' 'nonce-"
|
||||||
|
.. self.session_data.nonce_style
|
||||||
|
.. "'; font-src 'self' https://fonts.gstatic.com data:; base-uri 'self';"
|
||||||
elseif self.session_data.type == "hcaptcha" then
|
elseif self.session_data.type == "hcaptcha" then
|
||||||
ngx.header[header] =
|
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
.. self.session_data.nonce_script
|
||||||
self.session_data.nonce_script ..
|
.. "' https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' http: https:; img-src 'self' data:;"
|
||||||
"' https://hcaptcha.com https://*.hcaptcha.com 'unsafe-inline' http: https:; img-src 'self' data:; frame-src https://hcaptcha.com https://*.hcaptcha.com; style-src 'self' 'nonce-" ..
|
.. " frame-src https://hcaptcha.com https://*.hcaptcha.com; style-src 'self' 'nonce-"
|
||||||
self.session_data.nonce_style ..
|
.. self.session_data.nonce_style
|
||||||
"' https://hcaptcha.com https://*.hcaptcha.com; connect-src https://hcaptcha.com https://*.hcaptcha.com; font-src 'self' data:; base-uri 'self';"
|
.. "' https://hcaptcha.com https://*.hcaptcha.com; connect-src https://hcaptcha.com https://*.hcaptcha.com; "
|
||||||
|
.. " font-src 'self' data:; base-uri 'self';"
|
||||||
elseif self.session_data.type == "turnstile" then
|
elseif self.session_data.type == "turnstile" then
|
||||||
ngx.header[header] =
|
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
.. self.session_data.nonce_script
|
||||||
self.session_data.nonce_script ..
|
.. "' https://challenges.cloudflare.com 'unsafe-inline' http: https:; img-src 'self' data:;"
|
||||||
"' https://challenges.cloudflare.com 'unsafe-inline' http: https:; img-src 'self' data:; frame-src https://challenges.cloudflare.com; style-src 'self' 'nonce-" ..
|
.. " frame-src https://challenges.cloudflare.com; style-src 'self' 'nonce-"
|
||||||
self.session_data.nonce_style ..
|
.. self.session_data.nonce_style
|
||||||
"'; font-src 'self' data:; base-uri 'self';"
|
.. "'; font-src 'self' data:; base-uri 'self';"
|
||||||
else
|
else
|
||||||
ngx.header[header] =
|
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||||
"default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-" ..
|
.. self.session_data.nonce_script
|
||||||
self.session_data.nonce_script ..
|
.. "' 'unsafe-inline' http: https:; img-src 'self' data:; style-src 'self' 'nonce-"
|
||||||
"' 'unsafe-inline' http: https:; img-src 'self' data:; style-src 'self' 'nonce-" ..
|
.. self.session_data.nonce_style
|
||||||
self.session_data.nonce_style ..
|
.. "'; font-src 'self' data:; base-uri 'self';"
|
||||||
"'; font-src 'self' data:; base-uri 'self';"
|
|
||||||
end
|
end
|
||||||
return self:ret(true, "Successfully overridden CSP header")
|
return self:ret(true, "Successfully overridden CSP header")
|
||||||
end
|
end
|
||||||
|
@ -138,6 +139,7 @@ function antibot:access()
|
||||||
|
|
||||||
-- Check challenge
|
-- Check challenge
|
||||||
if self.ctx.bw.request_method == "POST" then
|
if self.ctx.bw.request_method == "POST" then
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, err, redirect = self:check_challenge()
|
local ok, err, redirect = self:check_challenge()
|
||||||
local set_ok, set_err = self:set_session_data()
|
local set_ok, set_err = self:set_session_data()
|
||||||
if not set_ok then
|
if not set_ok then
|
||||||
|
@ -152,7 +154,7 @@ function antibot:access()
|
||||||
return self:ret(true, "check challenge redirect : " .. redirect, nil, redirect)
|
return self:ret(true, "check challenge redirect : " .. redirect, nil, redirect)
|
||||||
end
|
end
|
||||||
self:prepare_challenge()
|
self:prepare_challenge()
|
||||||
local ok, err = self:set_session_data()
|
ok, err = self:set_session_data()
|
||||||
if not ok then
|
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, ngx.HTTP_INTERNAL_SERVER_ERROR)
|
||||||
end
|
end
|
||||||
|
@ -215,7 +217,9 @@ function antibot:check_session()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- Check if new prepare is needed
|
-- Check if new prepare is needed
|
||||||
if not resolved and (time_resolve > time or time - time_resolve > tonumber(self.variables["ANTIBOT_TIME_RESOLVE"])) then
|
if
|
||||||
|
not resolved and (time_resolve > time or time - time_resolve > tonumber(self.variables["ANTIBOT_TIME_RESOLVE"]))
|
||||||
|
then
|
||||||
self.session_data = {}
|
self.session_data = {}
|
||||||
self.session_updated = true
|
self.session_updated = true
|
||||||
return
|
return
|
||||||
|
@ -312,7 +316,7 @@ function antibot:check_challenge()
|
||||||
return nil, "challenge not prepared"
|
return nil, "challenge not prepared"
|
||||||
end
|
end
|
||||||
|
|
||||||
local resolved = false
|
local resolved
|
||||||
|
|
||||||
self.session_data.prepared = false
|
self.session_data.prepared = false
|
||||||
self.session_updated = true
|
self.session_updated = true
|
||||||
|
@ -364,12 +368,15 @@ function antibot:check_challenge()
|
||||||
end
|
end
|
||||||
local res, err = httpc:request_uri("https://www.google.com/recaptcha/api/siteverify", {
|
local res, err = httpc:request_uri("https://www.google.com/recaptcha/api/siteverify", {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
body = "secret=" ..
|
body = "secret="
|
||||||
self.variables["ANTIBOT_RECAPTCHA_SECRET"] ..
|
.. self.variables["ANTIBOT_RECAPTCHA_SECRET"]
|
||||||
"&response=" .. args["token"] .. "&remoteip=" .. self.ctx.bw.remote_addr,
|
.. "&response="
|
||||||
|
.. args["token"]
|
||||||
|
.. "&remoteip="
|
||||||
|
.. self.ctx.bw.remote_addr,
|
||||||
headers = {
|
headers = {
|
||||||
["Content-Type"] = "application/x-www-form-urlencoded"
|
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
httpc:close()
|
httpc:close()
|
||||||
if not res then
|
if not res then
|
||||||
|
@ -400,12 +407,15 @@ function antibot:check_challenge()
|
||||||
end
|
end
|
||||||
local res, err = httpc:request_uri("https://hcaptcha.com/siteverify", {
|
local res, err = httpc:request_uri("https://hcaptcha.com/siteverify", {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
body = "secret=" ..
|
body = "secret="
|
||||||
self.variables["ANTIBOT_HCAPTCHA_SECRET"] ..
|
.. self.variables["ANTIBOT_HCAPTCHA_SECRET"]
|
||||||
"&response=" .. args["token"] .. "&remoteip=" .. self.ctx.bw.remote_addr,
|
.. "&response="
|
||||||
|
.. args["token"]
|
||||||
|
.. "&remoteip="
|
||||||
|
.. self.ctx.bw.remote_addr,
|
||||||
headers = {
|
headers = {
|
||||||
["Content-Type"] = "application/x-www-form-urlencoded"
|
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
httpc:close()
|
httpc:close()
|
||||||
if not res then
|
if not res then
|
||||||
|
@ -413,7 +423,7 @@ function antibot:check_challenge()
|
||||||
end
|
end
|
||||||
local ok, hdata = pcall(cjson.decode, res.body)
|
local ok, hdata = pcall(cjson.decode, res.body)
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, "error while decoding JSON from hCaptcha API : " .. data, nil
|
return nil, "error while decoding JSON from hCaptcha API : " .. hdata, nil
|
||||||
end
|
end
|
||||||
if not hdata.success then
|
if not hdata.success then
|
||||||
return false, "client failed challenge", nil
|
return false, "client failed challenge", nil
|
||||||
|
@ -436,12 +446,15 @@ function antibot:check_challenge()
|
||||||
end
|
end
|
||||||
local res, err = httpc:request_uri("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
local res, err = httpc:request_uri("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
|
||||||
method = "POST",
|
method = "POST",
|
||||||
body = "secret=" ..
|
body = "secret="
|
||||||
self.variables["ANTIBOT_TURNSTILE_SECRET"] ..
|
.. self.variables["ANTIBOT_TURNSTILE_SECRET"]
|
||||||
"&response=" .. args["token"] .. "&remoteip=" .. self.ctx.bw.remote_addr,
|
.. "&response="
|
||||||
|
.. args["token"]
|
||||||
|
.. "&remoteip="
|
||||||
|
.. self.ctx.bw.remote_addr,
|
||||||
headers = {
|
headers = {
|
||||||
["Content-Type"] = "application/x-www-form-urlencoded"
|
["Content-Type"] = "application/x-www-form-urlencoded",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
httpc:close()
|
httpc:close()
|
||||||
if not res then
|
if not res then
|
||||||
|
@ -449,7 +462,7 @@ function antibot:check_challenge()
|
||||||
end
|
end
|
||||||
local ok, tdata = pcall(cjson.decode, res.body)
|
local ok, tdata = pcall(cjson.decode, res.body)
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, "error while decoding JSON from Turnstile API : " .. data, nil
|
return nil, "error while decoding JSON from Turnstile API : " .. tdata, nil
|
||||||
end
|
end
|
||||||
if not tdata.success then
|
if not tdata.success then
|
||||||
return false, "client failed challenge", nil
|
return false, "client failed challenge", nil
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local badbehavior = class("badbehavior", plugin)
|
local badbehavior = class("badbehavior", plugin)
|
||||||
|
|
||||||
|
@ -23,14 +23,20 @@ function badbehavior:log()
|
||||||
return self:ret(true, "not increasing counter")
|
return self:ret(true, "not increasing counter")
|
||||||
end
|
end
|
||||||
-- Check if we are already banned
|
-- Check if we are already banned
|
||||||
local banned, err = self.datastore:get("bans_ip_" .. self.ctx.bw.remote_addr)
|
local banned, _ = self.datastore:get("bans_ip_" .. self.ctx.bw.remote_addr)
|
||||||
if banned then
|
if banned then
|
||||||
return self:ret(true, "already banned")
|
return self:ret(true, "already banned")
|
||||||
end
|
end
|
||||||
-- Call increase function later and with cosocket enabled
|
-- Call increase function later and with cosocket enabled
|
||||||
local ok, err = ngx.timer.at(0, badbehavior.increase, self.ctx.bw.remote_addr,
|
local ok, err = ngx.timer.at(
|
||||||
tonumber(self.variables["BAD_BEHAVIOR_COUNT_TIME"]), tonumber(self.variables["BAD_BEHAVIOR_BAN_TIME"]),
|
0,
|
||||||
tonumber(self.variables["BAD_BEHAVIOR_THRESHOLD"]), self.use_redis)
|
badbehavior.increase,
|
||||||
|
self.ctx.bw.remote_addr,
|
||||||
|
tonumber(self.variables["BAD_BEHAVIOR_COUNT_TIME"]),
|
||||||
|
tonumber(self.variables["BAD_BEHAVIOR_BAN_TIME"]),
|
||||||
|
tonumber(self.variables["BAD_BEHAVIOR_THRESHOLD"]),
|
||||||
|
self.use_redis
|
||||||
|
)
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:ret(false, "can't create increase timer : " .. err)
|
return self:ret(false, "can't create increase timer : " .. err)
|
||||||
end
|
end
|
||||||
|
@ -45,6 +51,7 @@ function badbehavior:log_stream()
|
||||||
return self:log()
|
return self:log()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore 212
|
||||||
function badbehavior.increase(premature, ip, count_time, ban_time, threshold, use_redis)
|
function badbehavior.increase(premature, ip, count_time, ban_time, threshold, use_redis)
|
||||||
-- Instantiate objects
|
-- Instantiate objects
|
||||||
local logger = require "bunkerweb.logger":new("badbehavior")
|
local logger = require "bunkerweb.logger":new("badbehavior")
|
||||||
|
@ -84,16 +91,28 @@ function badbehavior.increase(premature, ip, count_time, ban_time, threshold, us
|
||||||
end
|
end
|
||||||
-- Store local ban
|
-- Store local ban
|
||||||
if counter > threshold then
|
if counter > threshold then
|
||||||
local ok, err = utils.add_ban(ip, "bad behavior", ban_time)
|
ok, err = utils.add_ban(ip, "bad behavior", ban_time)
|
||||||
if not ok then
|
if not ok then
|
||||||
logger:log(ngx.ERR, "(increase) can't save ban : " .. err)
|
logger:log(ngx.ERR, "(increase) can't save ban : " .. err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
logger:log(ngx.WARN,
|
logger:log(
|
||||||
"IP " .. ip .. " is banned for " .. ban_time .. "s (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
ngx.WARN,
|
||||||
|
"IP "
|
||||||
|
.. ip
|
||||||
|
.. " is banned for "
|
||||||
|
.. ban_time
|
||||||
|
.. "s ("
|
||||||
|
.. tostring(counter)
|
||||||
|
.. "/"
|
||||||
|
.. tostring(threshold)
|
||||||
|
.. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
logger:log(ngx.NOTICE,
|
logger:log(
|
||||||
"increased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
ngx.NOTICE,
|
||||||
|
"increased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
||||||
|
@ -126,7 +145,7 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
||||||
-- Store local counter
|
-- Store local counter
|
||||||
if counter <= 0 then
|
if counter <= 0 then
|
||||||
counter = 0
|
counter = 0
|
||||||
local ok, err = datastore:delete("plugin_badbehavior_count_" .. ip)
|
datastore:delete("plugin_badbehavior_count_" .. ip)
|
||||||
else
|
else
|
||||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
|
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter, count_time)
|
||||||
if not ok then
|
if not ok then
|
||||||
|
@ -134,8 +153,10 @@ function badbehavior.decrease(premature, ip, count_time, threshold, use_redis)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
logger:log(ngx.NOTICE,
|
logger:log(
|
||||||
"decreased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
ngx.NOTICE,
|
||||||
|
"decreased counter for IP " .. ip .. " (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function badbehavior.redis_increase(ip, count_time, ban_time)
|
function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||||
|
@ -168,9 +189,8 @@ function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
-- Execute LUA script
|
-- Execute LUA script
|
||||||
local counter, err = clusterstore:call("eval", redis_script, 2, "plugin_bad_behavior_" .. ip, "bans_ip" .. ip,
|
local counter, err =
|
||||||
count_time,
|
clusterstore:call("eval", redis_script, 2, "plugin_bad_behavior_" .. ip, "bans_ip" .. ip, count_time, ban_time)
|
||||||
ban_time)
|
|
||||||
if not counter then
|
if not counter then
|
||||||
clusterstore:close()
|
clusterstore:close()
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
|
||||||
local utils = require "bunkerweb.utils"
|
|
||||||
local ipmatcher = require "resty.ipmatcher"
|
local ipmatcher = require "resty.ipmatcher"
|
||||||
|
local plugin = require "bunkerweb.plugin"
|
||||||
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local blacklist = class("blacklist", plugin)
|
local blacklist = class("blacklist", plugin)
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ function blacklist:init()
|
||||||
}
|
}
|
||||||
local i = 0
|
local i = 0
|
||||||
for kind, _ in pairs(blacklists) do
|
for kind, _ in pairs(blacklists) do
|
||||||
local f, err = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
local f, _ = io.open("/var/cache/bunkerweb/blacklist/" .. kind .. ".list", "r")
|
||||||
if f then
|
if f then
|
||||||
for line in f:lines() do
|
for line in f:lines() do
|
||||||
table.insert(blacklists[kind], line)
|
table.insert(blacklists[kind], line)
|
||||||
|
@ -102,7 +102,7 @@ function blacklist:access()
|
||||||
end
|
end
|
||||||
-- Check the caches
|
-- Check the caches
|
||||||
local checks = {
|
local checks = {
|
||||||
["IP"] = "ip" .. self.ctx.bw.remote_addr
|
["IP"] = "ip" .. self.ctx.bw.remote_addr,
|
||||||
}
|
}
|
||||||
if self.ctx.bw.http_user_agent then
|
if self.ctx.bw.http_user_agent then
|
||||||
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
||||||
|
@ -113,14 +113,18 @@ function blacklist:access()
|
||||||
local already_cached = {
|
local already_cached = {
|
||||||
["IP"] = false,
|
["IP"] = false,
|
||||||
["URI"] = false,
|
["URI"] = false,
|
||||||
["UA"] = false
|
["UA"] = false,
|
||||||
}
|
}
|
||||||
for k, v in pairs(checks) do
|
for k, v in pairs(checks) do
|
||||||
local ok, cached = self:is_in_cache(v)
|
local ok, cached = self:is_in_cache(v)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
self.logger:log(ngx.ERR, "error while checking cache : " .. cached)
|
||||||
elseif cached and cached ~= "ok" then
|
elseif cached and cached ~= "ok" then
|
||||||
return self:ret(true, k .. " is in cached blacklist (info : " .. cached .. ")", utils.get_deny_status(self.ctx))
|
return self:ret(
|
||||||
|
true,
|
||||||
|
k .. " is in cached blacklist (info : " .. cached .. ")",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
if ok and cached then
|
if ok and cached then
|
||||||
already_cached[k] = true
|
already_cached[k] = true
|
||||||
|
@ -131,18 +135,23 @@ function blacklist:access()
|
||||||
return self:ret(false, "lists is nil")
|
return self:ret(false, "lists is nil")
|
||||||
end
|
end
|
||||||
-- Perform checks
|
-- Perform checks
|
||||||
for k, v in pairs(checks) do
|
for k, _ in pairs(checks) do
|
||||||
if not already_cached[k] then
|
if not already_cached[k] then
|
||||||
local ok, blacklisted = self:is_blacklisted(k)
|
local ok, blacklisted = self:is_blacklisted(k)
|
||||||
if ok == nil then
|
if ok == nil then
|
||||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
|
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is blacklisted : " .. blacklisted)
|
||||||
else
|
else
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), blacklisted)
|
local ok, err = self:add_to_cache(self:kind_to_ele(k), blacklisted)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||||
end
|
end
|
||||||
if blacklisted ~= "ok" then
|
if blacklisted ~= "ok" then
|
||||||
return self:ret(true, k .. " is blacklisted (info : " .. blacklisted .. ")", utils.get_deny_status(self.ctx))
|
return self:ret(
|
||||||
|
true,
|
||||||
|
k .. " is blacklisted (info : " .. blacklisted .. ")",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -205,11 +214,11 @@ function blacklist:is_blacklisted_ip()
|
||||||
end
|
end
|
||||||
if not match then
|
if not match then
|
||||||
-- Check if IP is in blacklist
|
-- Check if IP is in blacklist
|
||||||
local ipm, err = ipmatcher.new(self.lists["IP"])
|
ipm, err = ipmatcher.new(self.lists["IP"])
|
||||||
if not ipm then
|
if not ipm then
|
||||||
return nil, err
|
return nil, err
|
||||||
end
|
end
|
||||||
local match, err = ipm:match(self.ctx.bw.remote_addr)
|
match, err = ipm:match(self.ctx.bw.remote_addr)
|
||||||
if err then
|
if err then
|
||||||
return nil, err
|
return nil, err
|
||||||
end
|
end
|
||||||
|
@ -225,13 +234,14 @@ function blacklist:is_blacklisted_ip()
|
||||||
end
|
end
|
||||||
if check_rdns then
|
if check_rdns then
|
||||||
-- Get rDNS
|
-- Get rDNS
|
||||||
|
-- luacheck: ignore 421
|
||||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||||
if rdns_list then
|
if rdns_list then
|
||||||
-- Check if rDNS is in ignore list
|
-- Check if rDNS is in ignore list
|
||||||
local ignore = false
|
local ignore = false
|
||||||
for i, rdns in ipairs(rdns_list) do
|
for _, rdns in ipairs(rdns_list) do
|
||||||
for j, suffix in ipairs(self.lists["IGNORE_RDNS"]) do
|
for _, suffix in ipairs(self.lists["IGNORE_RDNS"]) do
|
||||||
if rdns:sub(- #suffix) == suffix then
|
if rdns:sub(-#suffix) == suffix then
|
||||||
ignore = true
|
ignore = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -239,9 +249,9 @@ function blacklist:is_blacklisted_ip()
|
||||||
end
|
end
|
||||||
-- Check if rDNS is in blacklist
|
-- Check if rDNS is in blacklist
|
||||||
if not ignore then
|
if not ignore then
|
||||||
for i, rdns in ipairs(rdns_list) do
|
for _, rdns in ipairs(rdns_list) do
|
||||||
for j, suffix in ipairs(self.lists["RDNS"]) do
|
for _, suffix in ipairs(self.lists["RDNS"]) do
|
||||||
if rdns:sub(- #suffix) == suffix then
|
if rdns:sub(-#suffix) == suffix then
|
||||||
return true, "rDNS " .. suffix
|
return true, "rDNS " .. suffix
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -259,7 +269,7 @@ function blacklist:is_blacklisted_ip()
|
||||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||||
else
|
else
|
||||||
local ignore = false
|
local ignore = false
|
||||||
for i, ignore_asn in ipairs(self.lists["IGNORE_ASN"]) do
|
for _, ignore_asn in ipairs(self.lists["IGNORE_ASN"]) do
|
||||||
if ignore_asn == tostring(asn) then
|
if ignore_asn == tostring(asn) then
|
||||||
ignore = true
|
ignore = true
|
||||||
break
|
break
|
||||||
|
@ -267,7 +277,7 @@ function blacklist:is_blacklisted_ip()
|
||||||
end
|
end
|
||||||
-- Check if ASN is in blacklist
|
-- Check if ASN is in blacklist
|
||||||
if not ignore then
|
if not ignore then
|
||||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||||
if bl_asn == tostring(asn) then
|
if bl_asn == tostring(asn) then
|
||||||
return true, "ASN " .. bl_asn
|
return true, "ASN " .. bl_asn
|
||||||
end
|
end
|
||||||
|
@ -283,7 +293,7 @@ end
|
||||||
function blacklist:is_blacklisted_uri()
|
function blacklist:is_blacklisted_uri()
|
||||||
-- Check if URI is in ignore list
|
-- Check if URI is in ignore list
|
||||||
local ignore = false
|
local ignore = false
|
||||||
for i, ignore_uri in ipairs(self.lists["IGNORE_URI"]) do
|
for _, ignore_uri in ipairs(self.lists["IGNORE_URI"]) do
|
||||||
if utils.regex_match(self.ctx.bw.uri, ignore_uri) then
|
if utils.regex_match(self.ctx.bw.uri, ignore_uri) then
|
||||||
ignore = true
|
ignore = true
|
||||||
break
|
break
|
||||||
|
@ -291,7 +301,7 @@ function blacklist:is_blacklisted_uri()
|
||||||
end
|
end
|
||||||
-- Check if URI is in blacklist
|
-- Check if URI is in blacklist
|
||||||
if not ignore then
|
if not ignore then
|
||||||
for i, uri in ipairs(self.lists["URI"]) do
|
for _, uri in ipairs(self.lists["URI"]) do
|
||||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||||
return true, "URI " .. uri
|
return true, "URI " .. uri
|
||||||
end
|
end
|
||||||
|
@ -304,7 +314,7 @@ end
|
||||||
function blacklist:is_blacklisted_ua()
|
function blacklist:is_blacklisted_ua()
|
||||||
-- Check if UA is in ignore list
|
-- Check if UA is in ignore list
|
||||||
local ignore = false
|
local ignore = false
|
||||||
for i, ignore_ua in ipairs(self.lists["IGNORE_USER_AGENT"]) do
|
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 utils.regex_match(self.ctx.bw.http_user_agent, ignore_ua) then
|
||||||
ignore = true
|
ignore = true
|
||||||
break
|
break
|
||||||
|
@ -312,7 +322,7 @@ function blacklist:is_blacklisted_ua()
|
||||||
end
|
end
|
||||||
-- Check if UA is in blacklist
|
-- Check if UA is in blacklist
|
||||||
if not ignore then
|
if not ignore then
|
||||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||||
return true, "UA " .. ua
|
return true, "UA " .. ua
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
local class = require "middleclass"
|
local cjson = require "cjson"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local class = require "middleclass"
|
||||||
local utils = require "bunkerweb.utils"
|
local http = require "resty.http"
|
||||||
local cjson = require "cjson"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local http = require "resty.http"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local bunkernet = class("bunkernet", plugin)
|
local bunkernet = class("bunkernet", plugin)
|
||||||
|
|
||||||
|
@ -49,12 +49,15 @@ function bunkernet:init_worker()
|
||||||
return self:ret(false, "missing instance ID")
|
return self:ret(false, "missing instance ID")
|
||||||
end
|
end
|
||||||
-- Send ping request
|
-- Send ping request
|
||||||
local ok, err, status, data = self:ping()
|
local ok, err, status, _ = self:ping()
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:ret(false, "error while sending request to API : " .. err)
|
return self:ret(false, "error while sending request to API : " .. err)
|
||||||
end
|
end
|
||||||
if status ~= 200 then
|
if status ~= 200 then
|
||||||
return self:ret(false, "received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id)
|
return self:ret(
|
||||||
|
false,
|
||||||
|
"received status " .. tostring(status) .. " from API using instance ID " .. self.bunkernet_id
|
||||||
|
)
|
||||||
end
|
end
|
||||||
self.logger:log(ngx.NOTICE, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
self.logger:log(ngx.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")
|
return self:ret(true, "connectivity with API using instance ID " .. self.bunkernet_id .. " is successful")
|
||||||
|
@ -82,7 +85,7 @@ function bunkernet:init()
|
||||||
local ret = true
|
local ret = true
|
||||||
local i = 0
|
local i = 0
|
||||||
local db = {
|
local db = {
|
||||||
ip = {}
|
ip = {},
|
||||||
}
|
}
|
||||||
local f, err = io.open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
local f, err = io.open("/var/cache/bunkerweb/bunkernet/ip.list", "r")
|
||||||
if not f then
|
if not f then
|
||||||
|
@ -128,6 +131,7 @@ function bunkernet:access()
|
||||||
if db then
|
if db then
|
||||||
-- Check if is IP is present
|
-- Check if is IP is present
|
||||||
if #db.ip > 0 then
|
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 = utils.is_ip_in_networks(self.ctx.bw.remote_addr, db.ip)
|
||||||
if present == nil then
|
if present == nil then
|
||||||
return self:ret(false, "can't check if ip is in db : " .. err)
|
return self:ret(false, "can't check if ip is in db : " .. err)
|
||||||
|
@ -166,8 +170,9 @@ function bunkernet:log(bypass_checks)
|
||||||
return self:ret(true, "IP is not global")
|
return self:ret(true, "IP is not global")
|
||||||
end
|
end
|
||||||
-- TODO : check if IP has been reported recently
|
-- TODO : check if IP has been reported recently
|
||||||
|
-- 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)
|
||||||
local ok, err, status, data = obj:report(ip, reason, method, url, headers)
|
local ok, err, status, _ = obj:report(ip, reason, method, url, headers)
|
||||||
if status == 429 then
|
if status == 429 then
|
||||||
obj.logger:log(ngx.WARN, "bunkernet API is rate limiting us")
|
obj.logger:log(ngx.WARN, "bunkernet API is rate limiting us")
|
||||||
elseif not ok then
|
elseif not ok then
|
||||||
|
@ -177,8 +182,16 @@ function bunkernet:log(bypass_checks)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local hdr, err = ngx.timer.at(0, report_callback, self, self.ctx.bw.remote_addr, reason, self.ctx.bw.request_method,
|
local hdr, err = ngx.timer.at(
|
||||||
self.ctx.bw.request_uri, ngx.req.get_headers())
|
0,
|
||||||
|
report_callback,
|
||||||
|
self,
|
||||||
|
self.ctx.bw.remote_addr,
|
||||||
|
reason,
|
||||||
|
self.ctx.bw.request_method,
|
||||||
|
self.ctx.bw.request_uri,
|
||||||
|
ngx.req.get_headers()
|
||||||
|
)
|
||||||
if not hdr then
|
if not hdr then
|
||||||
return self:ret(false, "can't create report timer : " .. err)
|
return self:ret(false, "can't create report timer : " .. err)
|
||||||
end
|
end
|
||||||
|
@ -218,7 +231,7 @@ function bunkernet:request(method, url, data)
|
||||||
local all_data = {
|
local all_data = {
|
||||||
id = self.bunkernet_id,
|
id = self.bunkernet_id,
|
||||||
version = self.version,
|
version = self.version,
|
||||||
integration = self.integration
|
integration = self.integration,
|
||||||
}
|
}
|
||||||
if data then
|
if data then
|
||||||
for k, v in pairs(data) do
|
for k, v in pairs(data) do
|
||||||
|
@ -230,8 +243,8 @@ function bunkernet:request(method, url, data)
|
||||||
body = cjson.encode(all_data),
|
body = cjson.encode(all_data),
|
||||||
headers = {
|
headers = {
|
||||||
["Content-Type"] = "application/json",
|
["Content-Type"] = "application/json",
|
||||||
["User-Agent"] = "BunkerWeb/" .. self.version
|
["User-Agent"] = "BunkerWeb/" .. self.version,
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
httpc:close()
|
httpc:close()
|
||||||
if not res then
|
if not res then
|
||||||
|
@ -257,7 +270,7 @@ function bunkernet:report(ip, reason, method, url, headers)
|
||||||
reason = reason,
|
reason = reason,
|
||||||
method = method,
|
method = method,
|
||||||
url = url,
|
url = url,
|
||||||
headers = headers
|
headers = headers,
|
||||||
}
|
}
|
||||||
return self:request("POST", "/report", data)
|
return self:request("POST", "/report", data)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local cors = class("cors", plugin)
|
local cors = class("cors", plugin)
|
||||||
|
|
||||||
function cors:initialize(ctx)
|
function cors:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
|
@ -17,7 +17,7 @@ function cors:initialize(ctx)
|
||||||
["CORS_MAX_AGE"] = "Access-Control-Max-Age",
|
["CORS_MAX_AGE"] = "Access-Control-Max-Age",
|
||||||
["CORS_ALLOW_CREDENTIALS"] = "Access-Control-Allow-Credentials",
|
["CORS_ALLOW_CREDENTIALS"] = "Access-Control-Allow-Credentials",
|
||||||
["CORS_ALLOW_METHODS"] = "Access-Control-Allow-Methods",
|
["CORS_ALLOW_METHODS"] = "Access-Control-Allow-Methods",
|
||||||
["CORS_ALLOW_HEADERS"] = "Access-Control-Allow-Headers"
|
["CORS_ALLOW_HEADERS"] = "Access-Control-Allow-Headers",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -43,7 +43,12 @@ function cors:header()
|
||||||
ngx.header.Vary = "Origin"
|
ngx.header.Vary = "Origin"
|
||||||
end
|
end
|
||||||
-- Check if Origin is allowed
|
-- 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"]) then
|
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"])
|
||||||
|
then
|
||||||
self.logger:log(ngx.WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
self.logger:log(ngx.WARN, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||||
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed")
|
||||||
end
|
end
|
||||||
|
@ -81,9 +86,17 @@ function cors:access()
|
||||||
return self:ret(true, "service doesn't use CORS")
|
return self:ret(true, "service doesn't use CORS")
|
||||||
end
|
end
|
||||||
-- Deny as soon as possible if needed
|
-- Deny as soon as possible if needed
|
||||||
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"]) then
|
if
|
||||||
return self:ret(true, "origin " .. self.ctx.bw.http_origin .. " is not allowed, denying access",
|
self.ctx.bw.http_origin
|
||||||
utils.get_deny_status(self.ctx))
|
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"])
|
||||||
|
then
|
||||||
|
return self:ret(
|
||||||
|
true,
|
||||||
|
"origin " .. self.ctx.bw.http_origin .. " is not allowed, denying access",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
-- Send CORS policy with a 204 (no content) status
|
-- Send CORS policy with a 204 (no content) status
|
||||||
if self.ctx.bw.request_method == "OPTIONS" and self.ctx.bw.http_origin then
|
if self.ctx.bw.request_method == "OPTIONS" and self.ctx.bw.http_origin then
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local class = require "middleclass"
|
local cjson = require "cjson"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local class = require "middleclass"
|
||||||
local utils = require "bunkerweb.utils"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local cjson = require "cjson"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local country = class("country", plugin)
|
local country = class("country", plugin)
|
||||||
|
|
||||||
|
@ -16,17 +16,28 @@ function country:access()
|
||||||
return self:ret(true, "country not activated")
|
return self:ret(true, "country not activated")
|
||||||
end
|
end
|
||||||
-- Check if IP is in cache
|
-- Check if IP is in cache
|
||||||
local ok, data = self:is_in_cache(self.ctx.bw.remote_addr)
|
local _, data = self:is_in_cache(self.ctx.bw.remote_addr)
|
||||||
if data then
|
if data then
|
||||||
data = cjson.decode(data)
|
data = cjson.decode(data)
|
||||||
if data.result == "ok" then
|
if data.result == "ok" then
|
||||||
return self:ret(true,
|
return self:ret(
|
||||||
"client IP " ..
|
true,
|
||||||
self.ctx.bw.remote_addr .. " is in country cache (not blacklisted, country = " .. data.country .. ")")
|
"client IP "
|
||||||
|
.. self.ctx.bw.remote_addr
|
||||||
|
.. " is in country cache (not blacklisted, country = "
|
||||||
|
.. data.country
|
||||||
|
.. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
return self:ret(true,
|
return self:ret(
|
||||||
"client IP " .. self.ctx.bw.remote_addr .. " is in country cache (blacklisted, country = " .. data.country .. ")",
|
true,
|
||||||
utils.get_deny_status(self.ctx))
|
"client IP "
|
||||||
|
.. self.ctx.bw.remote_addr
|
||||||
|
.. " is in country cache (blacklisted, country = "
|
||||||
|
.. data.country
|
||||||
|
.. ")",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Don't go further if IP is not global
|
-- Don't go further if IP is not global
|
||||||
|
@ -39,50 +50,64 @@ function country:access()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the country of client
|
-- Get the country of client
|
||||||
local country, err = utils.get_country(self.ctx.bw.remote_addr)
|
local country_data, err = utils.get_country(self.ctx.bw.remote_addr)
|
||||||
if not country then
|
if not country_data then
|
||||||
return self:ret(false, "can't get country of client IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
return self:ret(false, "can't get country of client IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Process whitelist first
|
-- Process whitelist first
|
||||||
if self.variables["WHITELIST_COUNTRY"] ~= "" then
|
if self.variables["WHITELIST_COUNTRY"] ~= "" then
|
||||||
for wh_country in self.variables["WHITELIST_COUNTRY"]:gmatch("%S+") do
|
for wh_country in self.variables["WHITELIST_COUNTRY"]:gmatch "%S+" do
|
||||||
if wh_country == country then
|
if wh_country == country_data then
|
||||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ok")
|
-- luacheck: ignore 421
|
||||||
|
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ok")
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:ret(false, "error while adding item to cache : " .. err)
|
return self:ret(false, "error while adding item to cache : " .. err)
|
||||||
end
|
end
|
||||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is whitelisted (country = " .. country .. ")")
|
return self:ret(
|
||||||
|
true,
|
||||||
|
"client IP " .. self.ctx.bw.remote_addr .. " is whitelisted (country = " .. country_data .. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ko")
|
-- luacheck: ignore 421
|
||||||
|
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ko")
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:ret(false, "error while adding item to cache : " .. err)
|
return self:ret(false, "error while adding item to cache : " .. err)
|
||||||
end
|
end
|
||||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country .. ")",
|
return self:ret(
|
||||||
utils.get_deny_status(self.ctx))
|
true,
|
||||||
|
"client IP " .. self.ctx.bw.remote_addr .. " is not whitelisted (country = " .. country_data .. ")",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- And then blacklist
|
-- And then blacklist
|
||||||
if self.variables["BLACKLIST_COUNTRY"] ~= "" then
|
if self.variables["BLACKLIST_COUNTRY"] ~= "" then
|
||||||
for bl_country in self.variables["BLACKLIST_COUNTRY"]:gmatch("%S+") do
|
for bl_country in self.variables["BLACKLIST_COUNTRY"]:gmatch "%S+" do
|
||||||
if bl_country == country then
|
if bl_country == country_data then
|
||||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ko")
|
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ko")
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:ret(false, "error while adding item to cache : " .. err)
|
return self:ret(false, "error while adding item to cache : " .. err)
|
||||||
end
|
end
|
||||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country .. ")",
|
return self:ret(
|
||||||
utils.get_deny_status(self.ctx))
|
true,
|
||||||
|
"client IP " .. self.ctx.bw.remote_addr .. " is blacklisted (country = " .. country_data .. ")",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Country IP is not in blacklist
|
-- Country IP is not in blacklist
|
||||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country, "ok")
|
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr, country_data, "ok")
|
||||||
if not ok then
|
if not ok then
|
||||||
return self:ret(false, "error while caching IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
return self:ret(false, "error while caching IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||||
end
|
end
|
||||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is not blacklisted (country = " .. country .. ")")
|
return self:ret(
|
||||||
|
true,
|
||||||
|
"client IP " .. self.ctx.bw.remote_addr .. " is not blacklisted (country = " .. country_data .. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function country:preread()
|
function country:preread()
|
||||||
|
@ -97,9 +122,12 @@ function country:is_in_cache(ip)
|
||||||
return true, data
|
return true, data
|
||||||
end
|
end
|
||||||
|
|
||||||
function country:add_to_cache(ip, country, result)
|
function country:add_to_cache(ip, country_data, result)
|
||||||
local ok, err = self.cachestore:set("plugin_country_" .. self.ctx.bw.server_name .. ip,
|
local ok, err = self.cachestore:set(
|
||||||
cjson.encode({ country = country, result = result }), 86400)
|
"plugin_country_" .. self.ctx.bw.server_name .. ip,
|
||||||
|
cjson.encode { country = country_data, result = result },
|
||||||
|
86400
|
||||||
|
)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,23 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
|
||||||
local resolver = require "resty.dns.resolver"
|
local resolver = require "resty.dns.resolver"
|
||||||
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local dnsbl = class("dnsbl", plugin)
|
local dnsbl = class("dnsbl", plugin)
|
||||||
|
|
||||||
|
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)
|
||||||
|
if not ips then
|
||||||
|
return nil, server, err
|
||||||
|
end
|
||||||
|
for _, ip in ipairs(ips) do
|
||||||
|
if ip:find "^127%.0%.0%." then
|
||||||
|
return true, server
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false, server
|
||||||
|
end
|
||||||
|
|
||||||
function dnsbl:initialize(ctx)
|
function dnsbl:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
|
@ -26,14 +40,15 @@ function dnsbl:init_worker()
|
||||||
local threads = {}
|
local threads = {}
|
||||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||||
-- Create thread
|
-- Create thread
|
||||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, "127.0.0.2", server)
|
local thread = ngx.thread.spawn(is_in_dnsbl, "127.0.0.2", server)
|
||||||
threads[server] = thread
|
threads[server] = thread
|
||||||
end
|
end
|
||||||
-- Wait for threads
|
-- Wait for threads
|
||||||
for dnsbl, thread in pairs(threads) do
|
for data, thread in pairs(threads) do
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, result, server, err = ngx.thread.wait(thread)
|
local ok, result, server, err = ngx.thread.wait(thread)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error while waiting thread of " .. dnsbl .. " check : " .. result)
|
self.logger:log(ngx.ERR, "error while waiting thread of " .. data .. " check : " .. result)
|
||||||
elseif result == nil then
|
elseif result == nil then
|
||||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||||
elseif not result then
|
elseif not result then
|
||||||
|
@ -65,14 +80,17 @@ function dnsbl:access()
|
||||||
if cached == "ok" then
|
if cached == "ok" then
|
||||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (not blacklisted)")
|
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (not blacklisted)")
|
||||||
end
|
end
|
||||||
return self:ret(true, "client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")",
|
return self:ret(
|
||||||
utils.get_deny_status(self.ctx))
|
true,
|
||||||
|
"client IP " .. self.ctx.bw.remote_addr .. " is in DNSBL cache (server = " .. cached .. ")",
|
||||||
|
utils.get_deny_status(self.ctx)
|
||||||
|
)
|
||||||
end
|
end
|
||||||
-- Loop on DNSBL list
|
-- Loop on DNSBL list
|
||||||
local threads = {}
|
local threads = {}
|
||||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||||
-- Create thread
|
-- Create thread
|
||||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, self.ctx.bw.remote_addr, server)
|
local thread = ngx.thread.spawn(is_in_dnsbl, self.ctx.bw.remote_addr, server)
|
||||||
threads[server] = thread
|
threads[server] = thread
|
||||||
end
|
end
|
||||||
-- Wait for threads
|
-- Wait for threads
|
||||||
|
@ -82,7 +100,7 @@ function dnsbl:access()
|
||||||
while true do
|
while true do
|
||||||
-- Compute threads to wait
|
-- Compute threads to wait
|
||||||
local wait_threads = {}
|
local wait_threads = {}
|
||||||
for dnsbl, thread in pairs(threads) do
|
for _, thread in pairs(threads) do
|
||||||
table.insert(wait_threads, thread)
|
table.insert(wait_threads, thread)
|
||||||
end
|
end
|
||||||
-- No server reported IP
|
-- No server reported IP
|
||||||
|
@ -90,6 +108,7 @@ function dnsbl:access()
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
-- Wait for first thread
|
-- Wait for first thread
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
||||||
-- Error case
|
-- Error case
|
||||||
if not ok then
|
if not ok then
|
||||||
|
@ -115,7 +134,7 @@ function dnsbl:access()
|
||||||
-- Kill other threads
|
-- Kill other threads
|
||||||
if #threads > 0 then
|
if #threads > 0 then
|
||||||
local wait_threads = {}
|
local wait_threads = {}
|
||||||
for dnsbl, thread in pairs(threads) do
|
for _, thread in pairs(threads) do
|
||||||
table.insert(wait_threads, thread)
|
table.insert(wait_threads, thread)
|
||||||
end
|
end
|
||||||
utils.kill_all_threads(wait_threads)
|
utils.kill_all_threads(wait_threads)
|
||||||
|
@ -159,18 +178,4 @@ function dnsbl:add_to_cache(ip, value)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function dnsbl:is_in_dnsbl(ip, server)
|
|
||||||
local request = resolver.arpa_str(ip):gsub("%.in%-addr%.arpa", ""):gsub("%.ip6%.arpa", "") .. "." .. server
|
|
||||||
local ips, err = utils.get_ips(request, false)
|
|
||||||
if not ips then
|
|
||||||
return nil, server, err
|
|
||||||
end
|
|
||||||
for i, ip in ipairs(ips) do
|
|
||||||
if ip:find("^127%.0%.0%.") then
|
|
||||||
return true, server
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false, server
|
|
||||||
end
|
|
||||||
|
|
||||||
return dnsbl
|
return dnsbl
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local template = nil
|
local template = nil
|
||||||
if ngx.shared.datastore then
|
if ngx.shared.datastore then
|
||||||
template = require "resty.template"
|
template = require "resty.template"
|
||||||
|
@ -14,52 +14,52 @@ function errors:initialize(ctx)
|
||||||
self.default_errors = {
|
self.default_errors = {
|
||||||
["400"] = {
|
["400"] = {
|
||||||
title = "Bad Request",
|
title = "Bad Request",
|
||||||
text = "The server did not understand the request."
|
text = "The server did not understand the request.",
|
||||||
},
|
},
|
||||||
["401"] = {
|
["401"] = {
|
||||||
title = "Not Authorized",
|
title = "Not Authorized",
|
||||||
text = "Valid authentication credentials needed for the target resource."
|
text = "Valid authentication credentials needed for the target resource.",
|
||||||
},
|
},
|
||||||
["403"] = {
|
["403"] = {
|
||||||
title = "Forbidden",
|
title = "Forbidden",
|
||||||
text = "Access is forbidden to the requested page."
|
text = "Access is forbidden to the requested page.",
|
||||||
},
|
},
|
||||||
["404"] = {
|
["404"] = {
|
||||||
title = "Not Found",
|
title = "Not Found",
|
||||||
text = "The server cannot find the requested page."
|
text = "The server cannot find the requested page.",
|
||||||
},
|
},
|
||||||
["405"] = {
|
["405"] = {
|
||||||
title = "Method Not Allowed",
|
title = "Method Not Allowed",
|
||||||
text = "The method specified in the request is not allowed."
|
text = "The method specified in the request is not allowed.",
|
||||||
},
|
},
|
||||||
["413"] = {
|
["413"] = {
|
||||||
title = "Request Entity Too Large",
|
title = "Request Entity Too Large",
|
||||||
text = "The server will not accept the request, because the request entity is too large."
|
text = "The server will not accept the request, because the request entity is too large.",
|
||||||
},
|
},
|
||||||
["429"] = {
|
["429"] = {
|
||||||
title = "Too Many Requests",
|
title = "Too Many Requests",
|
||||||
text = "Too many requests sent in a given amount of time, try again later."
|
text = "Too many requests sent in a given amount of time, try again later.",
|
||||||
},
|
},
|
||||||
["500"] = {
|
["500"] = {
|
||||||
title = "Internal Server Error",
|
title = "Internal Server Error",
|
||||||
text = "The request was not completed. The server met an unexpected condition."
|
text = "The request was not completed. The server met an unexpected condition.",
|
||||||
},
|
},
|
||||||
["501"] = {
|
["501"] = {
|
||||||
title = "Not Implemented",
|
title = "Not Implemented",
|
||||||
text = "The request was not completed. The server did not support the functionality required."
|
text = "The request was not completed. The server did not support the functionality required.",
|
||||||
},
|
},
|
||||||
["502"] = {
|
["502"] = {
|
||||||
title = "Bad Gateway",
|
title = "Bad Gateway",
|
||||||
text = "The request was not completed. The server received an invalid response from the upstream server."
|
text = "The request was not completed. The server received an invalid response from the upstream server.",
|
||||||
},
|
},
|
||||||
["503"] = {
|
["503"] = {
|
||||||
title = "Service Unavailable",
|
title = "Service Unavailable",
|
||||||
text = "The request was not completed. The server is temporarily overloading or down."
|
text = "The request was not completed. The server is temporarily overloading or down.",
|
||||||
},
|
},
|
||||||
["504"] = {
|
["504"] = {
|
||||||
title = "Gateway Timeout",
|
title = "Gateway Timeout",
|
||||||
text = "The gateway has timed out."
|
text = "The gateway has timed out.",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ function errors:render_template(code)
|
||||||
title = code .. " - " .. self.default_errors[code].title,
|
title = code .. " - " .. self.default_errors[code].title,
|
||||||
error_title = self.default_errors[code].title,
|
error_title = self.default_errors[code].title,
|
||||||
error_code = code,
|
error_code = code,
|
||||||
error_text = self.default_errors[code].text
|
error_text = self.default_errors[code].text,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
|
||||||
local utils = require "bunkerweb.utils"
|
|
||||||
local ipmatcher = require "resty.ipmatcher"
|
local ipmatcher = require "resty.ipmatcher"
|
||||||
|
local plugin = require "bunkerweb.plugin"
|
||||||
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local greylist = class("greylist", plugin)
|
local greylist = class("greylist", plugin)
|
||||||
|
|
||||||
function greylist:initialize(ctx)
|
function greylist:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
|
@ -22,7 +22,7 @@ function greylist:initialize(ctx)
|
||||||
["RDNS"] = {},
|
["RDNS"] = {},
|
||||||
["ASN"] = {},
|
["ASN"] = {},
|
||||||
["USER_AGENT"] = {},
|
["USER_AGENT"] = {},
|
||||||
["URI"] = {}
|
["URI"] = {},
|
||||||
}
|
}
|
||||||
for kind, _ in pairs(kinds) do
|
for kind, _ in pairs(kinds) do
|
||||||
for data in self.variables["GREYLIST_" .. kind]:gmatch("%S+") do
|
for data in self.variables["GREYLIST_" .. kind]:gmatch("%S+") do
|
||||||
|
@ -67,7 +67,7 @@ function greylist:init()
|
||||||
}
|
}
|
||||||
local i = 0
|
local i = 0
|
||||||
for kind, _ in pairs(greylists) do
|
for kind, _ in pairs(greylists) do
|
||||||
local f, err = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
local f, _ = io.open("/var/cache/bunkerweb/greylist/" .. kind .. ".list", "r")
|
||||||
if f then
|
if f then
|
||||||
for line in f:lines() do
|
for line in f:lines() do
|
||||||
table.insert(greylists[kind], line)
|
table.insert(greylists[kind], line)
|
||||||
|
@ -91,7 +91,7 @@ function greylist:access()
|
||||||
end
|
end
|
||||||
-- Check the caches
|
-- Check the caches
|
||||||
local checks = {
|
local checks = {
|
||||||
["IP"] = "ip" .. self.ctx.bw.remote_addr
|
["IP"] = "ip" .. self.ctx.bw.remote_addr,
|
||||||
}
|
}
|
||||||
if self.ctx.bw.http_user_agent then
|
if self.ctx.bw.http_user_agent then
|
||||||
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
||||||
|
@ -102,7 +102,7 @@ function greylist:access()
|
||||||
local already_cached = {
|
local already_cached = {
|
||||||
["IP"] = false,
|
["IP"] = false,
|
||||||
["URI"] = false,
|
["URI"] = false,
|
||||||
["UA"] = false
|
["UA"] = false,
|
||||||
}
|
}
|
||||||
for k, v in pairs(checks) do
|
for k, v in pairs(checks) do
|
||||||
local ok, cached = self:is_in_cache(v)
|
local ok, cached = self:is_in_cache(v)
|
||||||
|
@ -120,12 +120,13 @@ function greylist:access()
|
||||||
return self:ret(false, "lists is nil")
|
return self:ret(false, "lists is nil")
|
||||||
end
|
end
|
||||||
-- Perform checks
|
-- Perform checks
|
||||||
for k, v in pairs(checks) do
|
for k, _ in pairs(checks) do
|
||||||
if not already_cached[k] then
|
if not already_cached[k] then
|
||||||
local ok, greylisted = self:is_greylisted(k)
|
local ok, greylisted = self:is_greylisted(k)
|
||||||
if ok == nil then
|
if ok == nil then
|
||||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is greylisted : " .. greylisted)
|
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is greylisted : " .. greylisted)
|
||||||
else
|
else
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), greylisted)
|
local ok, err = self:add_to_cache(self:kind_to_ele(k), greylisted)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||||
|
@ -187,12 +188,13 @@ function greylist:is_greylisted_ip()
|
||||||
end
|
end
|
||||||
if check_rdns then
|
if check_rdns then
|
||||||
-- Get rDNS
|
-- Get rDNS
|
||||||
|
-- luacheck: ignore 421
|
||||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||||
-- Check if rDNS is in greylist
|
-- Check if rDNS is in greylist
|
||||||
if rdns_list then
|
if rdns_list then
|
||||||
for i, rdns in ipairs(rdns_list) do
|
for _, rdns in ipairs(rdns_list) do
|
||||||
for j, suffix in ipairs(self.lists["RDNS"]) do
|
for _, suffix in ipairs(self.lists["RDNS"]) do
|
||||||
if rdns:sub(- #suffix) == suffix then
|
if rdns:sub(-#suffix) == suffix then
|
||||||
return true, "rDNS " .. suffix
|
return true, "rDNS " .. suffix
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -208,7 +210,7 @@ function greylist:is_greylisted_ip()
|
||||||
if not asn then
|
if not asn then
|
||||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||||
else
|
else
|
||||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||||
if bl_asn == tostring(asn) then
|
if bl_asn == tostring(asn) then
|
||||||
return true, "ASN " .. bl_asn
|
return true, "ASN " .. bl_asn
|
||||||
end
|
end
|
||||||
|
@ -222,7 +224,7 @@ end
|
||||||
|
|
||||||
function greylist:is_greylisted_uri()
|
function greylist:is_greylisted_uri()
|
||||||
-- Check if URI is in greylist
|
-- Check if URI is in greylist
|
||||||
for i, uri in ipairs(self.lists["URI"]) do
|
for _, uri in ipairs(self.lists["URI"]) do
|
||||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||||
return true, "URI " .. uri
|
return true, "URI " .. uri
|
||||||
end
|
end
|
||||||
|
@ -233,7 +235,7 @@ end
|
||||||
|
|
||||||
function greylist:is_greylisted_ua()
|
function greylist:is_greylisted_ua()
|
||||||
-- Check if UA is in greylist
|
-- Check if UA is in greylist
|
||||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||||
return true, "UA " .. ua
|
return true, "UA " .. ua
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,99 +1,110 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local headers = class("headers", plugin)
|
local headers = class("headers", plugin)
|
||||||
|
|
||||||
function headers:initialize(ctx)
|
function headers:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
plugin.initialize(self, "headers", ctx)
|
plugin.initialize(self, "headers", ctx)
|
||||||
self.all_headers = {
|
self.all_headers = {
|
||||||
["STRICT_TRANSPORT_SECURITY"] = "Strict-Transport-Security",
|
["STRICT_TRANSPORT_SECURITY"] = "Strict-Transport-Security",
|
||||||
["CONTENT_SECURITY_POLICY"] = "Content-Security-Policy",
|
["CONTENT_SECURITY_POLICY"] = "Content-Security-Policy",
|
||||||
["REFERRER_POLICY"] = "Referrer-Policy",
|
["REFERRER_POLICY"] = "Referrer-Policy",
|
||||||
["PERMISSIONS_POLICY"] = "Permissions-Policy",
|
["PERMISSIONS_POLICY"] = "Permissions-Policy",
|
||||||
["FEATURE_POLICY"] = "Feature-Policy",
|
["FEATURE_POLICY"] = "Feature-Policy",
|
||||||
["X_FRAME_OPTIONS"] = "X-Frame-Options",
|
["X_FRAME_OPTIONS"] = "X-Frame-Options",
|
||||||
["X_CONTENT_TYPE_OPTIONS"] = "X-Content-Type-Options",
|
["X_CONTENT_TYPE_OPTIONS"] = "X-Content-Type-Options",
|
||||||
["X_XSS_PROTECTION"] = "X-XSS-Protection"
|
["X_XSS_PROTECTION"] = "X-XSS-Protection",
|
||||||
}
|
}
|
||||||
-- Load data from datastore if needed
|
-- Load data from datastore if needed
|
||||||
if ngx.get_phase() ~= "init" then
|
if ngx.get_phase() ~= "init" then
|
||||||
-- Get custom headers from datastore
|
-- Get custom headers from datastore
|
||||||
local custom_headers, err = self.datastore:get("plugin_headers_custom_headers", true)
|
local custom_headers, err = self.datastore:get("plugin_headers_custom_headers", true)
|
||||||
if not custom_headers then
|
if not custom_headers then
|
||||||
self.logger:log(ngx.ERR, err)
|
self.logger:log(ngx.ERR, err)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
self.custom_headers = {}
|
self.custom_headers = {}
|
||||||
-- Extract global headers
|
-- Extract global headers
|
||||||
if custom_headers.global then
|
if custom_headers.global then
|
||||||
for k, v in pairs(custom_headers.global) do
|
for k, v in pairs(custom_headers.global) do
|
||||||
self.custom_headers[k] = v
|
self.custom_headers[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Extract and overwrite if needed server headers
|
-- Extract and overwrite if needed server headers
|
||||||
if custom_headers[self.ctx.bw.server_name] then
|
if custom_headers[self.ctx.bw.server_name] then
|
||||||
for k, v in pairs(custom_headers[self.ctx.bw.server_name]) do
|
for k, v in pairs(custom_headers[self.ctx.bw.server_name]) do
|
||||||
self.custom_headers[k] = v
|
self.custom_headers[k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function headers:init()
|
function headers:init()
|
||||||
-- Get variables
|
-- Get variables
|
||||||
local variables, err = utils.get_multiple_variables({ "CUSTOM_HEADER" })
|
local variables, err = utils.get_multiple_variables({ "CUSTOM_HEADER" })
|
||||||
if variables == nil then
|
if variables == nil then
|
||||||
return self:ret(false, err)
|
return self:ret(false, err)
|
||||||
end
|
end
|
||||||
-- Store custom headers name and value
|
-- Store custom headers name and value
|
||||||
local data = {}
|
local data = {}
|
||||||
local i = 0
|
local i = 0
|
||||||
for srv, vars in pairs(variables) do
|
for srv, vars in pairs(variables) do
|
||||||
for var, value in pairs(vars) do
|
for _, value in pairs(vars) do
|
||||||
if data[srv] == nil then
|
if data[srv] == nil then
|
||||||
data[srv] = {}
|
data[srv] = {}
|
||||||
end
|
end
|
||||||
local m = utils.regex_match(value, "([\\w-]+): ([^,]+)")
|
local m = utils.regex_match(value, "([\\w-]+): ([^,]+)")
|
||||||
if m then
|
if m then
|
||||||
data[srv][m[1]] = m[2]
|
data[srv][m[1]] = m[2]
|
||||||
end
|
end
|
||||||
i = i + 1
|
i = i + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local ok, err = self.datastore:set("plugin_headers_custom_headers", data, nil, true)
|
local ok
|
||||||
if not ok then
|
ok, err = self.datastore:set("plugin_headers_custom_headers", data, nil, true)
|
||||||
return self:ret(false, err)
|
if not ok then
|
||||||
end
|
return self:ret(false, err)
|
||||||
return self:ret(true, "successfully loaded " .. tostring(i) .. " custom headers")
|
end
|
||||||
|
return self:ret(true, "successfully loaded " .. tostring(i) .. " custom headers")
|
||||||
end
|
end
|
||||||
|
|
||||||
function headers:header()
|
function headers:header()
|
||||||
-- Override upstream headers if needed
|
-- Override upstream headers if needed
|
||||||
local ssl = self.ctx.bw.scheme == "https"
|
local ssl = self.ctx.bw.scheme == "https"
|
||||||
for variable, header in pairs(self.all_headers) do
|
for variable, header in pairs(self.all_headers) do
|
||||||
if 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) then
|
if
|
||||||
if (header ~= "Strict-Transport-Security" or ssl) then
|
ngx.header[header] == nil
|
||||||
if header == "Content-Security-Policy" and self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes" then
|
or (
|
||||||
ngx.header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
self.variables[variable] ~= ""
|
||||||
else
|
and self.variables["KEEP_UPSTREAM_HEADERS"] ~= "*"
|
||||||
ngx.header[header] = self.variables[variable]
|
and utils.regex_match(self.variables["KEEP_UPSTREAM_HEADERS"], "(^| )" .. header .. "($| )") == nil
|
||||||
end
|
)
|
||||||
end
|
then
|
||||||
end
|
if header ~= "Strict-Transport-Security" or ssl then
|
||||||
end
|
if
|
||||||
-- Add custom headers
|
header == "Content-Security-Policy"
|
||||||
for header, value in pairs(self.custom_headers) do
|
and self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes"
|
||||||
ngx.header[header] = value
|
then
|
||||||
end
|
ngx.header["Content-Security-Policy-Report-Only"] = self.variables[variable]
|
||||||
-- Remove headers
|
else
|
||||||
if self.variables["REMOVE_HEADERS"] ~= "" then
|
ngx.header[header] = self.variables[variable]
|
||||||
for header in self.variables["REMOVE_HEADERS"]:gmatch("%S+") do
|
end
|
||||||
ngx.header[header] = nil
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self:ret(true, "edited headers for request")
|
-- Add custom headers
|
||||||
|
for header, value in pairs(self.custom_headers) do
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self:ret(true, "edited headers for request")
|
||||||
end
|
end
|
||||||
|
|
||||||
return headers
|
return headers
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
local class = require "middleclass"
|
local cjson = require "cjson"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local class = require "middleclass"
|
||||||
local cjson = require "cjson"
|
local plugin = require "bunkerweb.plugin"
|
||||||
|
|
||||||
local letsencrypt = class("letsencrypt", plugin)
|
local letsencrypt = class("letsencrypt", plugin)
|
||||||
|
|
||||||
|
@ -17,9 +17,12 @@ function letsencrypt:access()
|
||||||
return self:ret(true, "success")
|
return self:ret(true, "success")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- luacheck: ignore 212
|
||||||
function letsencrypt:api(ctx)
|
function letsencrypt:api(ctx)
|
||||||
if not string.match(ctx.bw.uri, "^/lets%-encrypt/challenge$") or
|
if
|
||||||
(ctx.bw.request_method ~= "POST" and ctx.bw.request_method ~= "DELETE") then
|
not string.match(ctx.bw.uri, "^/lets%-encrypt/challenge$")
|
||||||
|
or (ctx.bw.request_method ~= "POST" and ctx.bw.request_method ~= "DELETE")
|
||||||
|
then
|
||||||
return false, nil, nil
|
return false, nil, nil
|
||||||
end
|
end
|
||||||
local acme_folder = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
|
local acme_folder = "/var/tmp/bunkerweb/lets-encrypt/.well-known/acme-challenge/"
|
||||||
|
@ -32,7 +35,9 @@ function letsencrypt:api(ctx)
|
||||||
if ctx.bw.request_method == "POST" then
|
if ctx.bw.request_method == "POST" then
|
||||||
local file, err = io.open(acme_folder .. data.token, "w+")
|
local file, err = io.open(acme_folder .. data.token, "w+")
|
||||||
if not file then
|
if not file then
|
||||||
return true, ngx.HTTP_INTERNAL_SERVER_ERROR, { status = "error", msg = "can't write validation token : " .. err }
|
return true,
|
||||||
|
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
{ status = "error", msg = "can't write validation token : " .. err }
|
||||||
end
|
end
|
||||||
file:write(data.validation)
|
file:write(data.validation)
|
||||||
file:close()
|
file:close()
|
||||||
|
@ -40,7 +45,9 @@ function letsencrypt:api(ctx)
|
||||||
elseif ctx.bw.request_method == "DELETE" then
|
elseif ctx.bw.request_method == "DELETE" then
|
||||||
local ok, err = os.remove(acme_folder .. data.token)
|
local ok, err = os.remove(acme_folder .. data.token)
|
||||||
if not ok then
|
if not ok then
|
||||||
return true, ngx.HTTP_INTERNAL_SERVER_ERROR, { status = "error", msg = "can't remove validation token : " .. err }
|
return true,
|
||||||
|
ngx.HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
{ status = "error", msg = "can't remove validation token : " .. err }
|
||||||
end
|
end
|
||||||
return true, ngx.HTTP_OK, { status = "success", msg = "validation token removed" }
|
return true, ngx.HTTP_OK, { status = "success", msg = "validation token removed" }
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,40 @@
|
||||||
local class = require "middleclass"
|
local cjson = require "cjson"
|
||||||
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local utils = require "bunkerweb.utils"
|
||||||
local cjson = require "cjson"
|
|
||||||
|
|
||||||
local limit = class("limit", plugin)
|
local limit = class("limit", plugin)
|
||||||
|
|
||||||
|
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 delay = 0
|
||||||
|
if rate_time == "s" then
|
||||||
|
delay = 1
|
||||||
|
elseif rate_time == "m" then
|
||||||
|
delay = 60
|
||||||
|
elseif rate_time == "h" then
|
||||||
|
delay = 3600
|
||||||
|
elseif rate_time == "d" then
|
||||||
|
delay = 86400
|
||||||
|
end
|
||||||
|
-- Keep only timestamp within the delay
|
||||||
|
for _, timestamp in ipairs(timestamps) do
|
||||||
|
if current_timestamp - timestamp <= delay then
|
||||||
|
table.insert(new_timestamps, timestamp)
|
||||||
|
else
|
||||||
|
updated = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Only insert the new timestamp if client is not limited already to avoid infinite insert
|
||||||
|
if #new_timestamps <= rate_max then
|
||||||
|
table.insert(new_timestamps, current_timestamp)
|
||||||
|
updated = true
|
||||||
|
end
|
||||||
|
return updated, new_timestamps, delay
|
||||||
|
end
|
||||||
|
|
||||||
function limit:initialize(ctx)
|
function limit:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
|
@ -11,7 +42,6 @@ function limit:initialize(ctx)
|
||||||
-- Load rules if needed
|
-- Load rules if needed
|
||||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||||
-- Get all rules from datastore
|
-- Get all rules from datastore
|
||||||
local limited = false
|
|
||||||
local all_rules, err = self.datastore:get("plugin_limit_rules", true)
|
local all_rules, err = self.datastore:get("plugin_limit_rules", true)
|
||||||
if not all_rules then
|
if not all_rules then
|
||||||
self.logger:log(ngx.ERR, err)
|
self.logger:log(ngx.ERR, err)
|
||||||
|
@ -93,19 +123,16 @@ function limit:access()
|
||||||
return self:ret(true, "limit request not enabled")
|
return self:ret(true, "limit request not enabled")
|
||||||
end
|
end
|
||||||
-- Check if URI is limited
|
-- Check if URI is limited
|
||||||
local rate = nil
|
local rate
|
||||||
local uri = nil
|
|
||||||
for k, v in pairs(self.rules) do
|
for k, v in pairs(self.rules) do
|
||||||
if k ~= "/" and utils.regex_match(self.ctx.bw.uri, k) then
|
if k ~= "/" and utils.regex_match(self.ctx.bw.uri, k) then
|
||||||
rate = v
|
rate = v
|
||||||
uri = k
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if not rate then
|
if not rate then
|
||||||
if self.rules["/"] then
|
if self.rules["/"] then
|
||||||
rate = self.rules["/"]
|
rate = self.rules["/"]
|
||||||
uri = "/"
|
|
||||||
else
|
else
|
||||||
return self:ret(true, "no rule for " .. self.ctx.bw.uri)
|
return self:ret(true, "no rule for " .. self.ctx.bw.uri)
|
||||||
end
|
end
|
||||||
|
@ -118,19 +145,37 @@ function limit:access()
|
||||||
end
|
end
|
||||||
-- Limit reached
|
-- Limit reached
|
||||||
if limited then
|
if limited then
|
||||||
return self:ret(true,
|
return self:ret(
|
||||||
"client IP " ..
|
true,
|
||||||
self.ctx.bw.remote_addr ..
|
"client IP "
|
||||||
" is limited for URL " ..
|
.. self.ctx.bw.remote_addr
|
||||||
self.ctx.bw.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")",
|
.. " is limited for URL "
|
||||||
ngx.HTTP_TOO_MANY_REQUESTS)
|
.. self.ctx.bw.uri
|
||||||
|
.. " (current rate = "
|
||||||
|
.. current_rate
|
||||||
|
.. "r/"
|
||||||
|
.. rate_time
|
||||||
|
.. " and max rate = "
|
||||||
|
.. rate
|
||||||
|
.. ")",
|
||||||
|
ngx.HTTP_TOO_MANY_REQUESTS
|
||||||
|
)
|
||||||
end
|
end
|
||||||
-- Limit not reached
|
-- Limit not reached
|
||||||
return self:ret(true,
|
return self:ret(
|
||||||
"client IP " ..
|
true,
|
||||||
self.ctx.bw.remote_addr ..
|
"client IP "
|
||||||
" is not limited for URL " ..
|
.. self.ctx.bw.remote_addr
|
||||||
self.ctx.bw.uri .. " (current rate = " .. current_rate .. "r/" .. rate_time .. " and max rate = " .. rate .. ")")
|
.. " is not limited for URL "
|
||||||
|
.. self.ctx.bw.uri
|
||||||
|
.. " (current rate = "
|
||||||
|
.. current_rate
|
||||||
|
.. "r/"
|
||||||
|
.. rate_time
|
||||||
|
.. " and max rate = "
|
||||||
|
.. rate
|
||||||
|
.. ")"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
function limit:limit_req(rate_max, rate_time)
|
function limit:limit_req(rate_max, rate_time)
|
||||||
|
@ -143,9 +188,12 @@ function limit:limit_req(rate_max, rate_time)
|
||||||
else
|
else
|
||||||
timestamps = redis_timestamps
|
timestamps = redis_timestamps
|
||||||
-- Save the new timestamps
|
-- Save the new timestamps
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, err = self.datastore:set(
|
local ok, err = self.datastore:set(
|
||||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||||
cjson.encode(timestamps), delay)
|
cjson.encode(timestamps),
|
||||||
|
delay
|
||||||
|
)
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, "can't update timestamps : " .. err
|
return nil, "can't update timestamps : " .. err
|
||||||
end
|
end
|
||||||
|
@ -167,8 +215,8 @@ end
|
||||||
|
|
||||||
function limit:limit_req_local(rate_max, rate_time)
|
function limit:limit_req_local(rate_max, rate_time)
|
||||||
-- Get timestamps
|
-- Get timestamps
|
||||||
local timestamps, err = self.datastore:get("plugin_limit_" ..
|
local timestamps, err =
|
||||||
self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri)
|
self.datastore:get("plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri)
|
||||||
if not timestamps and err ~= "not found" then
|
if not timestamps and err ~= "not found" then
|
||||||
return nil, err
|
return nil, err
|
||||||
elseif err == "not found" then
|
elseif err == "not found" then
|
||||||
|
@ -176,12 +224,15 @@ function limit:limit_req_local(rate_max, rate_time)
|
||||||
end
|
end
|
||||||
timestamps = cjson.decode(timestamps)
|
timestamps = cjson.decode(timestamps)
|
||||||
-- Compute new timestamps
|
-- Compute new timestamps
|
||||||
local updated, new_timestamps, delay = self:limit_req_timestamps(rate_max, rate_time, timestamps)
|
local updated, new_timestamps, delay = limit_req_timestamps(rate_max, rate_time, timestamps)
|
||||||
-- Save new timestamps if needed
|
-- Save new timestamps if needed
|
||||||
if updated then
|
if updated then
|
||||||
|
-- luacheck: ignore 421
|
||||||
local ok, err = self.datastore:set(
|
local ok, err = self.datastore:set(
|
||||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri,
|
||||||
cjson.encode(new_timestamps), delay)
|
cjson.encode(new_timestamps),
|
||||||
|
delay
|
||||||
|
)
|
||||||
if not ok then
|
if not ok then
|
||||||
return nil, err
|
return nil, err
|
||||||
end
|
end
|
||||||
|
@ -245,9 +296,15 @@ function limit:limit_req_redis(rate_max, rate_time)
|
||||||
return nil, err
|
return nil, err
|
||||||
end
|
end
|
||||||
-- Execute script
|
-- Execute script
|
||||||
local timestamps, err = self.clusterstore:call("eval", redis_script, 1,
|
local timestamps, err = self.clusterstore:call(
|
||||||
"plugin_limit_" .. self.ctx.bw.server_name .. self.ctx.bw.remote_addr .. self.ctx.bw.uri, rate_max, rate_time,
|
"eval",
|
||||||
os.time(os.date("!*t")))
|
redis_script,
|
||||||
|
1,
|
||||||
|
"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"))
|
||||||
|
)
|
||||||
if not timestamps then
|
if not timestamps then
|
||||||
self.clusterstore:close()
|
self.clusterstore:close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -257,35 +314,4 @@ function limit:limit_req_redis(rate_max, rate_time)
|
||||||
return timestamps, "success"
|
return timestamps, "success"
|
||||||
end
|
end
|
||||||
|
|
||||||
function limit:limit_req_timestamps(rate_max, rate_time, timestamps)
|
|
||||||
-- Compute new timestamps
|
|
||||||
local updated = false
|
|
||||||
local new_timestamps = {}
|
|
||||||
local current_timestamp = os.time(os.date("!*t"))
|
|
||||||
local delay = 0
|
|
||||||
if rate_time == "s" then
|
|
||||||
delay = 1
|
|
||||||
elseif rate_time == "m" then
|
|
||||||
delay = 60
|
|
||||||
elseif rate_time == "h" then
|
|
||||||
delay = 3600
|
|
||||||
elseif rate_time == "d" then
|
|
||||||
delay = 86400
|
|
||||||
end
|
|
||||||
-- Keep only timestamp within the delay
|
|
||||||
for i, timestamp in ipairs(timestamps) do
|
|
||||||
if current_timestamp - timestamp <= delay then
|
|
||||||
table.insert(new_timestamps, timestamp)
|
|
||||||
else
|
|
||||||
updated = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- Only insert the new timestamp if client is not limited already to avoid infinite insert
|
|
||||||
if #new_timestamps <= rate_max then
|
|
||||||
table.insert(new_timestamps, current_timestamp)
|
|
||||||
updated = true
|
|
||||||
end
|
|
||||||
return updated, new_timestamps, delay
|
|
||||||
end
|
|
||||||
|
|
||||||
return limit
|
return limit
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local misc = class("misc", plugin)
|
local misc = class("misc", plugin)
|
||||||
|
|
||||||
function misc:initialize(ctx)
|
function misc:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
plugin.initialize(self, "misc", ctx)
|
plugin.initialize(self, "misc", ctx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function misc:access()
|
function misc:access()
|
||||||
-- Check if method is valid
|
-- Check if method is valid
|
||||||
local method = self.ctx.bw.request_method
|
local method = self.ctx.bw.request_method
|
||||||
if not method or not utils.regex_match(method, "^[A-Z]+$") then
|
if not method or not utils.regex_match(method, "^[A-Z]+$") then
|
||||||
return self:ret(true, "method is not valid", ngx.HTTP_BAD_REQUEST)
|
return self:ret(true, "method is not valid", ngx.HTTP_BAD_REQUEST)
|
||||||
end
|
end
|
||||||
-- Check if method is allowed
|
-- Check if method is allowed
|
||||||
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
||||||
if method == allowed_method then
|
if method == allowed_method then
|
||||||
return self:ret(true, "method " .. method .. " is allowed")
|
return self:ret(true, "method " .. method .. " is allowed")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self:ret(true, "method " .. method .. " is not allowed", ngx.HTTP_NOT_ALLOWED)
|
return self:ret(true, "method " .. method .. " is not allowed", ngx.HTTP_NOT_ALLOWED)
|
||||||
end
|
end
|
||||||
|
|
||||||
return misc
|
return misc
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
|
|
||||||
local redis = class("redis", plugin)
|
local redis = class("redis", plugin)
|
||||||
|
|
||||||
function redis:initialize()
|
function redis:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
plugin.initialize(self, "redis", ctx)
|
plugin.initialize(self, "redis", ctx)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,153 +1,155 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local utils = require "bunkerweb.utils"
|
||||||
local cachestore = require "bunkerweb.cachestore"
|
|
||||||
local cjson = require "cjson"
|
|
||||||
|
|
||||||
local reversescan = class("reversescan", plugin)
|
local reversescan = class("reversescan", plugin)
|
||||||
|
|
||||||
function reversescan:initialize(ctx)
|
function reversescan:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
plugin.initialize(self, "reversescan", ctx)
|
plugin.initialize(self, "reversescan", ctx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function reversescan:access()
|
function reversescan:access()
|
||||||
-- Check if access is needed
|
-- Check if access is needed
|
||||||
if self.variables["USE_REVERSE_SCAN"] ~= "yes" then
|
if self.variables["USE_REVERSE_SCAN"] ~= "yes" then
|
||||||
return self:ret(true, "reverse scan not activated")
|
return self:ret(true, "reverse scan not activated")
|
||||||
end
|
end
|
||||||
-- Loop on ports
|
-- Loop on ports
|
||||||
local threads = {}
|
local threads = {}
|
||||||
local ret_threads = nil
|
local ret_threads = nil
|
||||||
local ret_err = nil
|
local ret_err = nil
|
||||||
for port in self.variables["REVERSE_SCAN_PORTS"]:gmatch("%S+") do
|
for port in self.variables["REVERSE_SCAN_PORTS"]:gmatch("%S+") do
|
||||||
-- Check if the scan is already cached
|
-- Check if the scan is already cached
|
||||||
local ok, cached = self:is_in_cache(self.ctx.bw.remote_addr .. ":" .. port)
|
local ok, cached = self:is_in_cache(self.ctx.bw.remote_addr .. ":" .. port)
|
||||||
if not ok then
|
if not ok then
|
||||||
ret_threads = false
|
ret_threads = false
|
||||||
ret_err = "error getting info from cachestore : " .. cached
|
ret_err = "error getting info from cachestore : " .. cached
|
||||||
break
|
break
|
||||||
-- Deny access if port opened
|
-- Deny access if port opened
|
||||||
elseif cached == "open" then
|
elseif cached == "open" then
|
||||||
ret_threads = true
|
ret_threads = true
|
||||||
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
||||||
break
|
break
|
||||||
-- Perform scan in a thread
|
-- Perform scan in a thread
|
||||||
elseif not cached then
|
elseif not cached then
|
||||||
local thread = ngx.thread.spawn(self.scan, self.ctx.bw.remote_addr, tonumber(port),
|
local thread = ngx.thread.spawn(
|
||||||
tonumber(self.variables["REVERSE_SCAN_TIMEOUT"]))
|
self.scan,
|
||||||
threads[port] = thread
|
self.ctx.bw.remote_addr,
|
||||||
end
|
tonumber(port),
|
||||||
end
|
tonumber(self.variables["REVERSE_SCAN_TIMEOUT"])
|
||||||
if ret_threads ~= nil then
|
)
|
||||||
if #threads > 0 then
|
threads[port] = thread
|
||||||
local wait_threads = {}
|
end
|
||||||
for port, thread in pairs(threads) do
|
end
|
||||||
table.insert(wait_threads, thread)
|
if ret_threads ~= nil then
|
||||||
end
|
if #threads > 0 then
|
||||||
utils.kill_all_threads(wait_threads)
|
local wait_threads = {}
|
||||||
end
|
for _, thread in pairs(threads) do
|
||||||
-- Open port case
|
table.insert(wait_threads, thread)
|
||||||
if ret_threads then
|
end
|
||||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
utils.kill_all_threads(wait_threads)
|
||||||
end
|
end
|
||||||
-- Error case
|
-- Open port case
|
||||||
return self:ret(false, ret_err)
|
if ret_threads then
|
||||||
end
|
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||||
-- Check results of threads
|
end
|
||||||
ret_threads = nil
|
-- Error case
|
||||||
ret_err = nil
|
return self:ret(false, ret_err)
|
||||||
local results = {}
|
end
|
||||||
while true do
|
-- Check results of threads
|
||||||
-- Compute threads to wait
|
ret_threads = nil
|
||||||
local wait_threads = {}
|
ret_err = nil
|
||||||
for port, thread in pairs(threads) do
|
local results = {}
|
||||||
table.insert(wait_threads, thread)
|
while true do
|
||||||
end
|
-- Compute threads to wait
|
||||||
-- No port opened
|
local wait_threads = {}
|
||||||
if #wait_threads == 0 then
|
for _, thread in pairs(threads) do
|
||||||
break
|
table.insert(wait_threads, thread)
|
||||||
end
|
end
|
||||||
-- Wait for first thread
|
-- No port opened
|
||||||
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
if #wait_threads == 0 then
|
||||||
-- Error case
|
break
|
||||||
if not ok then
|
end
|
||||||
ret_threads = false
|
-- Wait for first thread
|
||||||
ret_err = "error while waiting thread : " .. open
|
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
||||||
break
|
-- Error case
|
||||||
end
|
if not ok then
|
||||||
port = tostring(port)
|
ret_threads = false
|
||||||
-- Remove thread from list
|
ret_err = "error while waiting thread : " .. open
|
||||||
threads[port] = nil
|
break
|
||||||
-- Add result to cache
|
end
|
||||||
local result = "close"
|
port = tostring(port)
|
||||||
if open then
|
-- Remove thread from list
|
||||||
result = "open"
|
threads[port] = nil
|
||||||
end
|
-- Add result to cache
|
||||||
results[port] = result
|
local result = "close"
|
||||||
-- Port is opened
|
if open then
|
||||||
if open then
|
result = "open"
|
||||||
ret_threads = true
|
end
|
||||||
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
results[port] = result
|
||||||
break
|
-- Port is opened
|
||||||
end
|
if open then
|
||||||
end
|
ret_threads = true
|
||||||
-- Kill running threads
|
ret_err = "port " .. port .. " is opened for IP " .. self.ctx.bw.remote_addr
|
||||||
if #threads > 0 then
|
break
|
||||||
local wait_threads = {}
|
end
|
||||||
for port, thread in pairs(threads) do
|
end
|
||||||
table.insert(wait_threads, thread)
|
-- Kill running threads
|
||||||
end
|
if #threads > 0 then
|
||||||
utils.kill_all_threads(wait_threads)
|
local wait_threads = {}
|
||||||
end
|
for _, thread in pairs(threads) do
|
||||||
-- Cache results
|
table.insert(wait_threads, thread)
|
||||||
for port, result in pairs(results) do
|
end
|
||||||
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr .. ":" .. port, result)
|
utils.kill_all_threads(wait_threads)
|
||||||
if not ok then
|
end
|
||||||
return self:ret(false, "error while adding element to cache : " .. err)
|
-- Cache results
|
||||||
end
|
for port, result in pairs(results) do
|
||||||
end
|
local ok, err = self:add_to_cache(self.ctx.bw.remote_addr .. ":" .. port, result)
|
||||||
if ret_threads ~= nil then
|
if not ok then
|
||||||
-- Open port case
|
return self:ret(false, "error while adding element to cache : " .. err)
|
||||||
if ret_threads then
|
end
|
||||||
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
end
|
||||||
end
|
if ret_threads ~= nil then
|
||||||
-- Error case
|
-- Open port case
|
||||||
return self:ret(false, ret_err)
|
if ret_threads then
|
||||||
end
|
return self:ret(true, ret_err, utils.get_deny_status(self.ctx))
|
||||||
-- No port opened
|
end
|
||||||
return self:ret(true, "no port open for IP " .. self.ctx.bw.remote_addr)
|
-- Error case
|
||||||
|
return self:ret(false, ret_err)
|
||||||
|
end
|
||||||
|
-- No port opened
|
||||||
|
return self:ret(true, "no port open for IP " .. self.ctx.bw.remote_addr)
|
||||||
end
|
end
|
||||||
|
|
||||||
function reversescan:preread()
|
function reversescan:preread()
|
||||||
return self:access()
|
return self:access()
|
||||||
end
|
end
|
||||||
|
|
||||||
function reversescan.scan(ip, port, timeout)
|
function reversescan.scan(ip, port, timeout)
|
||||||
local tcpsock = ngx.socket.tcp()
|
local tcpsock = ngx.socket.tcp()
|
||||||
tcpsock:settimeout(timeout)
|
tcpsock:settimeout(timeout)
|
||||||
local ok, err = tcpsock:connect(ip, port)
|
local ok, _ = tcpsock:connect(ip, port)
|
||||||
tcpsock:close()
|
tcpsock:close()
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, port
|
return false, port
|
||||||
end
|
end
|
||||||
return true, port
|
return true, port
|
||||||
end
|
end
|
||||||
|
|
||||||
function reversescan:is_in_cache(ip_port)
|
function reversescan:is_in_cache(ip_port)
|
||||||
local ok, data = self.cachestore:get("plugin_reverse_scan_" .. ip_port)
|
local ok, data = self.cachestore:get("plugin_reverse_scan_" .. ip_port)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, data
|
return false, data
|
||||||
end
|
end
|
||||||
return true, data
|
return true, data
|
||||||
end
|
end
|
||||||
|
|
||||||
function reversescan:add_to_cache(ip_port, value)
|
function reversescan:add_to_cache(ip_port, value)
|
||||||
local ok, err = self.cachestore:set("plugin_reverse_scan_" .. ip_port, value, 86400)
|
local ok, err = self.cachestore:set("plugin_reverse_scan_" .. ip_port, value, 86400)
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, err
|
return false, err
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return reversescan
|
return reversescan
|
||||||
|
|
|
@ -1,118 +1,118 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local plugin = require "bunkerweb.plugin"
|
||||||
local utils = require "bunkerweb.utils"
|
local session = require "resty.session"
|
||||||
local session = require "resty.session"
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local sessions = class("sessions", plugin)
|
local sessions = class("sessions", plugin)
|
||||||
|
|
||||||
function sessions:initialize(ctx)
|
function sessions:initialize(ctx)
|
||||||
-- Call parent initialize
|
-- Call parent initialize
|
||||||
plugin.initialize(self, "sessions", ctx)
|
plugin.initialize(self, "sessions", ctx)
|
||||||
-- Check if random cookie name and secrets are already generated
|
-- Check if random cookie name and secrets are already generated
|
||||||
local is_random = {
|
local is_random = {
|
||||||
"SESSIONS_SECRET",
|
"SESSIONS_SECRET",
|
||||||
"SESSIONS_NAME"
|
"SESSIONS_NAME",
|
||||||
}
|
}
|
||||||
self.randoms = {}
|
self.randoms = {}
|
||||||
for i, var in ipairs(is_random) do
|
for _, var in ipairs(is_random) do
|
||||||
if self.variables[var] == "random" then
|
if self.variables[var] == "random" then
|
||||||
local data, err = self.datastore:get("storage_sessions_" .. var)
|
local data, _ = self.datastore:get("storage_sessions_" .. var)
|
||||||
if data then
|
if data then
|
||||||
self.randoms[var] = data
|
self.randoms[var] = data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sessions:set()
|
function sessions:set()
|
||||||
if self.is_loading or self.kind ~= "http" then
|
if self.is_loading or self.kind ~= "http" then
|
||||||
return self:ret(true, "set not needed")
|
return self:ret(true, "set not needed")
|
||||||
end
|
end
|
||||||
local checks = {
|
local checks = {
|
||||||
["IP"] = self.ctx.bw.remote_addr,
|
["IP"] = self.ctx.bw.remote_addr,
|
||||||
["USER_AGENT"] = self.ctx.bw.http_user_agent or ""
|
["USER_AGENT"] = self.ctx.bw.http_user_agent or "",
|
||||||
}
|
}
|
||||||
self.ctx.bw.sessions_checks = {}
|
self.ctx.bw.sessions_checks = {}
|
||||||
for check, value in pairs(checks) do
|
for check, value in pairs(checks) do
|
||||||
if self.variables["SESSIONS_CHECK_" .. check] == "yes" then
|
if self.variables["SESSIONS_CHECK_" .. check] == "yes" then
|
||||||
table.insert(self.ctx.bw.sessions_checks, { check, value })
|
table.insert(self.ctx.bw.sessions_checks, { check, value })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self:ret(true, "success")
|
return self:ret(true, "success")
|
||||||
end
|
end
|
||||||
|
|
||||||
function sessions:init()
|
function sessions:init()
|
||||||
if self.is_loading or self.kind ~= "http" then
|
if self.is_loading or self.kind ~= "http" then
|
||||||
return self:ret(true, "init not needed")
|
return self:ret(true, "init not needed")
|
||||||
end
|
end
|
||||||
-- Get redis vars
|
-- Get redis vars
|
||||||
local redis_vars = {
|
local redis_vars = {
|
||||||
["USE_REDIS"] = "",
|
["USE_REDIS"] = "",
|
||||||
["REDIS_HOST"] = "",
|
["REDIS_HOST"] = "",
|
||||||
["REDIS_PORT"] = "",
|
["REDIS_PORT"] = "",
|
||||||
["REDIS_DATABASE"] = "",
|
["REDIS_DATABASE"] = "",
|
||||||
["REDIS_SSL"] = "",
|
["REDIS_SSL"] = "",
|
||||||
["REDIS_TIMEOUT"] = "",
|
["REDIS_TIMEOUT"] = "",
|
||||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||||
["REDIS_KEEPALIVE_POOL"] = ""
|
["REDIS_KEEPALIVE_POOL"] = "",
|
||||||
}
|
}
|
||||||
for k, v in pairs(redis_vars) do
|
for k, _ in pairs(redis_vars) do
|
||||||
local value, err = utils.get_variable(k, false)
|
local value, err = utils.get_variable(k, false)
|
||||||
if value == nil then
|
if value == nil then
|
||||||
return self:ret(false, "can't get " .. k .. " variable : " .. err)
|
return self:ret(false, "can't get " .. k .. " variable : " .. err)
|
||||||
end
|
end
|
||||||
redis_vars[k] = value
|
redis_vars[k] = value
|
||||||
end
|
end
|
||||||
-- Init configuration
|
-- Init configuration
|
||||||
local config = {
|
local config = {
|
||||||
secret = self.variables["SESSIONS_SECRET"],
|
secret = self.variables["SESSIONS_SECRET"],
|
||||||
cookie_name = self.variables["SESSIONS_NAME"],
|
cookie_name = self.variables["SESSIONS_NAME"],
|
||||||
idling_timeout = tonumber(self.variables["SESSIONS_IDLING_TIMEOUT"]),
|
idling_timeout = tonumber(self.variables["SESSIONS_IDLING_TIMEOUT"]),
|
||||||
rolling_timeout = tonumber(self.variables["SESSIONS_ROLLING_TIMEOUT"]),
|
rolling_timeout = tonumber(self.variables["SESSIONS_ROLLING_TIMEOUT"]),
|
||||||
absolute_timeout = tonumber(self.variables["SESSIONS_ABSOLUTE_TIMEOUT"])
|
absolute_timeout = tonumber(self.variables["SESSIONS_ABSOLUTE_TIMEOUT"]),
|
||||||
}
|
}
|
||||||
if self.variables["SESSIONS_SECRET"] == "random" then
|
if self.variables["SESSIONS_SECRET"] == "random" then
|
||||||
if self.randoms["SESSIONS_SECRET"] then
|
if self.randoms["SESSIONS_SECRET"] then
|
||||||
config.secret = self.randoms["SESSIONS_SECRET"]
|
config.secret = self.randoms["SESSIONS_SECRET"]
|
||||||
else
|
else
|
||||||
config.secret = utils.rand(16)
|
config.secret = utils.rand(16)
|
||||||
local ok, err = self.datastore:set("storage_sessions_SESSIONS_SECRET", config.secret)
|
local ok, err = self.datastore:set("storage_sessions_SESSIONS_SECRET", config.secret)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.variables["SESSIONS_NAME"] == "random" then
|
if self.variables["SESSIONS_NAME"] == "random" then
|
||||||
if self.randoms["SESSIONS_NAME"] then
|
if self.randoms["SESSIONS_NAME"] then
|
||||||
config.cookie_name = self.randoms["SESSIONS_NAME"]
|
config.cookie_name = self.randoms["SESSIONS_NAME"]
|
||||||
else
|
else
|
||||||
config.cookie_name = utils.rand(16)
|
config.cookie_name = utils.rand(16)
|
||||||
local ok, err = self.datastore:set("storage_sessions_SESSIONS_NAME", config.cookie_name)
|
local ok, err = self.datastore:set("storage_sessions_SESSIONS_NAME", config.cookie_name)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
self.logger:log(ngx.ERR, "error from datastore:set : " .. err)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if redis_vars["USE_REDIS"] ~= "yes" then
|
if redis_vars["USE_REDIS"] ~= "yes" then
|
||||||
config.storage = "cookie"
|
config.storage = "cookie"
|
||||||
else
|
else
|
||||||
config.storage = "redis"
|
config.storage = "redis"
|
||||||
config.redis = {
|
config.redis = {
|
||||||
prefix = "sessions_",
|
prefix = "sessions_",
|
||||||
connect_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
connect_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||||
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||||
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||||
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
|
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
|
||||||
pool = "bw-redis",
|
pool = "bw-redis",
|
||||||
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
|
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
|
||||||
ssl = redis_vars["REDIS_SSL"] == "yes",
|
ssl = redis_vars["REDIS_SSL"] == "yes",
|
||||||
host = redis_vars["REDIS_HOST"],
|
host = redis_vars["REDIS_HOST"],
|
||||||
port = tonumber(redis_vars["REDIS_PORT"]),
|
port = tonumber(redis_vars["REDIS_PORT"]),
|
||||||
database = tonumber(redis_vars["REDIS_DATABASE"])
|
database = tonumber(redis_vars["REDIS_DATABASE"]),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
session.init(config)
|
session.init(config)
|
||||||
return self:ret(true, "sessions init successful")
|
return self:ret(true, "sessions init successful")
|
||||||
end
|
end
|
||||||
|
|
||||||
return sessions
|
return sessions
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
local class = require "middleclass"
|
local class = require "middleclass"
|
||||||
local plugin = require "bunkerweb.plugin"
|
local env = require "resty.env"
|
||||||
local utils = require "bunkerweb.utils"
|
|
||||||
local ipmatcher = require "resty.ipmatcher"
|
local ipmatcher = require "resty.ipmatcher"
|
||||||
local env = require "resty.env"
|
local plugin = require "bunkerweb.plugin"
|
||||||
|
local utils = require "bunkerweb.utils"
|
||||||
|
|
||||||
local whitelist = class("whitelist", plugin)
|
local whitelist = class("whitelist", plugin)
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ function whitelist:initialize(ctx)
|
||||||
["RDNS"] = {},
|
["RDNS"] = {},
|
||||||
["ASN"] = {},
|
["ASN"] = {},
|
||||||
["USER_AGENT"] = {},
|
["USER_AGENT"] = {},
|
||||||
["URI"] = {}
|
["URI"] = {},
|
||||||
}
|
}
|
||||||
for kind, _ in pairs(kinds) do
|
for kind, _ in pairs(kinds) do
|
||||||
for data in self.variables["WHITELIST_" .. kind]:gmatch("%S+") do
|
for data in self.variables["WHITELIST_" .. kind]:gmatch("%S+") do
|
||||||
|
@ -64,11 +64,11 @@ function whitelist:init()
|
||||||
["RDNS"] = {},
|
["RDNS"] = {},
|
||||||
["ASN"] = {},
|
["ASN"] = {},
|
||||||
["USER_AGENT"] = {},
|
["USER_AGENT"] = {},
|
||||||
["URI"] = {}
|
["URI"] = {},
|
||||||
}
|
}
|
||||||
local i = 0
|
local i = 0
|
||||||
for kind, _ in pairs(whitelists) do
|
for kind, _ in pairs(whitelists) do
|
||||||
local f, err = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
local f, _ = io.open("/var/cache/bunkerweb/whitelist/" .. kind .. ".list", "r")
|
||||||
if f then
|
if f then
|
||||||
for line in f:lines() do
|
for line in f:lines() do
|
||||||
table.insert(whitelists[kind], line)
|
table.insert(whitelists[kind], line)
|
||||||
|
@ -123,13 +123,14 @@ function whitelist:access()
|
||||||
return self:ret(true, err, ngx.OK)
|
return self:ret(true, err, ngx.OK)
|
||||||
end
|
end
|
||||||
-- Perform checks
|
-- Perform checks
|
||||||
for k, v in pairs(already_cached) do
|
local ok
|
||||||
|
for k, _ in pairs(already_cached) do
|
||||||
if not already_cached[k] then
|
if not already_cached[k] then
|
||||||
local ok, whitelisted = self:is_whitelisted(k)
|
ok, whitelisted = self:is_whitelisted(k)
|
||||||
if ok == nil then
|
if ok == nil then
|
||||||
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
self.logger:log(ngx.ERR, "error while checking if " .. k .. " is whitelisted : " .. whitelisted)
|
||||||
else
|
else
|
||||||
local ok, err = self:add_to_cache(self:kind_to_ele(k), whitelisted)
|
ok, err = self:add_to_cache(self:kind_to_ele(k), whitelisted)
|
||||||
if not ok then
|
if not ok then
|
||||||
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
self.logger:log(ngx.ERR, "error while adding element to cache : " .. err)
|
||||||
end
|
end
|
||||||
|
@ -163,7 +164,7 @@ end
|
||||||
function whitelist:check_cache()
|
function whitelist:check_cache()
|
||||||
-- Check the caches
|
-- Check the caches
|
||||||
local checks = {
|
local checks = {
|
||||||
["IP"] = "ip" .. self.ctx.bw.remote_addr
|
["IP"] = "ip" .. self.ctx.bw.remote_addr,
|
||||||
}
|
}
|
||||||
if self.ctx.bw.http_user_agent then
|
if self.ctx.bw.http_user_agent then
|
||||||
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
checks["UA"] = "ua" .. self.ctx.bw.http_user_agent
|
||||||
|
@ -172,7 +173,7 @@ function whitelist:check_cache()
|
||||||
checks["URI"] = "uri" .. self.ctx.bw.uri
|
checks["URI"] = "uri" .. self.ctx.bw.uri
|
||||||
end
|
end
|
||||||
local already_cached = {}
|
local already_cached = {}
|
||||||
for k, v in pairs(checks) do
|
for k, _ in pairs(checks) do
|
||||||
already_cached[k] = false
|
already_cached[k] = false
|
||||||
end
|
end
|
||||||
for k, v in pairs(checks) do
|
for k, v in pairs(checks) do
|
||||||
|
@ -242,14 +243,15 @@ function whitelist:is_whitelisted_ip()
|
||||||
end
|
end
|
||||||
if check_rdns then
|
if check_rdns then
|
||||||
-- Get rDNS
|
-- Get rDNS
|
||||||
|
-- luacheck: ignore 421
|
||||||
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
local rdns_list, err = utils.get_rdns(self.ctx.bw.remote_addr)
|
||||||
-- Check if rDNS is in whitelist
|
-- Check if rDNS is in whitelist
|
||||||
if rdns_list then
|
if rdns_list then
|
||||||
local forward_check = nil
|
local forward_check = nil
|
||||||
local rdns_suffix = nil
|
local rdns_suffix = nil
|
||||||
for i, rdns in ipairs(rdns_list) do
|
for _, rdns in ipairs(rdns_list) do
|
||||||
for j, suffix in ipairs(self.lists["RDNS"]) do
|
for _, suffix in ipairs(self.lists["RDNS"]) do
|
||||||
if rdns:sub(- #suffix) == suffix then
|
if rdns:sub(-#suffix) == suffix then
|
||||||
forward_check = rdns
|
forward_check = rdns
|
||||||
rdns_suffix = suffix
|
rdns_suffix = suffix
|
||||||
break
|
break
|
||||||
|
@ -262,12 +264,15 @@ function whitelist:is_whitelisted_ip()
|
||||||
if forward_check then
|
if forward_check then
|
||||||
local ip_list, err = utils.get_ips(forward_check)
|
local ip_list, err = utils.get_ips(forward_check)
|
||||||
if ip_list then
|
if ip_list then
|
||||||
for i, ip in ipairs(ip_list) do
|
for _, ip in ipairs(ip_list) do
|
||||||
if ip == self.ctx.bw.remote_addr then
|
if ip == self.ctx.bw.remote_addr then
|
||||||
return true, "rDNS " .. rdns_suffix
|
return true, "rDNS " .. rdns_suffix
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self.logger:log(ngx.WARN, "IP " .. self.ctx.bw.remote_addr .. " may spoof reverse DNS " .. forward_check)
|
self.logger:log(
|
||||||
|
ngx.WARN,
|
||||||
|
"IP " .. self.ctx.bw.remote_addr .. " may spoof reverse DNS " .. forward_check
|
||||||
|
)
|
||||||
else
|
else
|
||||||
self.logger:log(ngx.ERR, "error while getting rdns (forward check) : " .. err)
|
self.logger:log(ngx.ERR, "error while getting rdns (forward check) : " .. err)
|
||||||
end
|
end
|
||||||
|
@ -283,7 +288,7 @@ function whitelist:is_whitelisted_ip()
|
||||||
if not asn then
|
if not asn then
|
||||||
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
self.logger:log(ngx.ERR, "can't get ASN of IP " .. self.ctx.bw.remote_addr .. " : " .. err)
|
||||||
else
|
else
|
||||||
for i, bl_asn in ipairs(self.lists["ASN"]) do
|
for _, bl_asn in ipairs(self.lists["ASN"]) do
|
||||||
if bl_asn == tostring(asn) then
|
if bl_asn == tostring(asn) then
|
||||||
return true, "ASN " .. bl_asn
|
return true, "ASN " .. bl_asn
|
||||||
end
|
end
|
||||||
|
@ -297,7 +302,7 @@ end
|
||||||
|
|
||||||
function whitelist:is_whitelisted_uri()
|
function whitelist:is_whitelisted_uri()
|
||||||
-- Check if URI is in whitelist
|
-- Check if URI is in whitelist
|
||||||
for i, uri in ipairs(self.lists["URI"]) do
|
for _, uri in ipairs(self.lists["URI"]) do
|
||||||
if utils.regex_match(self.ctx.bw.uri, uri) then
|
if utils.regex_match(self.ctx.bw.uri, uri) then
|
||||||
return true, "URI " .. uri
|
return true, "URI " .. uri
|
||||||
end
|
end
|
||||||
|
@ -308,7 +313,7 @@ end
|
||||||
|
|
||||||
function whitelist:is_whitelisted_ua()
|
function whitelist:is_whitelisted_ua()
|
||||||
-- Check if UA is in whitelist
|
-- Check if UA is in whitelist
|
||||||
for i, ua in ipairs(self.lists["USER_AGENT"]) do
|
for _, ua in ipairs(self.lists["USER_AGENT"]) do
|
||||||
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
if utils.regex_match(self.ctx.bw.http_user_agent, ua) then
|
||||||
return true, "UA " .. ua
|
return true, "UA " .. ua
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
call_parentheses = "Input"
|
||||||
|
|
||||||
|
[sort_requires]
|
||||||
|
enabled = true
|
Loading…
Reference in New Issue