246 lines
7.5 KiB
Lua
246 lines
7.5 KiB
Lua
local _M = {}
|
|
_M.__index = _M
|
|
|
|
local utils = require "utils"
|
|
local datastore = require "datastore"
|
|
local logger = require "logger"
|
|
local cjson = require "cjson"
|
|
local ipmatcher = require "resty.ipmatcher"
|
|
|
|
function _M.new()
|
|
local self = setmetatable({}, _M)
|
|
return self, nil
|
|
end
|
|
|
|
function _M:init()
|
|
-- Check if init is needed
|
|
local init_needed, err = utils.has_variable("USE_BLACKLIST", "yes")
|
|
if init_needed == nil then
|
|
return false, err
|
|
end
|
|
if not init_needed then
|
|
return true, "no service uses Blacklist, skipping init"
|
|
end
|
|
-- Read blacklists
|
|
local blacklists = {
|
|
["IP"] = {},
|
|
["RDNS"] = {},
|
|
["ASN"] = {},
|
|
["USER_AGENT"] = {},
|
|
["URI"] = {}
|
|
}
|
|
local i = 0
|
|
for kind, _ in pairs(blacklists) do
|
|
local f, err = io.open("/opt/bunkerweb/cache/blacklist/" .. kind .. ".list", "r")
|
|
if f then
|
|
for line in f:lines() do
|
|
table.insert(blacklists[kind], line)
|
|
i = i + 1
|
|
end
|
|
f:close()
|
|
end
|
|
end
|
|
-- Load them into datastore
|
|
local ok, err = datastore:set("plugin_blacklist_list", cjson.encode(blacklists))
|
|
if not ok then
|
|
return false, "can't store Blacklist list into datastore : " .. err
|
|
end
|
|
return true, "successfully loaded " .. tostring(i) .. " bad IP/network/rDNS/ASN/User-Agent/URI"
|
|
end
|
|
|
|
function _M:access()
|
|
-- Check if access is needed
|
|
local access_needed, err = utils.get_variable("USE_BLACKLIST")
|
|
if access_needed == nil then
|
|
return false, err
|
|
end
|
|
if access_needed ~= "yes" then
|
|
return true, "Blacklist not activated"
|
|
end
|
|
|
|
-- Check the cache
|
|
local cached_ip, err = self:is_in_cache("ip" .. ngx.var.remote_addr)
|
|
if cached_ip and cached_ip ~= "ok" then
|
|
return true, "IP is in blacklist cache (info = " .. cached_ip .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
local cached_uri, err = self:is_in_cache("uri" .. ngx.var.uri)
|
|
if cached_uri and cached_uri ~= "ok" then
|
|
return true, "URI is in blacklist cache (info = " .. cached_uri .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
local cached_ua = true
|
|
if ngx.var.http_user_agent then
|
|
cached_ua, err = self:is_in_cache("ua" .. ngx.var.http_user_agent)
|
|
if cached_ua and cached_ua ~= "ok" then
|
|
return true, "User-Agent is in blacklist cache (info = " .. cached_ua .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
end
|
|
if cached_ip and cached_uri and cached_ua then
|
|
return true, "full request is in blacklist cache (not blacklisted)", nil, nil
|
|
end
|
|
|
|
-- Get list
|
|
local data, err = datastore:get("plugin_blacklist_list")
|
|
if not data then
|
|
return false, "can't get Blacklist list : " .. err, false, nil
|
|
end
|
|
local ok, blacklists = pcall(cjson.decode, data)
|
|
if not ok then
|
|
return false, "error while decoding blacklists : " .. blacklists, false, nil
|
|
end
|
|
|
|
-- Return value
|
|
local ret, ret_err = true, "success"
|
|
|
|
-- Check if IP is in IP/net blacklist
|
|
local ip_net, err = utils.get_variable("BLACKLIST_IP")
|
|
if ip_net and ip_net ~= "" then
|
|
for element in ip_net:gmatch("%S+") do
|
|
table.insert(blacklists["IP"], element)
|
|
end
|
|
end
|
|
if not cached_ip then
|
|
local ipm, err = ipmatcher.new(blacklists["IP"])
|
|
if not ipm then
|
|
ret = false
|
|
ret_err = "can't instantiate ipmatcher " .. err
|
|
else
|
|
if ipm:match(ngx.var.remote_addr) then
|
|
self:add_to_cache("ip" .. ngx.var.remote_addr, "ip/net")
|
|
return ret, "client IP " .. ngx.var.remote_addr .. " is in blacklist", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check if rDNS is in blacklist
|
|
local rdns_global, err = utils.get_variable("BLACKLIST_RDNS_GLOBAL")
|
|
local check = true
|
|
if not rdns_global then
|
|
logger.log(ngx.ERR, "BLACKLIST", "Error while getting BLACKLIST_RDNS_GLOBAL variable : " .. err)
|
|
elseif rdns_global == "yes" then
|
|
check, err = utils.ip_is_global(ngx.var.remote_addr)
|
|
if check == nil then
|
|
logger.log(ngx.ERR, "BLACKLIST", "Error while getting checking if IP is global : " .. err)
|
|
end
|
|
end
|
|
if not cached_ip and check then
|
|
local rdns, err = utils.get_rdns(ngx.var.remote_addr)
|
|
if not rdns then
|
|
ret = false
|
|
ret_err = "error while trying to get reverse dns : " .. err
|
|
else
|
|
local rdns_list, err = utils.get_variable("BLACKLIST_RDNS")
|
|
if rdns_list and rdns_list ~= "" then
|
|
for element in rdns_list:gmatch("%S+") do
|
|
table.insert(blacklists["RDNS"], element)
|
|
end
|
|
end
|
|
for i, suffix in ipairs(blacklists["RDNS"]) do
|
|
if rdns:sub(-#suffix) == suffix then
|
|
self:add_to_cache("ip" .. ngx.var.remote_addr, "rDNS " .. suffix)
|
|
return ret, "client IP " .. ngx.var.remote_addr .. " is in blacklist (info = rDNS " .. suffix .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check if ASN is in blacklist
|
|
if not cached_ip then
|
|
if utils.ip_is_global(ngx.var.remote_addr) then
|
|
local asn, err = utils.get_asn(ngx.var.remote_addr)
|
|
if not asn then
|
|
ret = false
|
|
ret_err = "error while trying to get asn number : " .. err
|
|
else
|
|
local asn_list, err = utils.get_variable("BLACKLIST_ASN")
|
|
if asn_list and asn_list ~= "" then
|
|
for element in asn_list:gmatch("%S+") do
|
|
table.insert(blacklists["ASN"], element)
|
|
end
|
|
end
|
|
for i, asn_bl in ipairs(blacklists["ASN"]) do
|
|
if tostring(asn) == asn_bl then
|
|
self:add_to_cache("ip" .. ngx.var.remote_addr, "ASN " .. tostring(asn))
|
|
return ret, "client IP " .. ngx.var.remote_addr .. " is in blacklist (kind = ASN " .. tostring(asn) .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- IP is not blacklisted
|
|
local ok, err = self:add_to_cache("ip" .. ngx.var.remote_addr, "ok")
|
|
if not ok then
|
|
ret = false
|
|
ret_err = err
|
|
end
|
|
|
|
-- Check if User-Agent is in blacklist
|
|
if not cached_ua and ngx.var.http_user_agent then
|
|
local ua_list, err = utils.get_variable("BLACKLIST_USER_AGENT")
|
|
if ua_list and ua_list ~= "" then
|
|
for element in ua_list:gmatch("%S+") do
|
|
table.insert(blacklists["USER_AGENT"], element)
|
|
end
|
|
end
|
|
for i, ua_bl in ipairs(blacklists["USER_AGENT"]) do
|
|
if ngx.var.http_user_agent:match(ua_bl) then
|
|
self:add_to_cache("ua" .. ngx.var.http_user_agent, "UA " .. ua_bl)
|
|
return ret, "client User-Agent " .. ngx.var.http_user_agent .. " is in blacklist (matched " .. ua_bl .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
end
|
|
-- UA is not blacklisted
|
|
local ok, err = self:add_to_cache("ua" .. ngx.var.http_user_agent, "ok")
|
|
if not ok then
|
|
ret = false
|
|
ret_err = err
|
|
end
|
|
end
|
|
|
|
-- Check if URI is in blacklist
|
|
if not cached_uri then
|
|
local uri_list, err = utils.get_variable("BLACKLIST_URI")
|
|
if uri_list and uri_list ~= "" then
|
|
for element in uri_list:gmatch("%S+") do
|
|
table.insert(blacklists["URI"], element)
|
|
end
|
|
end
|
|
for i, uri_bl in ipairs(blacklists["URI"]) do
|
|
if ngx.var.uri:match(uri_bl) then
|
|
self:add_to_cache("uri" .. ngx.var.uri, "URI " .. uri_bl)
|
|
return ret, "client URI " .. ngx.var.uri .. " is in blacklist (matched " .. uri_bl .. ")", true, ngx.HTTP_FORBIDDEN
|
|
end
|
|
end
|
|
end
|
|
|
|
-- URI is not blacklisted
|
|
local ok, err = self:add_to_cache("uri" .. ngx.var.uri, "ok")
|
|
if not ok then
|
|
ret = false
|
|
ret_err = err
|
|
end
|
|
|
|
return ret, "IP is not in list (error = " .. ret_err .. ")", false, nil
|
|
end
|
|
|
|
function _M:is_in_cache(ele)
|
|
local kind, err = datastore:get("plugin_blacklist_cache_" .. ngx.var.server_name .. ele)
|
|
if not kind then
|
|
if err ~= "not found" then
|
|
logger.log(ngx.ERR, "BLACKLIST", "Error while accessing cache : " .. err)
|
|
end
|
|
return false, err
|
|
end
|
|
return kind, "success"
|
|
end
|
|
|
|
function _M:add_to_cache(ele, kind)
|
|
local ok, err = datastore:set("plugin_blacklist_cache_" .. ngx.var.server_name .. ele, kind, 3600)
|
|
if not ok then
|
|
logger.log(ngx.ERR, "BLACKLIST", "Error while adding element to cache : " .. err)
|
|
return false, err
|
|
end
|
|
return true, "success"
|
|
end
|
|
|
|
return _M
|