bw - init work on redis
This commit is contained in:
parent
8fa94d6a52
commit
902fe6ad07
|
@ -0,0 +1,56 @@
|
|||
local M = {}
|
||||
local redis = require "resty.redis"
|
||||
local utils = require "utils"
|
||||
|
||||
function M:connect()
|
||||
-- Instantiate object
|
||||
local redis_client, err = redis:new()
|
||||
if redis_client == nil then
|
||||
return false, err
|
||||
end
|
||||
-- Get variables
|
||||
local variables = {
|
||||
["REDIS_HOST"] = "",
|
||||
["REDIS_PORT"] = "",
|
||||
["REDIS_SSL"] = "",
|
||||
["REDIS_TIMEOUT"] = "",
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = ""
|
||||
}
|
||||
for k, v in pairs(variables) do
|
||||
local value, err = utils.get_variable(k)
|
||||
if value == nil then
|
||||
return false, err
|
||||
end
|
||||
variables[k] = value
|
||||
end
|
||||
-- Set timeouts
|
||||
redis_client:set_timeouts(tonumber(variables["REDIS_TIMEOUT"]), tonumber(variables["REDIS_TIMEOUT"]), tonumber(variables["REDIS_TIMEOUT"]))
|
||||
-- Connect
|
||||
local options = {
|
||||
["ssl"] = false
|
||||
}
|
||||
if variables["REDIS_SSL"] == "yes" then
|
||||
options["ssl"] = true
|
||||
end
|
||||
return redis.connect(variables["REDIS_HOST"], tonumber(variables["REDIS_PORT"]), options)
|
||||
end
|
||||
|
||||
function M:close(redis_client)
|
||||
-- Get variables
|
||||
local variables = {
|
||||
["REDIS_KEEPALIVE_IDLE"] = "",
|
||||
["REDIS_KEEPALIVE_POOL"] = ""
|
||||
}
|
||||
for k, v in pairs(variables) do
|
||||
local value, err = utils.get_variable(k)
|
||||
if value == nil then
|
||||
return false, err
|
||||
end
|
||||
variables[k] = value
|
||||
end
|
||||
-- Equivalent to close but keep a pool of connections
|
||||
return redis_client:set_keepalive(tonumber(variables["REDIS_KEEPALIVE_IDLE"]), tonumber(variables["REDIS_KEEPALIVE_POOL"]))
|
||||
end
|
||||
|
||||
return M
|
|
@ -19,6 +19,34 @@ if banned then
|
|||
logger.log(ngx.WARN, "ACCESS", "IP " .. ngx.var.remote_addr .. " is banned with reason : " .. banned)
|
||||
ngx.exit(utils.get_deny_status())
|
||||
end
|
||||
-- Redis case
|
||||
local use_redis = utils.get_variable("USE_REDIS")
|
||||
if use_redis == "yes" then
|
||||
-- Connect
|
||||
local redis_client, err = clusterstore:connect()
|
||||
if not redis_client then
|
||||
logger.log(ngx.ERR, "ACCESS", "can't connect to redis server : " .. err)
|
||||
else
|
||||
-- Get ban
|
||||
local ban, err = redis_client:get("ban_" .. ngx.var.remote_addr)
|
||||
if err then
|
||||
logger.log(ngx.ERR, "ACCESS", "GET failed : " .. err)
|
||||
elseif ban then
|
||||
-- Get TTL
|
||||
local ttl, err = redis_client:ttl("ban_" .. ngx.var.remote_addr)
|
||||
if not ttl then
|
||||
logger.log(ngx.ERR, "ACCESS", "TTL failed : " .. err)
|
||||
else
|
||||
local ok, err = datastore:set("bans_ip_" .. ip, ban, ttl)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "ACCESS", "can't save ban to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
redis_client:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- List all plugins
|
||||
local list, err = plugins:list()
|
||||
|
|
|
@ -1,74 +1,173 @@
|
|||
local _M = {}
|
||||
_M.__index = _M
|
||||
|
||||
local utils = require "utils"
|
||||
local datastore = require "datastore"
|
||||
local logger = require "logger"
|
||||
local cjson = require "cjson"
|
||||
local utils = require "utils"
|
||||
local datastore = require "datastore"
|
||||
local logger = require "logger"
|
||||
local cjson = require "cjson"
|
||||
local clusterstore = require "clusterstore"
|
||||
|
||||
function _M.new()
|
||||
local self = setmetatable({}, _M)
|
||||
return self, nil
|
||||
end
|
||||
|
||||
function _M.increase(premature, use_redis, ip, count_time, ban_time, threshold)
|
||||
-- Local case
|
||||
local counter, err = datastore:get("plugin_badbehavior_count_" .. ip)
|
||||
if not counter and err ~= "not found" then
|
||||
return false, "can't get counts from the datastore : " .. err
|
||||
end
|
||||
if counter == nil then
|
||||
counter = 0
|
||||
end
|
||||
counter = counter + 1
|
||||
-- Redis case
|
||||
if use_redis then
|
||||
-- Connect to server
|
||||
local redis_client, err = clusterstore:connect()
|
||||
if not redis_client then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) Can't connect to redis server : " .. err)
|
||||
return
|
||||
end
|
||||
-- Start transaction
|
||||
local ok, err = redis_client:multi()
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) Can't start transaction : " .. err)
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
-- Increment counter
|
||||
counter, err = redis_client:incr("bad_behavior_" .. ip)
|
||||
if not counter then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) INCR failed : " .. err)
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
-- Expires counter
|
||||
local expire, err = redis_client:expire("bad_behavior_" .. ip, count_time)
|
||||
if not counter then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) EXPIRE failed : " .. err)
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
-- Add IP to redis bans if needed
|
||||
if counter > threshold then
|
||||
local ban, err = redis_client:set("ban_" .. ip, "bad behavior", "EX", ban_time)
|
||||
if not ban then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) SET failed : " .. err)
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Exec transaction
|
||||
local exec, err = redis_client:exec()
|
||||
if not exec then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) EXEC failed : " .. err)
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
for i, v in ipairs(exec) do
|
||||
if v[1] == false then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) Transaction failed : " .. v[2])
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
end
|
||||
-- End connection
|
||||
redis_client:close()
|
||||
end
|
||||
-- Store local counter
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, counter)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) can't save counts to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
-- Store local ban
|
||||
if counter > threshold then
|
||||
local ok, err = datastore:set("bans_ip_" .. ip, "bad behavior", ban_time)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) can't save ban to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
logger.log(ngx.WARN, "BAD-BEHAVIOR", "IP " .. ip .. " is banned for " .. ban_time .. "s (" .. tostring(counter) .. "/" .. tostring(threshold) .. ")")
|
||||
end
|
||||
-- Call decrease later
|
||||
local ok, err = ngx.timer.at(count_time, _M.decrease, use_redis, ip)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(increase) can't create decrease timer : " .. err)
|
||||
end
|
||||
end
|
||||
|
||||
function _M.decrease(premature, use_redis, ip)
|
||||
-- Decrease from local store
|
||||
local count, err = datastore:get("plugin_badbehavior_count_" .. ip)
|
||||
if err then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease) Can't get counts from the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
if not count then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease) Count is null")
|
||||
return
|
||||
end
|
||||
local new_count = count - 1
|
||||
if new_count <= 0 then
|
||||
datastore:delete("plugin_badbehavior_count_" .. ip)
|
||||
return
|
||||
end
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, new_count)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease) Can't save counts to the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
-- Decrease from redis
|
||||
if use_redis then
|
||||
-- Connect to server
|
||||
local redis_client, err = clusterstore:connect()
|
||||
if not redis_client then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease) Can't connect to redis server : " .. err)
|
||||
return
|
||||
end
|
||||
-- Decrement counter
|
||||
local counter, err = redis_client:decr("bad_behavior_" .. ip)
|
||||
if not counter then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease) DECR failed : " .. err)
|
||||
redis_client:close()
|
||||
return
|
||||
end
|
||||
redis_client:close()
|
||||
end
|
||||
end
|
||||
|
||||
function _M:log()
|
||||
-- Get vars
|
||||
self.use = utils.get_variable("USE_BAD_BEHAVIOR")
|
||||
self.ban_time = utils.get_variable("BAD_BEHAVIOR_BAN_TIME")
|
||||
self.status_codes = utils.get_variable("BAD_BEHAVIOR_STATUS_CODES")
|
||||
self.threshold = utils.get_variable("BAD_BEHAVIOR_THRESHOLD")
|
||||
self.count_time = utils.get_variable("BAD_BEHAVIOR_COUNT_TIME")
|
||||
self.use_redis = utils.get_variable("USE_REDIS")
|
||||
-- Check if bad behavior is activated
|
||||
if self.use ~= "yes" then
|
||||
return true, "bad behavior not activated"
|
||||
end
|
||||
-- Check if we have a bad status code
|
||||
if not self.status_codes:match(tostring(ngx.status)) then
|
||||
return true, "not increasing counter"
|
||||
end
|
||||
-- Check if we are whitelisted
|
||||
if ngx.var.is_whitelisted == "yes" then
|
||||
return true, "client is whitelisted"
|
||||
end
|
||||
local count, err = datastore:get("plugin_badbehavior_count_" .. ngx.var.remote_addr)
|
||||
if not count and err ~= "not found" then
|
||||
return false, "can't get counts from the datastore : " .. err
|
||||
-- Call increase function later and with cosocket enabled
|
||||
local use_redis = false
|
||||
if self.use_redis == "yes" then
|
||||
use_redis = true
|
||||
end
|
||||
local new_count = 1
|
||||
if count ~= nil then
|
||||
new_count = count + 1
|
||||
end
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ngx.var.remote_addr, new_count)
|
||||
if not ok then
|
||||
return false, "can't save counts to the datastore : " .. err
|
||||
end
|
||||
local function decrease_callback(premature, ip)
|
||||
local count, err = datastore:get("plugin_badbehavior_count_" .. ip)
|
||||
if err then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease_callback) Can't get counts from the datastore : " .. err)
|
||||
return
|
||||
end
|
||||
if not count then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease_callback) Count is null")
|
||||
return
|
||||
end
|
||||
local new_count = count - 1
|
||||
if new_count <= 0 then
|
||||
datastore:delete("plugin_badbehavior_count_" .. ip)
|
||||
return
|
||||
end
|
||||
local ok, err = datastore:set("plugin_badbehavior_count_" .. ip, new_count)
|
||||
if not ok then
|
||||
logger.log(ngx.ERR, "BAD-BEHAVIOR", "(decrease_callback) Can't save counts to the datastore : " .. err)
|
||||
end
|
||||
end
|
||||
local hdr, err = ngx.timer.at(tonumber(self.count_time), decrease_callback, ngx.var.remote_addr)
|
||||
local ok, err = ngx.timer.at(0, _M.increase, use_redis, ngx.var.remote_addr, tonumber(self.count_time), tonumber(self.ban_time), tonumber(self.threshold))
|
||||
if not ok then
|
||||
return false, "can't create decrease timer : " .. err
|
||||
end
|
||||
if new_count > tonumber(self.threshold) then
|
||||
local ok, err = datastore:set("bans_ip_" .. ngx.var.remote_addr, "bad behavior", tonumber(self.ban_time))
|
||||
if not ok then
|
||||
return false, "can't save ban to the datastore : " .. err
|
||||
end
|
||||
logger.log(ngx.WARN, "BAD-BEHAVIOR", "IP " .. ngx.var.remote_addr .. " is banned for " .. tostring(self.ban_time) .. "s (" .. tostring(new_count) .. "/" .. tostring(self.threshold) .. ")")
|
||||
end
|
||||
return true, "success"
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"id": "redis",
|
||||
"order": 999,
|
||||
"name": "Redis",
|
||||
"description": "Redis server configuration when using BunkerWeb in cluster mode.",
|
||||
"version": "0.1",
|
||||
"settings": {
|
||||
"USE_REDIS": {
|
||||
"context": "global",
|
||||
"default": "no",
|
||||
"help": "Activate Redis.",
|
||||
"id": "use-redis",
|
||||
"label": "Activate Redis",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"REDIS_HOST": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "Redis server IP or hostname.",
|
||||
"id": "redis-host",
|
||||
"label": "Redis server",
|
||||
"regex": "^.*$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_PORT": {
|
||||
"context": "global",
|
||||
"default": "6379",
|
||||
"help": "Redis server port.",
|
||||
"id": "redis-port",
|
||||
"label": "Redis port",
|
||||
"regex": "^[0-9]+$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_SSL": {
|
||||
"context": "global",
|
||||
"default": "no",
|
||||
"help": "Use SSL/TLS connection with Redis server.",
|
||||
"id": "redis-ssl",
|
||||
"label": "Redis SSL/TLS",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
},
|
||||
"REDIS_TIMEOUT": {
|
||||
"context": "global",
|
||||
"default": "1000",
|
||||
"help": "Redis server timeout (in ms) for connect, read and write.",
|
||||
"id": "redis-timeout",
|
||||
"label": "Redis timeout (ms)",
|
||||
"regex": "^[0-9]+$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_KEEPALIVE_IDLE": {
|
||||
"context": "global",
|
||||
"default": "30000",
|
||||
"help": "Max idle time (in ms) before closing redis connection in the pool.",
|
||||
"id": "redis-keepalive-idle",
|
||||
"label": "Redis keepalive idle (ms)",
|
||||
"regex": "^[0-9]+$",
|
||||
"type": "text"
|
||||
},
|
||||
"REDIS_KEEPALIVE_POOL": {
|
||||
"context": "global",
|
||||
"default": "10",
|
||||
"help": "Max number of redis connection(s) kept in the pool.",
|
||||
"id": "redis-keepalive-pool",
|
||||
"label": "Redis keepalive pool",
|
||||
"regex": "^[0-9]+$",
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
local _M = {}
|
||||
_M.__index = _M
|
||||
|
||||
local utils = require "utils"
|
||||
local datastore = require "datastore"
|
||||
local logger = require "logger"
|
||||
local cjson = require "cjson"
|
||||
local resolver = require "resty.dns.resolver"
|
||||
|
||||
function _M.new()
|
||||
local self = setmetatable({}, _M)
|
||||
return self, nil
|
||||
end
|
||||
|
||||
function _M:init()
|
||||
-- Check if init is needed
|
||||
local init_needed, err = utils.get_variable("USE_REDIS", "yes")
|
||||
if init_needed == nil then
|
||||
return false, "can't check USE_REDIS variable : " .. err
|
||||
end
|
||||
if not init_needed then
|
||||
return true, "redis not used"
|
||||
end
|
||||
-- TODO : check redis connectivity
|
||||
return true, "redis ping successful"
|
||||
end
|
||||
|
||||
return _M
|
Loading…
Reference in New Issue