Nginx + Lua + Redis:打造智能 IP 黑名单系统
nginx通过Lua+Redis实现动态封禁IP
需求背景
在Web服务中,为了防止恶意用户或爬虫对服务器造成不必要的负载和潜在的安全威胁,我们可以通过设置动态IP黑名单来拒绝来自这些IP的请求。本文将详细介绍如何使用Nginx配合Lua脚本及Redis数据库实现这一功能,并允许为每个被封禁的IP设定失效时间。
实现方案对比
在实现 IP 黑名单功能时,有多种方案可供选择:
为了兼顾灵活性和管理便捷性,我们选择通过 Nginx + Lua + Redis 的架构来实现 IP 黑名单功能。
这里选择结合Nginx与Lua脚本并通过Redis存储黑名单数据的方法,以达到灵活管理并共享黑名单的目的。
Nginx与Lua脚本并通过Redis存储黑名单数据的方法
操作步骤
1. 安装必要的软件确保你的系统已经安装了OpenResty版Nginx以及Redis服务。可以参考官方文档完成相关安装。
2. 创建Lua脚本文件创建一个名为access_limit.lua的文件,路径根据实际需要调整,例如 /usr/local/lua/access_limit.lua。该脚本负责检查客户端IP是否位于Redis维护的黑名单内,并据此决定是否继续处理请求。
lua脚本具体内容凯哥会放在文末。
配置 Nginx.conf
在 Nginx 配置文件中,我们需要在需要进行限制的 server 的 location 中添加 Lua 脚本的访问控制。
location / {
# 如果该location 下存在静态资源文件可以做一个判断
#if ($request_uri ~ .*\.(html|htm|jpg|js|css)) {
# access_by_lua_file /usr/local/lua/access_limit.lua;
#}
access_by_lua_file /usr/local/lua/access_limit.lua; # 加上了这条配置,则会根据 access_limit.lua 的规则进行限流
alias /usr/local/web/;
index index.html index.htm;
}
Lua 脚本实现
/usr/local/lua/access_limit.lua
-- 可以实现自动将访问频次过高的IP地址加入黑名单封禁一段时间
--连接池超时回收毫秒
local pool_max_idle_time = 10000
--连接池大小
local pool_size = 100
--redis 连接超时时间
local redis_connection_timeout = 100
--redis host
local redis_host = "your redis host ip"
--redis port
local redis_port = "your redis port"
--redis auth
local redis_auth = "your redis authpassword";
--封禁IP时间(秒)
local ip_block_time= 120
--指定ip访问频率时间段(秒)
local ip_time_out = 1
--指定ip访问频率计数最大值(次)
local ip_max_count = 3
-- 错误日志记录
local function errlog(msg, ex)
ngx.log(ngx.ERR, msg, ex)
end
-- 释放连接池
local function close_redis(red)
if not red then
return
end
local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
if not ok then
ngx.say("redis connct err:",err)
return red:close()
end
end
--连接redis
local redis = require "resty.redis"
local client = redis:new()
local ok, err = client:connect(redis_host, redis_port)
-- 连接失败返回服务器错误
if not ok then
close_redis(client)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
--设置超时时间
client:set_timeout(redis_connection_timeout)
-- 优化验证密码操作 代表连接在连接池使用的次数,如果为0代表未使用,不为0代表复用 在只有为0时才进行密码校验
local connCount, err = client:get_reused_times()
-- 新建连接,需要认证密码
if 0 == connCount then
local ok, err = client:auth(redis_auth)
if not ok then
errlog("failed to auth: ", err)
return
end
--从连接池中获取连接,无需再次认证密码
elseif err then
errlog("failed to get reused times: ", err)
return
end
-- 获取请求ip
local function getIp()
local clientIP = ngx.req.get_headers()["X-Real-IP"]
if clientIP == nil then
clientIP = ngx.req.get_headers()["x_forwarded_for"]
end
if clientIP == nil then
clientIP = ngx.var.remote_addr
end
return clientIP
end
local cliendIp = getIp();
local incrKey = "limit:count:"..cliendIp
local blockKey = "limit:block:"..cliendIp
--查询ip是否被禁止访问,如果存在则返回403错误代码
local is_block,err = client:get(blockKey)
if tonumber(is_block) == 1 then
ngx.exit(ngx.HTTP_FORBIDDEN)
close_redis(client)
end
local ip_count, err = client:incr(incrKey)
if tonumber(ip_count) == 1 then
client:expire(incrKey,ip_time_out)
end
--如果超过单位时间限制的访问次数,则添加限制访问标识,限制时间为ip_block_time
if tonumber(ip_count) > tonumber(ip_max_count) then
client:set(blockKey,1)
client:expire(blockKey,ip_block_time)
end
close_redis(client)
优点总结
通过 Nginx + Lua + Redis 实现的 IP 黑名单功能具有以下优点:
扩展应用场景
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。