mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 22:18:07 +08:00
feat(waf): 优化请求次数记录方式 (#4210)
This commit is contained in:
parent
7784ee9575
commit
cbfedb5d5e
@ -2167,7 +2167,7 @@ const message = {
|
|||||||
count4xx: '4xx quantity',
|
count4xx: '4xx quantity',
|
||||||
count5xx: '5xx quantity',
|
count5xx: '5xx quantity',
|
||||||
todayStatus: 'Today Status',
|
todayStatus: 'Today Status',
|
||||||
reqMap: 'Request map (30 days)',
|
reqMap: 'Attack map (30 days)',
|
||||||
resource: 'source',
|
resource: 'source',
|
||||||
count: 'Quantity',
|
count: 'Quantity',
|
||||||
hight: 'high',
|
hight: 'high',
|
||||||
|
@ -2024,7 +2024,7 @@ const message = {
|
|||||||
count4xx: '4xx 數量',
|
count4xx: '4xx 數量',
|
||||||
count5xx: '5xx 數量',
|
count5xx: '5xx 數量',
|
||||||
todayStatus: '今日狀態',
|
todayStatus: '今日狀態',
|
||||||
reqMap: '請求地圖(30日)',
|
reqMap: '攔截地圖(30日)',
|
||||||
resource: '來源',
|
resource: '來源',
|
||||||
count: '數量',
|
count: '數量',
|
||||||
hight: '高',
|
hight: '高',
|
||||||
|
@ -2025,7 +2025,7 @@ const message = {
|
|||||||
count4xx: '4xx 数量',
|
count4xx: '4xx 数量',
|
||||||
count5xx: '5xx 数量',
|
count5xx: '5xx 数量',
|
||||||
todayStatus: '今日状态',
|
todayStatus: '今日状态',
|
||||||
reqMap: '请求地图(30日)',
|
reqMap: '拦截地图(30日)',
|
||||||
resource: '来源',
|
resource: '来源',
|
||||||
count: '数量',
|
count: '数量',
|
||||||
hight: '高',
|
hight: '高',
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
{
|
{
|
||||||
"waf": "on",
|
"waf": {
|
||||||
|
"state": "on",
|
||||||
"mode": "protection",
|
"mode": "protection",
|
||||||
"secret": "qwer1234",
|
"secret": "qwer1234"
|
||||||
|
},
|
||||||
"redis": {
|
"redis": {
|
||||||
"state": "off",
|
"state": "off",
|
||||||
"host": "127.0.0.1",
|
"host": "127.0.0.1",
|
||||||
@ -11,7 +13,9 @@
|
|||||||
"poolSize": 10
|
"poolSize": 10
|
||||||
},
|
},
|
||||||
"ipWhite": {
|
"ipWhite": {
|
||||||
"state": "on"
|
"state": "on",
|
||||||
|
"type": "ipWhite",
|
||||||
|
"action": "allow"
|
||||||
},
|
},
|
||||||
"ipBlack": {
|
"ipBlack": {
|
||||||
"state": "on",
|
"state": "on",
|
||||||
@ -21,7 +25,9 @@
|
|||||||
"res": "ip"
|
"res": "ip"
|
||||||
},
|
},
|
||||||
"urlWhite": {
|
"urlWhite": {
|
||||||
"state": "on"
|
"type": "urlWhite",
|
||||||
|
"state": "on",
|
||||||
|
"action": "allow"
|
||||||
},
|
},
|
||||||
"urlBlack": {
|
"urlBlack": {
|
||||||
"type": "urlBlack",
|
"type": "urlBlack",
|
||||||
@ -30,7 +36,9 @@
|
|||||||
"action": "deny"
|
"action": "deny"
|
||||||
},
|
},
|
||||||
"uaWhite": {
|
"uaWhite": {
|
||||||
"state": "off"
|
"type": "uaWhite",
|
||||||
|
"state": "off",
|
||||||
|
"action": "allow"
|
||||||
},
|
},
|
||||||
"uaBlack": {
|
"uaBlack": {
|
||||||
"type": "uaBlack",
|
"type": "uaBlack",
|
||||||
@ -64,7 +72,9 @@
|
|||||||
"geoRestrict": {
|
"geoRestrict": {
|
||||||
"state": "on",
|
"state": "on",
|
||||||
"rules": [],
|
"rules": [],
|
||||||
"action": "deny"
|
"code": 444,
|
||||||
|
"action": "deny",
|
||||||
|
"type": "geoRestrict"
|
||||||
},
|
},
|
||||||
"defaultIpBlack": {
|
"defaultIpBlack": {
|
||||||
"state": "on",
|
"state": "on",
|
||||||
@ -87,7 +97,6 @@
|
|||||||
"cc": {
|
"cc": {
|
||||||
"state": "off",
|
"state": "off",
|
||||||
"type": "cc",
|
"type": "cc",
|
||||||
"rule": "cc",
|
|
||||||
"tokenTimeOut": 1800,
|
"tokenTimeOut": 1800,
|
||||||
"threshold": 120,
|
"threshold": 120,
|
||||||
"duration": 60,
|
"duration": 60,
|
||||||
@ -98,7 +107,6 @@
|
|||||||
"ccurl": {
|
"ccurl": {
|
||||||
"state": "off",
|
"state": "off",
|
||||||
"type": "urlcc",
|
"type": "urlcc",
|
||||||
"rule": "urlcc",
|
|
||||||
"action": "deny",
|
"action": "deny",
|
||||||
"ipBlock": "on",
|
"ipBlock": "on",
|
||||||
"ipBlockTime": 600
|
"ipBlockTime": 600
|
||||||
@ -112,18 +120,11 @@
|
|||||||
"ipBlock": "on",
|
"ipBlock": "on",
|
||||||
"ipBlockTime": 600
|
"ipBlockTime": 600
|
||||||
},
|
},
|
||||||
"fileExtCheck": {
|
"fileExt": {
|
||||||
"state": "on",
|
"state": "on",
|
||||||
"action": "deny",
|
"action": "deny",
|
||||||
"code": 403,
|
"code": 403,
|
||||||
"type": "fileExtCheck",
|
"type": "fileExtCheck"
|
||||||
"rules": [
|
|
||||||
"php",
|
|
||||||
"jsp",
|
|
||||||
"asp",
|
|
||||||
"exe",
|
|
||||||
"sh"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"cookie": {
|
"cookie": {
|
||||||
"type": "cookie",
|
"type": "cookie",
|
||||||
|
@ -145,15 +145,15 @@ function _M.get_html_res(name)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function _M.is_waf_on()
|
function _M.is_waf_on()
|
||||||
return config.global_config["waf"] == "on" and true or false
|
return _M.is_global_state_on("waf")
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.is_redis_on()
|
function _M.is_redis_on()
|
||||||
return config.global_config["redis"] == "on" and true or false
|
return _M.is_global_state_on("redis")
|
||||||
end
|
end
|
||||||
|
|
||||||
function _M.get_secret()
|
function _M.get_secret()
|
||||||
return config.global_config["secret"]
|
return config.global_config["waf"]["secret"]
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
@ -35,13 +35,12 @@ local function init_db_config(db_path)
|
|||||||
if not ok then
|
if not ok then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local wafdb
|
local wafdb = sqlite3.open(db_path)
|
||||||
wafdb = sqlite3.open(db_path)
|
|
||||||
if wafdb == nil then
|
if wafdb == nil then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
wafdb:exec([[PRAGMA journal_mode = wal]])
|
wafdb:exec([[PRAGMA journal_mode = wal]])
|
||||||
wafdb:exec([[PRAGMA synchronous = 0]])
|
wafdb:exec([[PRAGMA synchronous = OFF]])
|
||||||
wafdb:exec([[PRAGMA page_size = 8192]])
|
wafdb:exec([[PRAGMA page_size = 8192]])
|
||||||
wafdb:exec([[PRAGMA journal_size_limit = 2147483648]])
|
wafdb:exec([[PRAGMA journal_size_limit = 2147483648]])
|
||||||
return wafdb
|
return wafdb
|
||||||
|
@ -142,7 +142,11 @@ function _M.exec_action(rule_config, match_rule, data)
|
|||||||
|
|
||||||
local msg = "访问 IP " .. ngx.ctx.ip .. " 访问 URL" .. ngx.var.uri .. " 触发动作 " .. action .. " 规则类型 " .. rule_config.type
|
local msg = "访问 IP " .. ngx.ctx.ip .. " 访问 URL" .. ngx.var.uri .. " 触发动作 " .. action .. " 规则类型 " .. rule_config.type
|
||||||
if match_rule then
|
if match_rule then
|
||||||
msg = msg .. " 触发规则 " .. match_rule.type
|
if match_rule.type then
|
||||||
|
msg = msg .. " 触发规则类型 " .. match_rule.type
|
||||||
|
else
|
||||||
|
msg = msg .. " 触发规则 " .. match_rule.rule
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
ngx.log(ngx.ERR, msg)
|
ngx.log(ngx.ERR, msg)
|
||||||
|
@ -465,7 +465,6 @@ function _M.args_check()
|
|||||||
end
|
end
|
||||||
if val_arr and type(val_arr) ~= "boolean" and val_arr ~= "" then
|
if val_arr and type(val_arr) ~= "boolean" and val_arr ~= "" then
|
||||||
local m, mr = match_rule(args_list, utils.unescape_uri(val_arr))
|
local m, mr = match_rule(args_list, utils.unescape_uri(val_arr))
|
||||||
ngx.log(ngx.ERR, "args_check: ", m, " ", mr.rule, " ", val_arr)
|
|
||||||
if m then
|
if m then
|
||||||
exec_action(get_global_config("args"), mr)
|
exec_action(get_global_config("args"), mr)
|
||||||
return
|
return
|
||||||
@ -531,7 +530,7 @@ function _M.post_check()
|
|||||||
|
|
||||||
local boundary = get_boundary()
|
local boundary = get_boundary()
|
||||||
|
|
||||||
if boundary and is_site_state_on('fileExtCheck') then
|
if boundary and is_state_on('fileExt') then
|
||||||
if not ngx_re_match(content_type, '^multipart/form-data; boundary=') or not ngx_re_find(content_type, [[multipart]], 'ijo')then
|
if not ngx_re_match(content_type, '^multipart/form-data; boundary=') or not ngx_re_find(content_type, [[multipart]], 'ijo')then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -548,7 +547,7 @@ function _M.post_check()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local rule = get_site_config("fileExtCheck")
|
local rule = get_site_rule("fileExt")
|
||||||
while true do
|
while true do
|
||||||
local m = iterator()
|
local m = iterator()
|
||||||
if m then
|
if m then
|
||||||
|
@ -138,7 +138,7 @@ local function write_req_log(attack)
|
|||||||
is_block = is_block,
|
is_block = is_block,
|
||||||
is_attack = is_attack
|
is_attack = is_attack
|
||||||
}
|
}
|
||||||
local code = stmt:step()
|
stmt:step()
|
||||||
stmt:finalize()
|
stmt:finalize()
|
||||||
|
|
||||||
local code2 = 101
|
local code2 = 101
|
||||||
@ -160,12 +160,10 @@ local function write_req_log(attack)
|
|||||||
|
|
||||||
wafdb:execute([[COMMIT]])
|
wafdb:execute([[COMMIT]])
|
||||||
|
|
||||||
if code ~= 101 or code2 ~= 101 then
|
|
||||||
local error_msg = wafdb:errmsg()
|
local error_msg = wafdb:errmsg()
|
||||||
if error_msg then
|
if error_msg then
|
||||||
ngx.log(ngx.ERR, "insert attack_log error ", error_msg .. " ")
|
ngx.log(ngx.ERR, "insert attack_log error ", error_msg .. " ")
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -201,45 +199,25 @@ local function count_not_found()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local add_count = function(shared_dict,key)
|
||||||
|
local count, _ = shared_dict:incr(key, 1)
|
||||||
|
if not count then
|
||||||
|
shared_dict:set(key, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function count_req_status(is_attack)
|
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 status = ngx.status
|
||||||
|
local req_count = ngx.shared.dict_req_count
|
||||||
local stmt_exist = wafdb:prepare("SELECT COUNT(*) FROM waf_stat WHERE day = ?")
|
add_count(req_count, "req_count")
|
||||||
stmt_exist:bind_values(today)
|
if (status >= 400 and status < 500) then
|
||||||
stmt_exist:step()
|
add_count(req_count, "count_4xx")
|
||||||
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
|
end
|
||||||
|
if (status >= 500) then
|
||||||
if code ~= 101 then
|
add_count(req_count, "count_5xx")
|
||||||
local error_msg = wafdb:errmsg()
|
|
||||||
if error_msg then
|
|
||||||
ngx.log(ngx.ERR, "update waf_stat error ", error_msg .. " ")
|
|
||||||
end
|
end
|
||||||
|
if is_attack then
|
||||||
|
add_count(req_count, "attack_count")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -250,12 +228,10 @@ if config.is_waf_on() then
|
|||||||
if not ngx.ctx.ip then
|
if not ngx.ctx.ip then
|
||||||
ngx.ctx.ip = utils.get_real_ip()
|
ngx.ctx.ip = utils.get_real_ip()
|
||||||
ngx.ctx.ip_location = utils.get_ip_location(ngx.ctx.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
|
end
|
||||||
|
|
||||||
count_req_status(is_attack)
|
count_req_status(is_attack)
|
||||||
|
if is_attack then
|
||||||
write_req_log(is_attack)
|
write_req_log(is_attack)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,21 +2,23 @@
|
|||||||
"rules": [
|
"rules": [
|
||||||
{
|
{
|
||||||
"state": "on",
|
"state": "on",
|
||||||
"rule": "no Cookie",
|
"name": "no cookie",
|
||||||
"name": "拦截不带Cookie的请求",
|
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{
|
{
|
||||||
"field": "URL",
|
"field": "URL",
|
||||||
"pattern": "/test/\\d+\\.html"
|
"pattern": "eq",
|
||||||
|
"rule": "/test/\\d+\\.html"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field": "Cookie",
|
"field": "Cookie",
|
||||||
"pattern": ""
|
"pattern": "eq",
|
||||||
|
"rule": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"action": "deny",
|
"action": "deny",
|
||||||
"autoIpBlock": "off",
|
"res": "",
|
||||||
"ipBlockTimeout": 60,
|
"ipBlock": "off",
|
||||||
|
"ipBlockTime": 60,
|
||||||
"description": "拦截不带Cookie的请求"
|
"description": "拦截不带Cookie的请求"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
34
plugins/openresty/waf/rules/fileExt.json
Normal file
34
plugins/openresty/waf/rules/fileExt.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "php",
|
||||||
|
"name": "php",
|
||||||
|
"type": "fileExt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "jsp",
|
||||||
|
"name": "jsp",
|
||||||
|
"type": "fileExt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "asp",
|
||||||
|
"name": "asp",
|
||||||
|
"type": "fileExt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "exe",
|
||||||
|
"name": "exe",
|
||||||
|
"type": "fileExt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"rule": "sh",
|
||||||
|
"name": "sh",
|
||||||
|
"type": "fileExt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
16
plugins/openresty/waf/rules/geoRestrict.json
Normal file
16
plugins/openresty/waf/rules/geoRestrict.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"name": "appFilter1",
|
||||||
|
"rule": "/TomcatBypass/Command/Base64",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"state": "on",
|
||||||
|
"name": "appFilter2",
|
||||||
|
"rule": "j\\S*ndi\\S*:\\S*(?:dap|dns)\\S+",
|
||||||
|
"type": "appFilter"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
@ -156,4 +156,5 @@ if config.is_waf_on() then
|
|||||||
lib.cookie_check()
|
lib.cookie_check()
|
||||||
lib.post_check()
|
lib.post_check()
|
||||||
lib.header_check()
|
lib.header_check()
|
||||||
|
|
||||||
end
|
end
|
@ -1,2 +1,64 @@
|
|||||||
local uuid = require 'resty.uuid'
|
local uuid = require 'resty.uuid'
|
||||||
|
local utils = require "utils"
|
||||||
|
local config = require "config"
|
||||||
|
|
||||||
uuid.seed()
|
uuid.seed()
|
||||||
|
|
||||||
|
local update_req_count = function()
|
||||||
|
local req_count = ngx.shared.dict_req_count
|
||||||
|
local req_count_update = req_count:get("req_count") or 0
|
||||||
|
req_count:set("req_count", 0)
|
||||||
|
local count_4xx_update = req_count:get("count_4xx") or 0
|
||||||
|
req_count:set("count_4xx", 0)
|
||||||
|
local count_5xx_update = req_count:get("count_5xx") or 0
|
||||||
|
req_count:set("count_5xx", 0)
|
||||||
|
local attack_count_update = req_count:get("attack_count") or 0
|
||||||
|
req_count:set("attack_count", 0)
|
||||||
|
|
||||||
|
if req_count_update == 0 and count_4xx_update == 0 and count_5xx_update == 0 and attack_count_update == 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local today = ngx.today()
|
||||||
|
local wafdb = utils.get_wafdb(config.waf_db_path)
|
||||||
|
if not wafdb then
|
||||||
|
ngx.log(ngx.ERR, "get log db failed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
wafdb:execute([[BEGIN TRANSACTION]])
|
||||||
|
|
||||||
|
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 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
|
||||||
|
|
||||||
|
wafdb:execute([[COMMIT]])
|
||||||
|
|
||||||
|
--local error_msg = wafdb:errmsg()
|
||||||
|
--if error_msg then
|
||||||
|
-- ngx.log(ngx.ERR, "update waf_stat error ", error_msg .. " ")
|
||||||
|
--end
|
||||||
|
end
|
||||||
|
|
||||||
|
if 0 == ngx.worker.id() then
|
||||||
|
local ok, err = ngx.timer.every(2, update_req_count)
|
||||||
|
if not ok then
|
||||||
|
ngx.log(ngx.ERR, "failed to create the timer: ", err)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
Loading…
x
Reference in New Issue
Block a user