0 0 0

Nginx自定义拦截记录日志

酋长
24-03-05 1097

在我们的日常使用中,会直接在 Nginx 配置一些拦截规则,而被拦截的请求不会单独记录日志,例如我的香港中转服务器依然使用 NAXSI 进行基础过滤。如果规则有误则很难排查问题,在研究雷池的拦截页面后,我按照雷池的记录拦截 ID 的思路,使用 LUA 编写了一段代码以完成我的需求。需要注意,Nginx 必须安装了 LUA 模块才能使用此代码,Tengine 和 Openresty 已经默认安装 LUA 模块,执行 nginx -V (大写) 检查是否安装了 LUA 模块。

local function generate_unique_id()
    -- 生成一个基于时间和随机数的唯一ID
    local time_in_ms = ngx.now() * 1000
    local random_num = math.random(10000, 99999)
    return string.format("%s-%s", time_in_ms, random_num)
end
 
local function log_request_and_generate_hash()
    -- 使用Nginx的变量获取请求IP
    local client_ip = ngx.var.custom_x_real_ip
    local request_time = os.date("%Y-%m-%d %H:%M:%S", ngx.time())
    local request_url = ngx.var.request_uri
    local user_agent = ngx.var.http_user_agent
    local host = ngx.var.http_host
 
    -- 生成唯一的错误ID
    local error_id = generate_unique_id()
 
    -- 使用Lua的table和concat函数生成一个简单的哈希
    local hash_table = {host,client_ip, request_time, request_url, user_agent, error_id}
    local hash_value = table.concat(hash_table, "|")
 
    -- 返回请求信息、哈希值和错误ID
    return host,client_ip, request_time, request_url, user_agent, hash_value, error_id
end
 
-- 获取当前状态码
local status = ngx.var.status
 
-- 检查状态码是否为418
if status == "418" then
    -- 记录请求并生成哈希
    local host,client_ip, request_time, request_url, user_agent, hash_value, error_id = log_request_and_generate_hash()
 
    -- 打开文件以追加模式
    local file, err = io.open("/www/err/418/error.log", "a")
    if not file then
        ngx.log(ngx.ERR, "无法打开文件 /www/err/418/error.log: ", err)
        return
    end
 
    -- 记录请求信息、哈希值和错误ID
    -- 记录哈希值和错误ID
local log_entry = string.format("%s\n", hash_value)
local success, err = file:write(log_entry)
 
    if not success then
        ngx.log(ngx.ERR, "无法写入文件 /www/err/418/error.log: ", err)
        return
    end
    file:close()
 
    -- 读取418.html文件内容
    local error_file, err = io.open("/www/err/418.html", "r")
    if not error_file then
        ngx.log(ngx.ERR, "无法打开文件 /www/err/418.html: ", err)
        return
    end
    local file_content = error_file:read("*a")
    error_file:close()
 
    -- 在错误页面中添加错误ID的注释
    file_content = file_content .. "\n<!-- event_id: " .. error_id .. " -->"
 
    -- 输出418.html文件内容和错误ID
    ngx.say(file_content)
else
    ngx.log(ngx.ERR, "状态码不是418,当前状态码为: ", status)
end

论坛没有 LUA 的着色)

需要确保 /www/err/418.html 和日志目录 /www/err/418/error.log 真实存在,418 状态码为我自定义的拦截代码,该代码为一个愚人节玩笑而非非,I'M A TEAPOT,虽然 418 状态码在 RFC 7231 中并未明确定义,但它仍然被 4xx(客户端错误)类别所覆盖。然而,这个状态码并不是用来严肃对待的,也不会对客户端或 SEO 产生实质性影响。这个状态码在实际应用中很少使用,但在一些特定的情况下,开发者可能会使用它来进行一些特殊的处理。例如,如果服务器收到了一个无法处理的请求,它可能会返回 418 状态码,表示 "我是茶壶,找咖啡壶去"。这就是说,这个请求应该由其他能够处理的服务器(咖啡壶)来处理。使用 418 状态码为了不影响 403 页面的正常响应。

在 418.html 中,使用如下 JavaScript 代码获取拦截 ID:

 <script>
//此处为获取访客IP
        async function getIPInfo() {
            const response = await fetch('https://api.gymxbl.com/ip/get');
            const data = await response.json();
            return data;
        }
 
        function ipinfo() {
            getIPInfo().then(data => {
                const li = document.getElementById('ip-info');
                li.textContent += `${data.cip}  Location: ${data.clo}`;
            });
        }
 
        function setEventIDAndType() {
            var nodes = document.getElementsByTagName("body")[0].childNodes;
            var fit2inserts = null;
            for (var i = 0; i < nodes.length; i++) {
                if (
                    nodes[i].nodeType == 8 &&
                    nodes[i].data.trimLeft().startsWith("event_id")
                ) {
                    fit2inserts = nodes[i];
                }
            }
 
            try {
                var inserts =
                    document.getElementsByTagName("html")[0].nextSibling || fit2inserts;
                var insertsData = inserts && (inserts.data || "");
                var incertDataList = insertsData.split(" ");
                var getVal = function (key) {
                    return incertDataList[incertDataList.indexOf(key + ":") + 1];
                };
                var event_id = getVal("event_id");
                var type = getVal("type");
                // var anymore = getVal('anymore') // 新增参数示例
            } catch (e) {
                console.log(e);
            }
            if (event_id) {
                document.getElementById("EventID").innerText = "你的ID(•ω•):" + event_id;
            }
            if (type) {
                document.getElementById("TYPE").innerText = "TYPE: " + type;
            }
        }
 
        // 使用addEventListener添加事件监听器
        window.addEventListener('load', function() {
            ipinfo();
            setEventIDAndType();
        });
//这一段基本是从雷池的拦截页抄的
    </script>

接下来在希望向访客展示错误的地方将 ID 填入: id="ip-info" id="EventID"

<div class="md:flex min-h-screen main-body">
			<div class="w-full md:w-1/2 bg-white flex items-center justify-center left-sec">
				<div class="max-w-sm m-8 content-body">
					<div class="text-black text-5xl md:text-15xl font-black">访问被拦截了哦 ヽ(*。>Д<)o゜</div>
 
							  <p class="text-grey-darker text-2xl md:text-3xl mb-7 leading-normal">
							</p>
							<p class="sub-header">
								<h1>好好反省为什么会被拦截叭!(ノ`Д)ノ</h1>
							</p>
							<br>
							<br>
							我是一个茶壶!I'm a teapot!
 
							<div class="sub-footer">
								<div class="w-full h-2 bg-grey-light my-3 md:my-6"></div>
								<ul>
									<p></p>
									<li id="EventID"></li>
									<br>
									<li id="ip-info">你的IP: </li>
									<br>
									<li>发生时间: <script type="text/javascript">
											document.write(Date())
										</script>
									</li>
									<br>
									<li>拦截由 Lua 脚本</li>
								</ul>
							</div>
 
					</div>
				</div>
 
				<div class="relative pb-full md:flex md:pb-0 md:min-h-screen w-full md:w-1/2 right-sec">
				</div>
			</div>

此 HTML 仅为示例,使用时需要自行定制。

部署 Nginx
在你希望应用该页面的域名所属 Server 块中添加:

location = /418.html
{
#交给LUA处理
content_by_lua_file /www/lua/418.lua;
#禁止外部访问,404错误码
error_page 404 /404.html;
#仅允许内部访问
internal;
}
#哦对了别忘了把errpage加上,特意回来加一句.....
error_page 418 /418.html;

重启 Nginx 使能生效。。。

 

我是搞不明白的 本文转自:https://dalao.net/thread-25718.htm

展开全文
『Ta』们也感兴趣 (0)

请先登录后发表评论!

最新回复 (0)

    暂无评论

返回
请先登录后发表评论!