mirror of https://github.com/1Panel-dev/1Panel
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
8.0 KiB
262 lines
8.0 KiB
local utils = require "utils"
|
|
local config = require "config"
|
|
local redis_util = require "redis_util"
|
|
local action = require "action"
|
|
local uuid = require"resty.uuid"
|
|
|
|
local upper_str = string.upper
|
|
local tonumber = tonumber
|
|
local pairs = pairs
|
|
local type = type
|
|
local concat_table = table.concat
|
|
|
|
local function write_req_log(attack)
|
|
local wafdb = utils.get_wafdb(config.waf_log_db_path)
|
|
if not wafdb then
|
|
ngx.log(ngx.ERR, "get log db failed")
|
|
return
|
|
end
|
|
|
|
local real_ip = ngx.ctx.ip
|
|
local ip_location = ngx.ctx.ip_location
|
|
local country
|
|
local province
|
|
local longitude = 0.0
|
|
local latitude = 0.0
|
|
local iso = "CN"
|
|
if ip_location then
|
|
country = ip_location.country or {
|
|
["zh"] = "unknown",
|
|
["en"] = "unknown"
|
|
}
|
|
province = ip_location.province or {
|
|
["zh"] = "",
|
|
["en"] = ""
|
|
}
|
|
longitude = ip_location.longitude
|
|
latitude = ip_location.latitude
|
|
iso = ip_location.iso
|
|
end
|
|
|
|
local exec_rule = {}
|
|
local rule_action = ""
|
|
local exec_rule_type = ""
|
|
local match_rule_detail = ""
|
|
local match_rule_type = ""
|
|
local is_attack = 0
|
|
local is_block = 0
|
|
local blocking_time = 0
|
|
|
|
local method = ""
|
|
local uri = ""
|
|
local ua = ""
|
|
local host = ""
|
|
local protocol = ""
|
|
local website_key = ""
|
|
local logs_str = ""
|
|
|
|
if attack then
|
|
exec_rule = ngx.ctx.exec_rule
|
|
rule_action = exec_rule.action
|
|
exec_rule_type = exec_rule.type
|
|
is_attack = 1
|
|
method = ngx.req.get_method()
|
|
uri = ngx.var.request_uri
|
|
ua = ngx.ctx.ua
|
|
host = ngx.var.server_name
|
|
protocol = ngx.var.server_protocol or ""
|
|
website_key = ngx.ctx.website_key
|
|
|
|
if exec_rule.match_rule then
|
|
match_rule_detail = exec_rule.match_rule.rule
|
|
match_rule_type = exec_rule.match_rule.type
|
|
end
|
|
|
|
if ngx.ctx.ip_blocked then
|
|
is_block = 1
|
|
blocking_time = tonumber(exec_rule.ipBlockTime)
|
|
end
|
|
|
|
logs_str = method .. " " .. uri .. " "..protocol.."\n"
|
|
local headers = ngx.req.get_headers(20000)
|
|
for k, v in pairs(headers) do
|
|
local value = ""
|
|
if v then
|
|
if type(v) == "table" then
|
|
value = concat_table(v, ",")
|
|
else
|
|
value = v
|
|
end
|
|
end
|
|
logs_str = logs_str .. upper_str(k) .. ": " .. value .. "\n"
|
|
end
|
|
end
|
|
|
|
local log_id = uuid()
|
|
local insertQuery = [[
|
|
INSERT INTO req_logs (
|
|
id, ip, ip_iso, ip_country_zh, ip_country_en,
|
|
ip_province_zh, ip_province_en, ip_longitude, ip_latitude,
|
|
localtime, server_name, website_key, host, method,
|
|
uri, user_agent, exec_rule, rule_type, match_rule, match_value,
|
|
nginx_log, blocking_time, action, is_block,is_attack
|
|
) VALUES (
|
|
:id, :real_ip, :iso, :country_zh, :country_en,
|
|
:province_zh, :province_en,:longitude, :latitude,
|
|
DATETIME('now'), :server_name,:host, :website_key, :method,
|
|
:uri, :ua, :exec_rule, :rule_type, :match_rule, :match_value,
|
|
:logs_str, :blocking_time, :action, :is_block, :is_attack
|
|
)
|
|
]]
|
|
|
|
wafdb:execute([[BEGIN TRANSACTION]])
|
|
|
|
local stmt = wafdb:prepare(insertQuery)
|
|
stmt:bind_names {
|
|
id = log_id,
|
|
iso = iso,
|
|
real_ip = real_ip,
|
|
country_zh = country["zh"],
|
|
country_en = country["en"],
|
|
province_zh = province["zh"],
|
|
province_en = province["en"],
|
|
longitude = longitude,
|
|
latitude = latitude,
|
|
host = host,
|
|
server_name = host,
|
|
website_key = website_key,
|
|
method = method,
|
|
uri = uri,
|
|
ua = ua,
|
|
exec_rule = exec_rule_type,
|
|
rule_type = match_rule_type,
|
|
match_rule = match_rule_detail,
|
|
match_value = "",
|
|
logs_str = logs_str,
|
|
blocking_time = blocking_time or 0,
|
|
action = rule_action,
|
|
is_block = is_block,
|
|
is_attack = is_attack
|
|
}
|
|
local code = stmt:step()
|
|
stmt:finalize()
|
|
|
|
local code2 = 101
|
|
if ngx.ctx.ip_blocked then
|
|
local insertBlockIp = [[
|
|
INSERT INTO block_ips (ip, is_block, blocking_time, req_log_id,create_date)
|
|
VALUES (:ip, :is_block, :blocking_time, :req_log_id, DATETIME('now'))
|
|
]]
|
|
stmt = wafdb:prepare(insertBlockIp)
|
|
stmt:bind_names {
|
|
ip=real_ip,
|
|
is_block = is_block,
|
|
blocking_time = blocking_time or 0,
|
|
req_log_id = log_id
|
|
}
|
|
code2 = stmt:step()
|
|
stmt:finalize()
|
|
end
|
|
|
|
wafdb:execute([[COMMIT]])
|
|
|
|
if code ~= 101 or code2 ~= 101 then
|
|
local error_msg = wafdb:errmsg()
|
|
if error_msg then
|
|
ngx.log(ngx.ERR, "insert attack_log error ", error_msg .. " ")
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
local function count_not_found()
|
|
if ngx.status ~= 404 then
|
|
return
|
|
end
|
|
if config.is_global_state_on("notFoundCount") then
|
|
local ip = ngx.ctx.ip
|
|
local not_found_config = config.get_global_config("notFoundCount")
|
|
local key = ip
|
|
|
|
if config.is_redis_on() then
|
|
key = "cc_attack_count:" .. key
|
|
local count, _ = redis_util.incr(key, not_found_config.duration)
|
|
if not count then
|
|
redis_util.set(key, 1, not_found_config.duration)
|
|
elseif count >= not_found_config.threshold then
|
|
action.block_ip(ip, not_found_config)
|
|
return
|
|
end
|
|
else
|
|
key = ip .. "not_found"
|
|
local limit = ngx.shared.waf_limit
|
|
local count, _ = limit:incr(key, 1, 0, not_found_config.duration)
|
|
if not count then
|
|
limit:set(key, 1, not_found_config.duration)
|
|
elseif count >= not_found_config.threshold then
|
|
action.block_ip(ip, not_found_config)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function count_req_status(is_attack)
|
|
local wafdb = utils.get_wafdb(config.waf_db_path)
|
|
if not wafdb then
|
|
ngx.log(ngx.ERR, "get log db failed")
|
|
return
|
|
end
|
|
|
|
local today = ngx.today()
|
|
local status = ngx.status
|
|
|
|
local stmt_exist = wafdb:prepare("SELECT COUNT(*) FROM waf_stat WHERE day = ?")
|
|
stmt_exist:bind_values(today)
|
|
stmt_exist:step()
|
|
local count = stmt_exist:get_uvalues()
|
|
stmt_exist:finalize()
|
|
|
|
local req_count_update = 1
|
|
local count_4xx_update = (status >= 400 and status < 500) and 1 or 0
|
|
local count_5xx_update = (status >= 500) and 1 or 0
|
|
local attack_count_update = is_attack and 1 or 0
|
|
local code = 0
|
|
|
|
if count > 0 then
|
|
local stmt = wafdb:prepare("UPDATE waf_stat SET req_count = req_count + ?, count4xx = count4xx + ?, count5xx = count5xx + ?, attack_count = attack_count + ? WHERE day = ?")
|
|
stmt:bind_values(req_count_update, count_4xx_update, count_5xx_update, attack_count_update, today)
|
|
code = stmt:step()
|
|
stmt:finalize()
|
|
else
|
|
local stmt = wafdb:prepare("INSERT INTO waf_stat (day, req_count, count4xx, count5xx, attack_count,create_date) VALUES (?, ?, ?, ?, ?,DATETIME('now'))")
|
|
stmt:bind_values(today, req_count_update, count_4xx_update, count_5xx_update, attack_count_update)
|
|
code = stmt:step()
|
|
stmt:finalize()
|
|
end
|
|
|
|
if code ~= 101 then
|
|
local error_msg = wafdb:errmsg()
|
|
if error_msg then
|
|
ngx.log(ngx.ERR, "update waf_stat error ", error_msg .. " ")
|
|
end
|
|
end
|
|
end
|
|
|
|
if config.is_waf_on() then
|
|
count_not_found()
|
|
local is_attack = ngx.ctx.is_attack
|
|
|
|
if not ngx.ctx.ip then
|
|
ngx.ctx.ip = utils.get_real_ip()
|
|
ngx.ctx.ip_location = utils.get_ip_location(ngx.ctx.ip)
|
|
local ua = utils.get_header("user-agent")
|
|
if not ua then
|
|
ua = ""
|
|
end
|
|
end
|
|
|
|
count_req_status(is_attack)
|
|
write_req_log(is_attack)
|
|
end
|