|
|
---
|
|
|
--- Generated by MC(https://www.magentochina.org/)
|
|
|
--- Created by Shua1.
|
|
|
--- DateTime: 2024/04/29 14:32
|
|
|
---
|
|
|
local log = require("waf.log")
|
|
|
local _M = {
|
|
|
version = 0.1,
|
|
|
name = "request-log2file"
|
|
|
}
|
|
|
-- 内存队列,用于缓存日志数据
|
|
|
local logQueue = {}
|
|
|
-- 获取当前日期
|
|
|
local function getCurrentDate()
|
|
|
return os.date("%Y-%m-%d")
|
|
|
end
|
|
|
-- 日志文件路径
|
|
|
local logFilePath = "/uuwaf/logs/access_log_" .. getCurrentDate() .. ".json"
|
|
|
-- 记录的请求数量阈值,降低IOPS
|
|
|
local LOG_THRESHOLD = 10
|
|
|
-- 计数器,用于跟踪已记录的请求数量
|
|
|
local requestCounter = 0
|
|
|
|
|
|
-- 将日志信息写入文件
|
|
|
local function logToFile(filename, logs)
|
|
|
local file = io.open(filename, "a")
|
|
|
if not file then
|
|
|
ngx.log(ngx.ERR, "Failed to open log file: ", filename)
|
|
|
return
|
|
|
end
|
|
|
|
|
|
for _, info in ipairs(logs) do
|
|
|
local json_str = log.encodeJson(info)
|
|
|
file:write(json_str .. "\n")
|
|
|
end
|
|
|
|
|
|
file:close()
|
|
|
end
|
|
|
|
|
|
-- 判断是否应该记录该请求
|
|
|
local function shouldLogRequest(waf)
|
|
|
-- 如果是POST请求,则无论URI是否匹配静态文件,都记录
|
|
|
if ngx.var.request_method == "POST" then
|
|
|
return true
|
|
|
end
|
|
|
-- 排除以静态文件格式为结尾的请求,可根据自身需求修改
|
|
|
local uri = ngx.var.uri
|
|
|
if uri:match("/[^/]*%.(js|css|jpg|jpeg|png|gif|svg|webp)$") then
|
|
|
return false
|
|
|
end
|
|
|
return true
|
|
|
end
|
|
|
|
|
|
-- 将日志信息写入内存队列
|
|
|
local function logToMemory(info)
|
|
|
table.insert(logQueue, info)
|
|
|
-- 写入文件
|
|
|
logToFile(logFilePath, logQueue)
|
|
|
end
|
|
|
|
|
|
-- 将内存队列中的日志写入文件
|
|
|
local function flushLogsToFile(premature, filename)
|
|
|
if not premature then
|
|
|
if #logQueue > 0 then
|
|
|
logToFile(filename, logQueue)
|
|
|
logQueue = {} -- 清空内存队列
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
-- 截断字符串到指定长度并进行Base64处理
|
|
|
local function truncateString(str, length)
|
|
|
if str and #str > length then
|
|
|
str = str:sub(1, length)
|
|
|
end
|
|
|
return ngx.encode_base64(str)
|
|
|
end
|
|
|
|
|
|
-- 请求阶段后过滤
|
|
|
function _M.log_pre_filter(waf)
|
|
|
-- 判断是否应该记录该请求
|
|
|
if shouldLogRequest(waf) then
|
|
|
local request_body_short = ""
|
|
|
local block_action = ""
|
|
|
local waf_rule_id = ""
|
|
|
if ngx.var.request_method == "POST" and waf.reqContentLength > 2 then
|
|
|
local body_data = (waf.form and waf.form["RAW"]) or ''
|
|
|
if body_data then
|
|
|
request_body_short = truncateString(body_data, 1000) -- 截断 request_body_short 并进行 Base64 处理
|
|
|
end
|
|
|
end
|
|
|
if waf.msg then
|
|
|
block_action = "uuWaf"
|
|
|
waf_rule_id = waf.rule_id
|
|
|
end
|
|
|
local info = {
|
|
|
["__time__"] = math.floor(ngx.var.msec),
|
|
|
["block_action"] = block_action,
|
|
|
["waf_rule_id"] = waf_rule_id,
|
|
|
["time"] = ngx.var.time_iso8601,
|
|
|
["real_client_ip"] = waf.ip,
|
|
|
["server_addr"] = ngx.var.server_addr,
|
|
|
["remote_addr"] = ngx.var.http_x_forwarded_for,
|
|
|
["scheme"] = ngx.var.scheme,
|
|
|
["request_method"] = ngx.var.request_method,
|
|
|
["request_uri"] = ngx.var.request_uri,
|
|
|
["request_length"] = ngx.var.request_length,
|
|
|
["uri"] = ngx.var.uri,
|
|
|
["request_time"] = ngx.var.request_time,
|
|
|
["body_bytes_sent"] = ngx.var.body_bytes_sent,
|
|
|
["request_body"] = request_body_short,
|
|
|
["bytes_sent"] = ngx.var.bytes_sent,
|
|
|
["status"] = ngx.var.status,
|
|
|
["upstream_time"] = ngx.var.upstream_response_time,
|
|
|
["upstream_host"] = ngx.var.upstream_addr,
|
|
|
["upstream_status"] = ngx.var.upstream_status,
|
|
|
["host"] = ngx.var.host,
|
|
|
["http_referer"] = ngx.var.http_referer,
|
|
|
["http_user_agent"] = ngx.var.http_user_agent,
|
|
|
["http_cookie"] = ngx.var.http_cookie
|
|
|
}
|
|
|
-- 将日志信息存入内存队列
|
|
|
table.insert(logQueue, info)
|
|
|
-- 更新计数器
|
|
|
requestCounter = requestCounter + 1
|
|
|
-- 如果达到记录阈值,则执行写入操作,并重置计数器
|
|
|
if requestCounter >= LOG_THRESHOLD then
|
|
|
logToFile(logFilePath, logQueue)
|
|
|
logQueue = {} -- 清空内存队列
|
|
|
requestCounter = 0 -- 重置计数器
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
return _M
|