Sindbad~EG File Manager

Current Path : /opt/nginxhttpd_/etc/openresty_config/lua/lib/
Upload File :
Current File : //opt/nginxhttpd_/etc/openresty_config/lua/lib/o2switch_ban.lua

--[[
    This lib is used to temporary ban IPs addresses that attack us.
    The list is automaticaly generated from the autoMitigation system, when an IP exceed the threshold.
    The ban is temporary, the connection is also drop early on the SSL handshake to avoid using too much CPU / SSL DDOS
--]]

local o2config = require "lib/o2switch_config"
local o2debug = require "lib/o2switch_debug"
local ipUtils = require "lib/o2switch_ip_utils"
local mlcache = require("resty.mlcache")
local timer = require("resty.timer")
local banDict = ngx.shared.ip_blacklist_dict
local ngx = ngx
local cache, err = nil, nil
local tostring = tostring 
local type = type
local ipairs = ipairs
local sub = string.sub
local table_insert = table.insert
local useCache = false
local defaultMlcacheOpts = { ttl = o2config.tmpBanTime, neg_ttl = 60, }
local _M = {}

-- This module will have his own cache and not use the o2switch_cache_wrapper because it's on a separate dict.
if o2config.useInternalCache and not cache then
    --o2debug.debug('Create cache called : ' .. tostring(o2config.tmpBanTime) .. ',' .. tostring(60))
    -- @See https://github.com/thibaultcha/lua-resty-mlcache
    cache, err = mlcache.new("ip_bl", "ip_blacklist_dict", {
        lru_size = 50000, -- size of the L1 (Lua VM) cache (nb of items)
        ttl      = o2config.tmpBanTime,
        neg_ttl  = 60, 
        ipc_shm = 'sync_dict' -- Name of the lua_shared_dict used by the workers sync, to purge/empty L1 cache
    })
    if not cache then
        o2debug.debugErr("Cache : Could not create mlcache." .. (err or ''))
    else
        useCache = true 
    end
end

-- Add an IP address to the blacklist
-- @binaryIp The IP address to blacklist, in the binary form (ngx.var.binary_remote_addr)
-- @whitelistMapValue The value of the $map_underattack_wl. We need to check that here because we deny on the SSL level, we cant rely on the customer_security/*.conf file, it's too early.
-- @return bool, err|nil
function _M.addIpToBlacklist(binaryIp, whitelistMapValue)
    if whitelistMapValue == "1" or whitelistMapValue == 1 then
        return false, "IP is whitelisted"
    end

    local ok, err = nil, nil
    if useCache and cache then
        ok, err = cache:set(binaryIp, defaultMlcacheOpts, binaryIp)
    else
        ok, err = banDict:set(binaryIp, binaryIp, o2config.tmpBanTime)
    end
    return ok, err
end

-- Remove an IP address from the blacklist
-- @binaryIp The IP address, in the binary form (ngx.var.binary_remote_addr)
-- @return bool, err|nil
function _M.removeFromBlacklist(binaryIp)
    local ok, err = nil, nil
    if useCache and cache then
        ok, err = cache:delete(binaryIp)
    else
        ok, err = banDict:delete(binaryIp)
    end
    return ok, err
end

-- Check if an IP address is blacklisted. Return true if so
-- @binaryIp The IP address in the binary form (ngx.var.binary_remote_addr)
-- @return bool
function _M.isBlacklisted(binaryIp)    
    local ok, err = nil, nil
    if useCache and cache then
        ok, err = cache:get(binaryIp)
    else
        ok, err = banDict:get(binaryIp)
    end

    if ok == nil or not ok then
        return false
    end

    return true
end

-- Export the list of the currently banned IP addresses
-- @return table, err|nil
function _M.exportBlacklistedIp()
    local keys = banDict:get_keys(0)
    local result = {}
    for _, k in ipairs(keys) do
        local value = nil
        if useCache then
            k = sub(k, 6) -- Remove the prefix of the mlcache to retrieve the original key
            value = cache:get(k)
        else
            value = banDict:get(k)
        end

        if type(value) == 'string' then
            value = ipUtils.binaryToStringIp(value)
            if type(value) == 'string' then
                table_insert(result, value)
            end
        end
    end

    return result, nil
end

--- Check if the cache is active, if so, execute the cache invalidation on the Nginx Worker. It will remove the stale
-- elements from the L1 cache. It can (should) be done once per requests.
-- @return {bool success, string|nil errMsg}
function _M.executeInvalidation()
    --o2debug.debug("o2switch_ban.executeInvalidation: Called")

    if o2config.useInternalCache == false then
        return true, nil -- Do we return an error or not ? Not sure.
    end

    if not cache then
        --o2debug.debug("o2switch_ban.executeInvalidation: Can't executeInvalidation, the cache object is null.")
        return false, "Can't executeInvalidation, the cache object is null."
    end

    local ok, err = cache:update(1);
    if not ok then
        --o2debug.debug("o2switch_ban.executeInvalidation:" .. (err or "no err msg"));
        return false, (err or "no err msg")
    end
    --o2debug.debug("o2switch_ban.executeInvalidation: Looks fine")
    return true, nil
end


-- Register one timer per nginx worker that will execute the cache invalidation (if the case is activated)
function _M.registerTimers() 
    --o2debug.debug('o2switch_ban.registerTimers() called')

    if o2config.useInternalCache == false then
        return nil
    end

    timer({
        interval = 89,  
        recurring = true, 
        immediate = false,
        detached = true, 
        jitter = 0.1,
        expire = function() 
            local ban = require "lib/o2switch_ban"
            ban.executeInvalidation()
        end
    })

    return nil
end

return _M

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists