nginx|openresty配置lua脚本自动封ip

本文不进行介绍怎么安装lua环境、openresty预安装有lua脚本运行环境、nginx需要自行编译;
本文会把黑名单ip定义在文件中,程序运行时定时覆盖内容到内存中,提升运行速度

1、nginx.conf配置加载lua
#WAF
lua_shared_dict blist 50m;
init_worker_by_lua_file "/usr/local/openresty/nginx/conf/lua/init.lua";
access_by_lua_file "/usr/local/openresty/nginx/conf/lua/blacklist.lua";

lua_shared_dict blist 作用:创建一个共享内存字典,命名为 blist(存储黑名单ip信息)

init_worker_by_lua_file 作用:初始化全局变量或其他 Lua 模块

access_by_lua_file 作用:在 access 阶段(请求访问控制阶段)运行指定的 Lua 脚本

获取访问进来的ip地址(兼容ipv4和ipv6)
map $http_x_forwarded_for $realip
{
 "" $remote_addr;
 ##兼容ipv4及ipv6,兼容特殊格式ipv6
 "~*(?P<firstAddr>([0-9a-f]{0,4}:){1,7}[0-9a-f]{1,4}|([0-9]{1,3}\.){3}[0-9]{1,3})" $firstAddr;
 }

把获取到的ip定义变量为$realip,后续脚本会调用

2、配置init.lua和blacklist.lua脚本
init.lua
 local function load_blacklist_to_shared_memory()
 local file, err = io.open("/usr/local/openresty/nginx/conf/waf/rule-config/iplist.txt", "r")
 if not file then
 ngx.log(ngx.ERR, "failed to open blacklist file: ", err)
 return
 end
 -- 检查文件是否为空
 local is_empty = true
 for _ in file:lines() do
 is_empty = false
 break
 end
 if is_empty then
 ngx.log(ngx.ERR, "blacklist file is empty")
 file:close()
 return
 end
 -- 文件非空时重新打开文件并加载数据
 file:seek("set", 0)
 local dict = ngx.shared.blist
 dict:flush_all() -- 清空共享内存,重新加载
 for line in file:lines() do
 dict:set(line, true)
 end
 file:close()
 end
 local function refresh_blacklist(premature)
 if premature then
 return
 end
 load_blacklist_to_shared_memory()
 ngx.log(ngx.INFO, "Blacklist reloaded successfully.")
 -- 重新设置定时器
 local ok, err = ngx.timer.at(5, refresh_blacklist)
 if not ok then
 ngx.log(ngx.ERR, "Failed to create timer: ", err)
 end
 end
 -- 初始化时加载黑名单
 load_blacklist_to_shared_memory()
 -- 设置定时器
 local ok, err = ngx.timer.at(5, refresh_blacklist)
 if not ok then
 ngx.log(ngx.ERR, "Failed to create timer: ", err)
 end

定时器是5秒钟重新到黑名单文件读取ip写到内存中

blacklist.lua
 -- 加载黑名单数据到共享内存
 local function load_blacklist_to_shared_memory()
 local file, err = io.open("/usr/local/openresty/nginx/conf/waf/rule-config/iplist.txt", "r")
 if not file then
 ngx.log(ngx.ERR, "failed to open blacklist file: ", err)
 return false
 end
 -- 检查文件是否为空
 local is_empty = true
 for _ in file:lines() do
 is_empty = false
 break
 end
 if is_empty then
 ngx.log(ngx.ERR, "blacklist file is empty")
 file:close()
 return false
 end
 -- 文件非空时重新打开文件并加载数据
 file:seek("set", 0)
 local dict = ngx.shared.blist
 dict:flush_all() -- 清空共享内存,重新加载
 for line in file:lines() do
 dict:set(line, true)
 end
 file:close()
 return true
 end
 -- 检查 IP 是否在黑名单中
 local function check_blacklist(ip)
 local dict = ngx.shared.blist
 return dict:get(ip)
 end
 -- 获取客户端 IP
 local function get_client_ip()
 return ngx.var.realip or "unknown"
 end
 -- 主逻辑
 -- local blacklist_loaded = load_blacklist_to_shared_memory()
 local client_ip = get_client_ip()
 if check_blacklist(client_ip) then
 ngx.log(ngx.ERR, "IP " .. client_ip .. " is blacklisted")
 ngx.exit(ngx.HTTP_FORBIDDEN)
 end
 -- 根据请求路径分发处理逻辑
 local uri = ngx.var.uri
 if uri == "/reload-blacklist" then
 -- 添加 IP 条件判断
 local allowed_ip_prefix = "172.31"
 local client_ip_prefix = string.sub(client_ip, 1, #allowed_ip_prefix)
 if client_ip_prefix ~= allowed_ip_prefix then
 ngx.log(ngx.ERR, "Access forbidden for IP: " .. client_ip)
 ngx.exit(ngx.HTTP_FORBIDDEN)
 end
 -- 重新加载黑名单到共享内存
 local reloaded = load_blacklist_to_shared_memory()
 if reloaded then
 ngx.say("Blacklist reloaded successfully.")
 else
 ngx.say("Blacklist file not found or is empty. No changes made.")
 end
 end

可以自定义访问reload-blacklist路径的url就进行刷新黑名单ip,allowed_ip_prefix对访问进来的ip进行白名单限制

最后定义iplist.txt就可以了,每个ip为一行,init.lua和blacklist.lua中用io.open定义iplist.txt的路径
iplist.txt
1.2.3.4
1a:d8:eb:42:91:f8

进行reload程序就可以使用了,在这个自动化的基础上,配合日志系统,根据自定义规则就可以自动化的屏蔽异常ip;
异常ip输出到error日志中

博主使用过loki日志系统和elk日志系统,都可以根据一定时间内ip次数到达多少就自动屏蔽ip;运维从此不再半夜起床排查异常ip;
后续更新这篇博客,加入跳转链接,敬请期待;

作者:有猫叉腰原文地址:https://www.cnblogs.com/cathat/p/18634875

%s 个评论

要回复文章请先登录注册