local redis = require "resty.redis" local cjson = require "cjson" -- Redis连接配置 local red = redis:new() red:set_timeout(1000) -- 1秒超时 local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "failed to connect: ", err) return end -- 获取客户端IP local client_ip = ngx.var.remote_addr local blacklist_key = "blacklist:" .. client_ip -- 检查IP是否在黑名单 local is_banned, err = red:get(blacklist_key) if is_banned == 1 then -- 获取封禁原因 local reason, err = red:get(blacklist_key .. ":reason") ngx.header["X-Ban-Reason"] = reason or "Unknown" ngx.status = ngx.HTTP_FORBIDDEN ngx.say("IP被封禁,请联系管理员") ngx.exit(ngx.HTTP_FORBIDDEN) end -- 记录访问频率(可选) local access_key = "access:" .. client_ip local count, err = red:incr(access_key) if count == 1 then red:expire(access_key, 60) -- 60秒窗口 end -- 如果60秒内访问超过100次,自动加入黑名单 if count > 100 then red:set(blacklist_key, 1) red:expire(blacklist_key, 3600) -- 封禁1小时 red:set(blacklist_key .. ":reason", "高频访问") ngx.log(ngx.WARN, "Auto-banned IP: ", client_ip) end -- 保持连接池 local ok, err = red:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, "failed to set keepalive: ", err) end
5.4 Nginx配置
# /usr/local/openresty/nginx/conf/nginx.conf http { # 引入Redis库 lua_package_path "/usr/local/openresty/lualib/?.lua;;"; # 共享内存配置 lua_shared_dict ban_dict 10m; init_by_lua_block { -- 初始化时加载配置 } server { listen 80; server_name example.com; # 在access阶段执行Lua脚本 access_by_lua_block { require("access").run() } location / { root /var/www/html; index index.html; } # 管理接口(需要IP白名单保护) location /admin/ban/ { allow 192.168.1.0/24; deny all; content_by_lua_block { local redis = require "resty.redis" local red = redis:new() red:connect("127.0.0.1", 6379) local uri = ngx.var.request_uri local method = ngx.req.get_method() -- 添加黑名单 if method == "POST" and uri:match("/admin/ban/add") then ngx.req.read_body() local body = ngx.req.get_body_data() local data = cjson.decode(body) red:set("blacklist:" .. data.ip, 1) red:expire("blacklist:" .. data.ip, data.ttl or 3600) red:set("blacklist:" .. data.ip .. ":reason", data.reason or "Manual ban") ngx.say("OK") end -- 移除黑名单 if method == "POST" and uri:match("/admin/ban/remove") then ngx.req.read_body() local body = ngx.req.get_body_data() local data = cjson.decode(body) red:del("blacklist:" .. data.ip) red:del("blacklist:" .. data.ip .. ":reason") ngx.say("OK") end -- 查看黑名单列表 if method == "GET" and uri:match("/admin/ban/list") then local keys = red:keys("blacklist:*") ngx.say(cjson.encode(keys)) end } } } }
-- 在access.lua中添加 -- 检测404错误过多 local status_key = "status:404:" .. client_ip local status_count = red:incr(status_key) if status_count == 1 then red:expire(status_key, 300) -- 5分钟窗口 end if status_count > 20 then red:set(blacklist_key, 1) red:expire(blacklist_key, 7200) red:set(blacklist_key .. ":reason", "过多404请求") end -- 检测User-Agent异常 local user_agent = ngx.var.http_user_agent or "" if user_agent == "" or user_agent:match("bot") or user_agent:match("spider") then -- 可疑UA,记录但不封禁 red:incr("suspicious:ua:" .. client_ip) end
白名单机制
-- 白名单IP检查 local whitelist_key = "whitelist:" .. client_ip if red:exists(whitelist_key) then -- 白名单IP,跳过所有检查 return end
分布式支持
-- Redis集群支持 local red = redis:new() red:set_timeout(1000) local ok, err = red:connect("redis-cluster.example.com", 6379) if not ok then -- 备用Redis ok, err = red:connect("redis-backup.example.com", 6379) end
5.7 性能优化
# 使用连接池 lua_resty_redis_max_idle_timeout 10000 lua_resty_redis_pool_size 100 # 本地缓存减少Redis查询 lua_shared_dict ip_blacklist_cache 10m; -- 缓存黑名单 # 定时同步黑名单到本地缓存 init_worker_by_lua_block { local delay = 5 -- 同步间隔(秒) local new_timer = ngx.timer.at local check = nil check = function(premature) if not premature then -- 从Redis加载黑名单到共享内存 local redis = require "resty.redis" local red = redis:new() red:connect("127.0.0.1", 6379) local keys = red:keys("blacklist:*") local ban_dict = ngx.shared.ban_dict for _, key in ipairs(keys) do local ip = key:gsub("blacklist:", "") ban_dict:set(ip, true) end new_timer(delay, check) end end new_timer(delay, check) }
5.8 优缺点分析
优点:
• 性能极佳,毫秒级响应
• 支持分布式部署
• 动态管理,无需重启
• 功能强大,易于扩展
缺点:
• 部署复杂度较高
• 需要额外的Redis服务
• 需要学习Lua编程
六、方案选型建议
决策树
方案选型决策树
组合使用方案
多层防御架构
实战推荐组合:
1. 基础防护:Nginx内置限流(limit_req)
2. 自动识别:Fail2ban检测攻击
3. 动态封禁:Lua+Redis统一管理
这种多层防御架构可以有效应对各种攻击场景。
七、生产环境最佳实践
7.1 监控告警
# 监控黑名单数量变化 def monitor_blacklist(): while True: bans = manager.list_bans() if len(bans) > 100: # 发送告警 send_alert(f"黑名单IP数量异常: {len(bans)}") time.sleep(60)