share common objects during the phase and add threading to DNSBL and reverse scan
This commit is contained in:
parent
b19ebbe6a8
commit
eea6d32cd3
3
TODO
3
TODO
|
@ -1,6 +1,5 @@
|
|||
- Ansible
|
||||
- Vagrant
|
||||
- Plugins
|
||||
- sessions helpers in utils
|
||||
- sessions security : check IP address, check UA, ...
|
||||
- Find a way to do rdns in background
|
||||
- fix db warnings (Got an error reading communication packets)
|
||||
|
|
|
@ -253,14 +253,19 @@ That kind of security is implemented but not enabled by default in BunkerWeb and
|
|||
|
||||
Here is the list of related settings :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--------------------------------------------------------: | :----------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `USE_ANTIBOT` | `no` | Accepted values to enable Antibot feature : `cookie`, `javascript`, `captcha`, `hcaptcha` and `recaptcha`. |
|
||||
| `ANTIBOT_URI` | `/challenge` | URI that clients will be redirected to in order to solve the challenge. Be sure that it isn't used in your web application. |
|
||||
| `ANTIBOT_SESSION_SECRET` | `random` | The secret used to encrypt cookies when using Antibot. The special value `random` will generate one for you. Be sure to set it when you use a clustered integration (32 chars). |
|
||||
| `ANTIBOT_HCAPTCHA_SITEKEY` and `ANTIBOT_RECAPTCHA_SITEKEY` | | The Sitekey value to use when `USE_ANTIBOT` is set to `hcaptcha` or `recaptcha`. |
|
||||
| `ANTIBOT_HCAPTCHA_SECRET` and `ANTIBOT_RECAPTCHA_SECRET` | | The Secret value to use when `USE_ANTIBOT` is set to `hcaptcha` or `recaptcha`. |
|
||||
| `ANTIBOT_RECAPTCHA_SCORE` | `0.7` | The minimum score that clients must have when `USE_ANTIBOT` is set to `recaptcha`. |
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------|------------|---------|--------|------------------------------------------------------------------------------------------------------------------------------|
|
||||
|`USE_ANTIBOT` |`no` |multisite|no |Activate antibot feature. |
|
||||
|`ANTIBOT_URI` |`/challenge`|multisite|no |Unused URI that clients will be redirected to to solve the challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SCORE` |`0.7` |multisite|no |Minimum score required for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SITEKEY`| |multisite|no |Sitekey for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SECRET` | |multisite|no |Secret for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_HCAPTCHA_SITEKEY` | |multisite|no |Sitekey for hCaptcha challenge. |
|
||||
|`ANTIBOT_HCAPTCHA_SECRET` | |multisite|no |Secret for hCaptcha challenge. |
|
||||
|`ANTIBOT_TIME_RESOLVE` |`60` |multisite|no |Maximum time (in seconds) clients have to resolve the challenge. Once this time has passed, a new challenge will be generated.|
|
||||
|`ANTIBOT_TIME_VALID` |`86400` |multisite|no |Maximum validity time of solved challenges. Once this time has passed, clients will need to resolve a new one. |
|
||||
|
||||
Please note that antibot feature is using a cookie to maintain a session with clients. If you are using BunkerWeb in a clustered environment, you will need to set the `SESSIONS_SECRET` and `SESSIONS_NAME` settings to another value than the default one (which is `random`). You will find more info about sessions [here](settings.md#sessions).
|
||||
|
||||
## Blacklisting, whitelisting and greylisting
|
||||
|
||||
|
|
|
@ -498,6 +498,8 @@ Management of session used by other plugins.
|
|||
|`SESSIONS_IDLING_TIMEOUT` |`1800` |global |no |Maximum time (in seconds) of inactivity before the session is invalidated. |
|
||||
|`SESSIONS_ROLLING_TIMEOUT` |`3600` |global |no |Maximum time (in seconds) before a session must be renewed. |
|
||||
|`SESSIONS_ABSOLUTE_TIMEOUT`|`86400` |global |no |Maximum time (in seconds) before a session is destroyed. |
|
||||
|`SESSIONS_CHECK_IP` |`yes` |global |no |Destroy session if IP address is different than original one. |
|
||||
|`SESSIONS_CHECK_USER_AGENT`|`yes` |global |no |Destroy session if User-Agent is different than original one. |
|
||||
|
||||
### UI
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local mlcache = require "resty.mlcache"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local class = require "middleclass"
|
||||
|
@ -41,17 +42,22 @@ if not cache then
|
|||
module_logger:log(ngx.ERR, "can't instantiate mlcache : " .. err)
|
||||
end
|
||||
|
||||
function cachestore:initialize(use_redis)
|
||||
function cachestore:initialize(use_redis, new_cs)
|
||||
self.cache = cache
|
||||
self.use_redis = (use_redis and utils.is_cosocket_available()) or false
|
||||
self.use_redis = use_redis or false
|
||||
self.logger = module_logger
|
||||
if new_cs then
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
else
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore")
|
||||
end
|
||||
end
|
||||
|
||||
function cachestore:get(key)
|
||||
local callback = function(key)
|
||||
-- Connect to redis
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
local ok, err, reused = clusterstore:connect()
|
||||
if not ok then
|
||||
return nil, "can't connect to redis : " .. err, nil
|
||||
end
|
||||
|
@ -88,7 +94,7 @@ function cachestore:get(key)
|
|||
return nil, nil, -1
|
||||
end
|
||||
local value, err, hit_level
|
||||
if self.use_redis then
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
value, err, hit_level = self.cache:get(key, nil, callback, key)
|
||||
else
|
||||
value, err, hit_level = self.cache:get(key, nil, callback_no_miss)
|
||||
|
@ -101,7 +107,7 @@ function cachestore:get(key)
|
|||
end
|
||||
|
||||
function cachestore:set(key, value, ex)
|
||||
if self.use_redis then
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local ok, err = self:set_redis(key, value, ex)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
|
@ -121,24 +127,23 @@ end
|
|||
|
||||
function cachestore:set_redis(key, value, ex)
|
||||
-- Connect to redis
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
local ok, err, reused = self.clusterstore:connect()
|
||||
if not ok then
|
||||
return false, "can't connect to redis : " .. err
|
||||
end
|
||||
-- Set value with ttl
|
||||
local default_ex = ex or 30
|
||||
local ok, err = clusterstore:call("set", key, value, "EX", default_ex)
|
||||
local ok, err = self.clusterstore:call("set", key, value, "EX", default_ex)
|
||||
if err then
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return false, "SET failed : " .. err
|
||||
end
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return true
|
||||
end
|
||||
|
||||
function cachestore:delete(key, value, ex)
|
||||
if self.use_redis then
|
||||
if self.use_redis and utils.is_cosocket_available() then
|
||||
local ok, err = self.del_redis(key)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
|
@ -153,18 +158,17 @@ end
|
|||
|
||||
function cachestore:del_redis(key)
|
||||
-- Connect to redis
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local ok, err = clusterstore:connect()
|
||||
local ok, err = self.clusterstore:connect()
|
||||
if not ok then
|
||||
return false, "can't connect to redis : " .. err
|
||||
end
|
||||
-- Set value with ttl
|
||||
local ok, err = clusterstore:del(key)
|
||||
local ok, err = self.clusterstore:del(key)
|
||||
if err then
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return false, "DEL failed : " .. err
|
||||
end
|
||||
clusterstore:close()
|
||||
self.clusterstore:close()
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ local redis = require "resty.redis"
|
|||
|
||||
local clusterstore = class("clusterstore")
|
||||
|
||||
function clusterstore:initialize()
|
||||
function clusterstore:initialize(pool)
|
||||
-- Instantiate logger
|
||||
self.logger = logger:new("CLUSTERSTORE")
|
||||
-- Get variables
|
||||
|
@ -29,12 +29,13 @@ function clusterstore:initialize()
|
|||
end
|
||||
-- Don't instantiate a redis object for now
|
||||
self.redis_client = nil
|
||||
self.pool = pool == nil or pool
|
||||
end
|
||||
|
||||
function clusterstore:connect()
|
||||
-- Check if we are already connected
|
||||
if self.redis_client ~= nil then
|
||||
return true, "already connected"
|
||||
if self.redis_client then
|
||||
return true, "already connected", self.redis_client:get_reused_times()
|
||||
end
|
||||
-- Instantiate object
|
||||
local redis_client, err = redis:new()
|
||||
|
@ -42,42 +43,50 @@ function clusterstore:connect()
|
|||
return false, err
|
||||
end
|
||||
-- Set timeouts
|
||||
redis_client:set_timeouts(tonumber(self.variables["REDIS_TIMEOUT"]), tonumber(self.variables["REDIS_TIMEOUT"]),
|
||||
tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
redis_client:set_timeout(tonumber(self.variables["REDIS_TIMEOUT"]))
|
||||
-- Connect
|
||||
local options = {
|
||||
ssl = self.variables["REDIS_SSL"] == "yes",
|
||||
pool = "bw",
|
||||
pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
}
|
||||
if self.pool then
|
||||
options.pool = "bw-redis"
|
||||
options.pool_size = tonumber(self.variables["REDIS_KEEPALIVE_POOL"])
|
||||
end
|
||||
local ok, err = redis_client:connect(self.variables["REDIS_HOST"], tonumber(self.variables["REDIS_PORT"]), options)
|
||||
if not ok then
|
||||
return false, err
|
||||
end
|
||||
-- Save client
|
||||
self.redis_client = redis_client
|
||||
-- Select database if needed
|
||||
local times, err = redis_client:get_reused_times()
|
||||
local times, err = self.redis_client:get_reused_times()
|
||||
if err then
|
||||
self:close()
|
||||
return false, err
|
||||
end
|
||||
if times == 0 then
|
||||
local select, err = redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||
local select, err = self.redis_client:select(tonumber(self.variables["REDIS_DATABASE"]))
|
||||
if err then
|
||||
self:close()
|
||||
return false, err
|
||||
end
|
||||
end
|
||||
return true, "success"
|
||||
return true, "success", times
|
||||
end
|
||||
|
||||
function clusterstore:close()
|
||||
if self.redis_client then
|
||||
-- Equivalent to close but keep a pool of connections
|
||||
local ok, err = self.redis_client:set_keepalive(tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]),
|
||||
tonumber(self.variables["REDIS_KEEPALIVE_POOL"]))
|
||||
self.redis_client = nil
|
||||
if self.pool then
|
||||
local ok, err = self.redis_client:set_keepalive(tonumber(self.variables["REDIS_KEEPALIVE_IDLE"]), tonumber(self.variables["REDIS_KEEPALIVE_POOL"]))
|
||||
self.redis_client = nil
|
||||
if not ok then
|
||||
require "bunkerweb.logger":new("clusterstore-close"):log(ngx.ERR, err)
|
||||
end
|
||||
return ok, err
|
||||
end
|
||||
-- Close
|
||||
local ok, err = self.redis_client:close()
|
||||
self.redis_client.redis_client = nil
|
||||
return ok, err
|
||||
end
|
||||
return false, "not connected"
|
||||
|
|
|
@ -146,45 +146,52 @@ helpers.call_plugin = function(plugin, method)
|
|||
end
|
||||
|
||||
helpers.fill_ctx = function()
|
||||
-- Check if ctx is already filled
|
||||
if ngx.ctx.bw then
|
||||
return true, "already filled"
|
||||
end
|
||||
-- Return errors as table
|
||||
local errors = {}
|
||||
-- Instantiate bw table
|
||||
local data = {}
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
data.kind = "stream"
|
||||
local use_redis = nil
|
||||
-- Check if ctx is already filled
|
||||
if not ngx.ctx.bw then
|
||||
-- Instantiate bw table
|
||||
local data = {}
|
||||
-- Common vars
|
||||
data.kind = "http"
|
||||
if ngx.shared.datastore_stream then
|
||||
data.kind = "stream"
|
||||
end
|
||||
data.remote_addr = ngx.var.remote_addr
|
||||
data.uri = ngx.var.uri
|
||||
data.request_uri = ngx.var.request_uri
|
||||
data.request_method = ngx.var.request_method
|
||||
data.http_user_agent = ngx.var.http_user_agent
|
||||
data.http_host = ngx.var.http_host
|
||||
data.server_name = ngx.var.server_name
|
||||
data.http_content_type = ngx.var.http_content_type
|
||||
data.http_origin = ngx.var.http_origin
|
||||
-- IP data : global
|
||||
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
|
||||
if ip_is_global == nil then
|
||||
table.insert(errors, "can't check if IP is global : " .. err)
|
||||
else
|
||||
data.ip_is_global = ip_is_global
|
||||
end
|
||||
-- IP data : v4 / v6
|
||||
data.ip_is_ipv4 = utils.is_ipv4(data.ip)
|
||||
data.ip_is_ipv6 = utils.is_ipv6(data.ip)
|
||||
-- Misc info
|
||||
data.integration = utils.get_integration()
|
||||
data.version = utils.get_version()
|
||||
-- Common objects
|
||||
use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
table.insert(errors, "can't get variable from datastore : " .. err)
|
||||
end
|
||||
-- Fill ctx
|
||||
ngx.ctx.bw = data
|
||||
end
|
||||
data.remote_addr = ngx.var.remote_addr
|
||||
data.uri = ngx.var.uri
|
||||
data.request_uri = ngx.var.request_uri
|
||||
data.request_method = ngx.var.request_method
|
||||
data.http_user_agent = ngx.var.http_user_agent
|
||||
data.http_host = ngx.var.http_host
|
||||
data.server_name = ngx.var.server_name
|
||||
data.http_content_type = ngx.var.http_content_type
|
||||
data.http_origin = ngx.var.http_origin
|
||||
-- IP data : global
|
||||
local ip_is_global, err = utils.ip_is_global(data.remote_addr)
|
||||
if ip_is_global == nil then
|
||||
table.insert(errors, "can't check if IP is global : " .. err)
|
||||
else
|
||||
data.ip_is_global = ip_is_global
|
||||
end
|
||||
-- IP data : v4 / v6
|
||||
data.ip_is_ipv4 = utils.is_ipv4(data.ip)
|
||||
data.ip_is_ipv6 = utils.is_ipv6(data.ip)
|
||||
-- Misc info
|
||||
data.integration = utils.get_integration()
|
||||
data.version = utils.get_version()
|
||||
-- Plugins
|
||||
data.plugins = {}
|
||||
-- Fill ctx
|
||||
ngx.ctx.bw = data
|
||||
-- Always create new objects for current phases in case of cosockets
|
||||
ngx.ctx.bw.datastore = require "bunkerweb.datastore":new()
|
||||
ngx.ctx.bw.clusterstore = require "bunkerweb.clusterstore":new()
|
||||
ngx.ctx.bw.cachestore = require "bunkerweb.cachestore":new(use_redis == "yes")
|
||||
return true, "ctx filled", errors
|
||||
end
|
||||
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
local class = require "middleclass"
|
||||
local logger = require "bunkerweb.logger"
|
||||
local datastore = require "bunkerweb.datastore"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local clusterstore = require "bunkerweb.clusterstore"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cjson = require "cjson"
|
||||
local plugin = class("plugin")
|
||||
|
||||
function plugin:initialize(id)
|
||||
-- Store default values
|
||||
-- Store common, values
|
||||
self.id = id
|
||||
self.variables = {}
|
||||
-- Instantiate objects
|
||||
self.logger = logger:new(id)
|
||||
self.datastore = datastore:new()
|
||||
-- Get metadata
|
||||
local encoded_metadata, err = self.datastore:get("plugin_" .. id)
|
||||
if not encoded_metadata then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
-- Store variables
|
||||
local metadata = cjson.decode(encoded_metadata)
|
||||
local multisite = false
|
||||
local current_phase = ngx.get_phase()
|
||||
for i, check_phase in ipairs({ "set", "access", "content", "header", "log", "preread", "log_stream", "log_default" }) do
|
||||
|
@ -29,6 +19,31 @@ function plugin:initialize(id)
|
|||
end
|
||||
end
|
||||
self.is_request = multisite
|
||||
-- Store common objets
|
||||
self.logger = logger:new(self.id)
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
if self.is_request then
|
||||
self.datastore = utils.get_ctx_obj("datastore") or datastore:new()
|
||||
self.cachestore = utils.get_ctx_obj("cachestore") or cachestore:new(use_redis == "yes", true)
|
||||
self.clusterstore = utils.get_ctx_obj("clusterstore") or clusterstore:new(false)
|
||||
else
|
||||
self.datastore = datastore:new()
|
||||
self.cachestore = cachestore:new(use_redis == "yes", true)
|
||||
self.clusterstore = clusterstore:new(false)
|
||||
end
|
||||
-- Get metadata
|
||||
local encoded_metadata, err = self.datastore:get("plugin_" .. id)
|
||||
if not encoded_metadata then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
return
|
||||
end
|
||||
-- Store variables
|
||||
self.variables = {}
|
||||
local metadata = cjson.decode(encoded_metadata)
|
||||
for k, v in pairs(metadata.settings) do
|
||||
local value, err = utils.get_variable(k, v.context == "multisite" and multisite)
|
||||
if value == nil then
|
||||
|
|
|
@ -680,7 +680,7 @@ utils.new_cachestore = function()
|
|||
use_redis = use_redis == "yes"
|
||||
end
|
||||
-- Instantiate
|
||||
return require "bunkerweb.cachestore":new(use_redis)
|
||||
return require "bunkerweb.cachestore":new(use_redis, true)
|
||||
end
|
||||
|
||||
utils.regex_match = function(str, regex, options)
|
||||
|
@ -725,4 +725,20 @@ utils.is_cosocket_available = function()
|
|||
return false
|
||||
end
|
||||
|
||||
utils.kill_all_threads = function(threads)
|
||||
for i, thread in ipairs(threads) do
|
||||
local ok, err = ngx.thread.kill(thread)
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "error while killing thread : " .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.get_ctx_obj = function(obj)
|
||||
if ngx.ctx and ngx.ctx.bw then
|
||||
return ngx.ctx.bw[obj]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
return utils
|
||||
|
|
|
@ -49,6 +49,10 @@ lua_shared_dict cachestore {{ CACHESTORE_MEMORY_SIZE }};
|
|||
lua_shared_dict cachestore_ipc {{ CACHESTORE_IPC_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_miss {{ CACHESTORE_MISS_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_locks {{ CACHESTORE_LOCKS_MEMORY_SIZE }};
|
||||
# only show LUA socket errors at info/debug
|
||||
{% if LOG_LEVEL != "info" and LOG_LEVEL != "debug" %}
|
||||
lua_socket_log_errors off;
|
||||
{% endif %}
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-lua.conf;
|
||||
|
|
|
@ -11,13 +11,6 @@ local logger = clogger:new("INIT")
|
|||
local datastore = cdatastore:new()
|
||||
logger:log(ngx.NOTICE, "init phase started")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new()
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Remove previous data from the datastore
|
||||
logger:log(ngx.NOTICE, "deleting old keys from datastore ...")
|
||||
local data_keys = {"^plugin", "^variable_", "^api_", "^misc_"}
|
||||
|
@ -50,6 +43,13 @@ for line in io.lines("/etc/nginx/variables.env") do
|
|||
end
|
||||
logger:log(ngx.NOTICE, "saved variables into datastore")
|
||||
|
||||
-- Purge cache
|
||||
local cachestore = require "bunkerweb.cachestore":new(false, true)
|
||||
local ok, err = cachestore:purge()
|
||||
if not ok then
|
||||
logger:log(ngx.ERR, "can't purge cachestore : " .. err)
|
||||
end
|
||||
|
||||
-- Set API values into the datastore
|
||||
logger:log(ngx.NOTICE, "saving API values into datastore ...")
|
||||
local value, err = datastore:get("variable_USE_API")
|
||||
|
|
|
@ -23,7 +23,7 @@ local ready_work = function(premature)
|
|||
end
|
||||
|
||||
-- Instantiate lock
|
||||
local lock = require "resty.lock":new("worker_lock")
|
||||
local lock = require "resty.lock":new("worker_lock", {timeout = 10})
|
||||
if not lock then
|
||||
logger:log(ngx.ERR, "lock:new() failed : " .. err)
|
||||
return
|
||||
|
|
|
@ -33,6 +33,10 @@ lua_shared_dict cachestore_stream {{ CACHESTORE_MEMORY_SIZE }};
|
|||
lua_shared_dict cachestore_ipc_stream {{ CACHESTORE_IPC_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_miss_stream {{ CACHESTORE_MISS_MEMORY_SIZE }};
|
||||
lua_shared_dict cachestore_locks_stream {{ CACHESTORE_LOCKS_MEMORY_SIZE }};
|
||||
# only show LUA socket errors at info/debug
|
||||
{% if LOG_LEVEL != "info" and LOG_LEVEL != "debug" %}
|
||||
lua_socket_log_errors off;
|
||||
{% endif %}
|
||||
|
||||
# LUA init block
|
||||
include /etc/nginx/init-stream-lua.conf;
|
||||
|
|
|
@ -7,12 +7,6 @@ local badbehavior = class("badbehavior", plugin)
|
|||
function badbehavior:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "badbehavior")
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
end
|
||||
|
||||
function badbehavior:log()
|
||||
|
@ -146,7 +140,7 @@ end
|
|||
|
||||
function badbehavior.redis_increase(ip, count_time, ban_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_incr = redis.pcall("INCR", KEYS[1])
|
||||
|
@ -188,7 +182,7 @@ end
|
|||
|
||||
function badbehavior.redis_decrease(ip, count_time)
|
||||
-- Instantiate objects
|
||||
local clusterstore = require "bunkerweb.clusterstore":new()
|
||||
local clusterstore = require "bunkerweb.clusterstore":new(false)
|
||||
-- Our LUA script to execute on redis
|
||||
local redis_script = [[
|
||||
local ret_decr = redis.pcall("DECR", KEYS[1])
|
||||
|
|
|
@ -11,12 +11,6 @@ local blacklist = class("blacklist", plugin)
|
|||
function blacklist:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "blacklist")
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_blacklist_lists")
|
||||
|
@ -47,8 +41,6 @@ function blacklist:initialize()
|
|||
end
|
||||
end
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function blacklist:is_needed()
|
||||
|
|
|
@ -9,13 +9,6 @@ local country = class("country", plugin)
|
|||
function country:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "country")
|
||||
-- Instantiate cachestore
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function country:access()
|
||||
|
|
|
@ -10,13 +10,6 @@ local dnsbl = class("dnsbl", plugin)
|
|||
function dnsbl:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "dnsbl")
|
||||
-- Instantiate cachestore
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function dnsbl:init_worker()
|
||||
|
@ -32,9 +25,18 @@ function dnsbl:init_worker()
|
|||
return self:ret(true, "no service uses DNSBL, skipping init_worker")
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
local result, err = self:is_in_dnsbl("127.0.0.2", server)
|
||||
if result == nil then
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, "127.0.0.2", server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
local ok, result, server, err = ngx.thread.wait(thread)
|
||||
if not ok then
|
||||
self.logger:log(ngx.ERR, "error while waiting thread of " .. dnsbl .. " check : " .. result)
|
||||
elseif result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
elseif not result then
|
||||
self.logger:log(ngx.ERR, "dnsbl check for " .. server .. " failed")
|
||||
|
@ -69,25 +71,74 @@ function dnsbl:access()
|
|||
utils.get_deny_status())
|
||||
end
|
||||
-- Loop on DNSBL list
|
||||
local threads = {}
|
||||
for server in self.variables["DNSBL_LIST"]:gmatch("%S+") do
|
||||
local result, err = self:is_in_dnsbl(ngx.ctx.bw.remote_addr, server)
|
||||
-- Create thread
|
||||
local thread = ngx.thread.spawn(self.is_in_dnsbl, self, ngx.ctx.bw.remote_addr, server)
|
||||
threads[server] = thread
|
||||
end
|
||||
-- Wait for threads
|
||||
local ret_threads = nil
|
||||
local ret_err = nil
|
||||
local ret_server = nil
|
||||
while true do
|
||||
-- Compute threads to wait
|
||||
local wait_threads = {}
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
-- No server reported IP
|
||||
if #wait_threads == 0 then
|
||||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, result, server, err = ngx.thread.wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error while waiting thread : " .. result
|
||||
break
|
||||
end
|
||||
-- Remove thread from list
|
||||
threads[server] = nil
|
||||
-- DNS error
|
||||
if result == nil then
|
||||
self.logger:log(ngx.ERR, "error while sending DNS request to " .. server .. " : " .. err)
|
||||
end
|
||||
-- IP is in DNSBL
|
||||
if result then
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr, server)
|
||||
ret_threads = true
|
||||
ret_err = "IP is blacklisted by " .. server
|
||||
ret_server = server
|
||||
break
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
-- Kill other threads
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for dnsbl, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Blacklisted by a server : add to cache and deny access
|
||||
if ret_threads then
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr, ret_server)
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
return self:ret(true, "IP is blacklisted by " .. server, utils.get_deny_status())
|
||||
return self:ret(true, "IP is blacklisted by " .. ret_server, utils.get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- IP is not in DNSBL
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr, "ok")
|
||||
if not ok then
|
||||
return self:ret(false, "IP is not in DNSBL (error = " .. err .. ")")
|
||||
end
|
||||
return self:ret(true, "IP is not in DNSBL", false, nil)
|
||||
return self:ret(true, "IP is not in DNSBL")
|
||||
end
|
||||
|
||||
function dnsbl:preread()
|
||||
|
@ -114,14 +165,15 @@ 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, err
|
||||
return nil, server, err
|
||||
end
|
||||
for i, ip in ipairs(ips) do
|
||||
if ip:find("^127%.0%.0%.") then
|
||||
return true, "success"
|
||||
ngx.sleep(5)
|
||||
return true, server
|
||||
end
|
||||
end
|
||||
return false, "success"
|
||||
return false, server
|
||||
end
|
||||
|
||||
return dnsbl
|
||||
|
|
|
@ -10,12 +10,6 @@ local greylist = class("greylist", plugin)
|
|||
function greylist:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "greylist")
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_greylist_lists")
|
||||
|
@ -41,8 +35,6 @@ function greylist:initialize()
|
|||
end
|
||||
end
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function greylist:is_needed()
|
||||
|
|
|
@ -10,13 +10,6 @@ local limit = class("limit", plugin)
|
|||
function limit:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "limit")
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
self.clusterstore = clusterstore:new()
|
||||
-- Load rules if needed
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
-- Get all rules from datastore
|
||||
|
|
|
@ -9,7 +9,6 @@ local redis = class("redis", plugin)
|
|||
function redis:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "redis")
|
||||
self.clusterstore = clusterstore:new()
|
||||
end
|
||||
|
||||
function redis:init_worker()
|
||||
|
|
|
@ -2,19 +2,13 @@ local class = require "middleclass"
|
|||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local cachestore = require "bunkerweb.cachestore"
|
||||
local cjson = require "cjson"
|
||||
|
||||
local reversescan = class("reversescan", plugin)
|
||||
|
||||
function reversescan:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "reversescan")
|
||||
-- Instantiate cachestore
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function reversescan:access()
|
||||
|
@ -23,31 +17,103 @@ function reversescan:access()
|
|||
return self:ret(true, "reverse scan not activated")
|
||||
end
|
||||
-- Loop on ports
|
||||
local threads = {}
|
||||
local ret_threads = nil
|
||||
local ret_err = nil
|
||||
for port in self.variables["REVERSE_SCAN_PORTS"]:gmatch("%S+") do
|
||||
-- Check if the scan is already cached
|
||||
local ok, cached = self:is_in_cache(ngx.ctx.bw.remote_addr .. ":" .. port)
|
||||
if not ok then
|
||||
return self:ret(false, "error getting cache from datastore : " .. cached)
|
||||
end
|
||||
if cached == "open" then
|
||||
return self:ret(true, "port " .. port .. " is opened for IP " .. ngx.ctx.bw.remote_addr,
|
||||
utils.get_deny_status())
|
||||
ret_threads = false
|
||||
ret_err = "error getting info from cachestore : " .. cached
|
||||
break
|
||||
-- Deny access if port opened
|
||||
elseif cached == "open" then
|
||||
ret_threads = true
|
||||
ret_err = "port " .. port .. " is opened for IP " .. ngx.ctx.bw.remote_addr
|
||||
break
|
||||
-- Perform scan in a thread
|
||||
elseif not cached then
|
||||
-- Do the scan
|
||||
local res = self:scan(ngx.ctx.bw.remote_addr, tonumber(port),
|
||||
tonumber(self.variables["REVERSE_SCAN_TIMEOUT"]))
|
||||
-- Cache the result
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr .. ":" .. port, res)
|
||||
if not ok then
|
||||
return self:ret(false, "error updating cache from datastore : " .. err)
|
||||
end
|
||||
-- Deny request if port is open
|
||||
if res == "open" then
|
||||
return self:ret(true, "port " .. port .. " is opened for IP " .. ngx.ctx.bw.remote_addr,
|
||||
utils.get_deny_status())
|
||||
end
|
||||
local thread = ngx.thread.spawn(self.scan, ngx.ctx.bw.remote_addr, tonumber(port), tonumber(self.variables["REVERSE_SCAN_TIMEOUT"]))
|
||||
threads[port] = thread
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for port, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- Check results of threads
|
||||
ret_threads = nil
|
||||
ret_err = nil
|
||||
local results = {}
|
||||
while true do
|
||||
-- Compute threads to wait
|
||||
local wait_threads = {}
|
||||
for port, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
-- No port opened
|
||||
if #wait_threads == 0 then
|
||||
break
|
||||
end
|
||||
-- Wait for first thread
|
||||
local ok, open, port = ngx.thread.wait(unpack(wait_threads))
|
||||
-- Error case
|
||||
if not ok then
|
||||
ret_threads = false
|
||||
ret_err = "error while waiting thread : " .. open
|
||||
break
|
||||
end
|
||||
port = tostring(port)
|
||||
-- Remove thread from list
|
||||
threads[port] = nil
|
||||
-- Add result to cache
|
||||
local result = "close"
|
||||
if open then
|
||||
result = "open"
|
||||
end
|
||||
results[port] = result
|
||||
-- Port is opened
|
||||
if open then
|
||||
ret_threads = true
|
||||
ret_err = "port " .. port .. " is opened for IP " .. ngx.ctx.bw.remote_addr
|
||||
break
|
||||
end
|
||||
end
|
||||
-- Kill running threads
|
||||
if #threads > 0 then
|
||||
local wait_threads = {}
|
||||
for port, thread in pairs(threads) do
|
||||
table.insert(wait_threads, thread)
|
||||
end
|
||||
utils.kill_all_threads(wait_threads)
|
||||
end
|
||||
-- Cache results
|
||||
for port, result in pairs(results) do
|
||||
local ok, err = self:add_to_cache(ngx.ctx.bw.remote_addr .. ":" .. port, result)
|
||||
if not ok then
|
||||
return self:ret(false, "error while adding element to cache : " .. err)
|
||||
end
|
||||
end
|
||||
if ret_threads ~= nil then
|
||||
-- Open port case
|
||||
if ret_threads then
|
||||
return self:ret(true, ret_err, utils.get_deny_status())
|
||||
end
|
||||
-- Error case
|
||||
return self:ret(false, ret_err)
|
||||
end
|
||||
-- No port opened
|
||||
return self:ret(true, "no port open for IP " .. ngx.ctx.bw.remote_addr)
|
||||
end
|
||||
|
@ -56,15 +122,15 @@ function reversescan:preread()
|
|||
return self:access()
|
||||
end
|
||||
|
||||
function reversescan:scan(ip, port, timeout)
|
||||
function reversescan.scan(ip, port, timeout)
|
||||
local tcpsock = ngx.socket.tcp()
|
||||
tcpsock:settimeout(timeout)
|
||||
local ok, err = tcpsock:connect(ip, port)
|
||||
tcpsock:close()
|
||||
if not ok then
|
||||
return "close"
|
||||
return false, port
|
||||
end
|
||||
return "open"
|
||||
return true, port
|
||||
end
|
||||
|
||||
function reversescan:is_in_cache(ip_port)
|
||||
|
|
|
@ -103,7 +103,7 @@ function sessions:init()
|
|||
send_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
read_timeout = tonumber(redis_vars["REDIS_TIMEOUT"]),
|
||||
keepalive_timeout = tonumber(redis_vars["REDIS_KEEPALIVE_IDLE"]),
|
||||
pool = "bw",
|
||||
pool = "bw-redis",
|
||||
pool_size = tonumber(redis_vars["REDIS_KEEPALIVE_POOL"]),
|
||||
ssl = redis_vars["REDIS_SSL"] == "yes",
|
||||
host = redis_vars["REDIS_HOST"],
|
||||
|
|
|
@ -12,12 +12,6 @@ local whitelist = class("whitelist", plugin)
|
|||
function whitelist:initialize()
|
||||
-- Call parent initialize
|
||||
plugin.initialize(self, "whitelist")
|
||||
-- Check if redis is enabled
|
||||
local use_redis, err = utils.get_variable("USE_REDIS", false)
|
||||
if not use_redis then
|
||||
self.logger:log(ngx.ERR, err)
|
||||
end
|
||||
self.use_redis = use_redis == "yes"
|
||||
-- Decode lists
|
||||
if ngx.get_phase() ~= "init" and self:is_needed() then
|
||||
local lists, err = self.datastore:get("plugin_whitelist_lists")
|
||||
|
@ -43,8 +37,6 @@ function whitelist:initialize()
|
|||
end
|
||||
end
|
||||
end
|
||||
-- Instantiate cachestore
|
||||
self.cachestore = cachestore:new(self.use_redis)
|
||||
end
|
||||
|
||||
function whitelist:is_needed()
|
||||
|
|
Loading…
Reference in New Issue