1Panel/plugins/openresty/waf/lib/action.lua

179 lines
4.9 KiB
Lua

local config = require "config"
local redis_util = require "redis_util"
local format_str = string.format
local _M = {}
local function deny(status_code, res)
ngx.status = status_code
if res then
ngx.header.content_type = "text/html; charset=UTF-8"
ngx.say(config.get_html_res(res))
end
ngx.exit(ngx.status)
end
local function redirect(status_code)
ngx.header.content_type = "text/html; charset=UTF-8"
ngx.say(config.get_html_res("redirect"))
ngx.status = status_code
ngx.exit(ngx.status)
end
local function slide()
ngx.header.content_type = "text/html; charset=UTF-8"
ngx.header.Cache_Control = "no-cache"
ngx.status = 200
ngx.say(format_str(config.get_html_res("slide"), ngx.md5(ngx.ctx.ip)))
ngx.exit(ngx.status)
end
local function five_second()
ngx.header.content_type = "text/html; charset=UTF-8"
ngx.header.Cache_Control = "no-cache"
ngx.status = 200
ngx.say(format_str(config.get_html_res("five_second"), ngx.md5(ngx.ctx.ip)))
ngx.exit(ngx.status)
end
function _M.block_ip(ip, rule)
local ok, err = nil, nil
local msg = "拉黑IP : " .. ip .. "国家 " .. ngx.ctx.geoip.country["zh"]
if rule then
msg = msg .. " 规则 " .. rule.type
end
ngx.log(ngx.ERR, msg)
if config.redis_on then
local red, err1 = redis_util.get_conn()
if not red then
return nil, err1
end
local key = "black_ip:" .. ip
local exists = red:exists(key)
if exists == 0 then
ok, err = red:set(key, 1)
if ok then
ngx.ctx.ipBlocked = true
else
ngx.log(ngx.ERR, "failed to set redis key " .. key, err)
end
end
if rule.ipBlockTime > 0 then
ok, err = red:expire(key, rule.ipBlockTime)
if not ok then
ngx.log(ngx.ERR, "failed to expire redis key " .. key, err)
end
end
redis_util.close_conn(red)
else
local wafBlackIp = ngx.shared.waf_black_ip
local exists = wafBlackIp:get(ip)
if not exists then
ok, err = wafBlackIp:set(ip, 1, rule.ipBlockTime)
if ok then
ngx.ctx.ipBlocked = true
else
ngx.log(ngx.ERR, "failed to set key " .. ip, err)
end
elseif rule.ipBlockTime > 0 then
ok, err = wafBlackIp:expire(ip, rule.ipBlockTime)
if ok then
ngx.ctx.ipBlocked = true
else
ngx.log(ngx.ERR, "failed to expire key " .. ip, err)
end
end
end
return ok
end
local function attack_count(config_type)
if config_type == "ipBlack" then
return
end
if config.is_global_state_on("attackCount") then
local ip = ngx.ctx.ip
local attack_config = config.get_global_config("attackCount")
local key = ip
if config.is_redis_on() then
key = "cc_attack_count:" .. key
local count, _ = redis_util.incr(key, attack_config.duration)
if not count then
redis_util.set(key, 1, attack_config.duration)
elseif count >= attack_config.threshold then
_M.block_ip(ip, attack_config)
return
end
else
key = ip .. "attack"
local limit = ngx.shared.waf_limit
local count, _ = limit:incr(key, 1, 0, attack_config.duration)
if not count then
limit:set(key, 1, attack_config.duration)
elseif count >= attack_config.threshold then
_M.block_ip(ip, attack_config)
return
end
end
end
end
function _M.exec_action(rule_config, match_rule, data)
local action = rule_config.action
if match_rule then
rule_config.rule = match_rule.rule
else
rule_config.rule = "默认"
end
ngx.ctx.rule_table = rule_config
ngx.ctx.action = action
ngx.ctx.hitData = data
ngx.ctx.is_attack = true
if rule_config.ipBlock and rule_config.ipBlock == 'on' then
_M.block_ip(ngx.ctx.ip, rule_config)
end
if rule_config.type == nil then
rule_config.type = "默认"
end
attack_count(rule_config.type)
local msg = "访问 IP " .. ngx.ctx.ip .. " 访问 URL" .. ngx.var.uri .. " 触发动作 " .. action .. " User-Agent " .. ngx.ctx.ua .. " 规则类型 " .. rule_config.type .. " 规则 " .. rule_config.rule
ngx.log(ngx.ERR, msg)
if action == "allow" then
return
elseif action == "deny" then
if rule_config.code and rule_config.code ~= 444 then
deny(rule_config.code, rule_config.res)
else
deny(444)
end
elseif action == "slide" then
slide()
elseif action == "fives" then
five_second()
else
redirect(444)
end
end
return _M