为什么要动态封禁IP?
动态封禁IP说白了就是给访问过于“勤快”的用户踩个急刹车。核心需求总结:
- 封禁恶意请求:比如那些试图暴力破解、SQL注入的攻击者。
- 动态管理黑名单:自动化拉黑,不用手动更新配置文件。
- 封禁时间设置:可控时长,让被封禁的IP有“改过自新”的机会。
技术选型:Nginx + Lua + Redis
整个方案看起来就像一顿营养均衡的程序员套餐:Nginx负责拦截,Lua负责逻辑,Redis负责存储。具体来说:- Nginx:作为反向代理,用它的性能处理高并发。
- Lua:通过OpenResty框架,可以灵活编写动态逻辑。
- Redis:存储IP黑名单,高效快速,支持过期时间。
环境配置
- 操作系统:CentOS 7 或 Ubuntu,其他主流发行版也行。
- Redis:建议使用 5.0.5 或更高版本。
- Nginx:推荐 Nginx-OpenResty,它让Lua配置更加方便。
# 安装OpenResty
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
sudo yum install -y openresty
# 安装Redis
sudo yum install -y epel-release
sudo yum install -y redis
sudo systemctl start redis
sudo systemctl enable redis
配置实现
下面是一个实际配置的完整流程:Nginx 配置
在Nginx配置文件的<font style="color:rgb(30, 107, 184);">server</font>
块中,添加Lua脚本的位置:
http {
lua_shared_dict ip_blacklist 10m; # 定义共享字典存储黑名单
server {
listen 80;
location / {
access_by_lua_file /etc/nginx/lua/block_ip.lua;
proxy_pass http://backend;
}
}
}
Lua 脚本
这段脚本会连接Redis并检查访问频率,逻辑大致如下:- 获取用户IP。
- 检查IP是否在黑名单中。
- 如果IP访问频率超标,加入黑名单。
- 黑名单的封禁时间由Redis设置。
-- 引入Redis库
local redis = require "resty.redis"
-- 定义Redis连接函数
local function connect_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 to Redis: ", err)
return nil
end
return red
end
-- 获取客户端IP
local client_ip = ngx.var.remote_addr
-- Redis存储逻辑
local red = connect_redis()
if not red then
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- 检查是否在黑名单
local is_blocked = red:get("block:" .. client_ip)
if is_blocked == "1" then
ngx.exit(ngx.HTTP_FORBIDDEN) -- 返回403
end
-- 记录访问次数
local count, err = red:incr("count:" .. client_ip)
if not count then
ngx.log(ngx.ERR, "Failed to increment count: ", err)
ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)
end
-- 设置访问次数过期时间
if count == 1 then
red:expire("count:" .. client_ip, 10) -- 10秒过期
end
-- 如果超过阈值,加入黑名单
if count > 100 then
red:set("block:" .. client_ip, "1")
red:expire("block:" .. client_ip, 60) -- 封禁1分钟
ngx.exit(ngx.HTTP_FORBIDDEN)
end
工作原理小解析
- IP 获取与检测:脚本中
<font style="color:rgb(30, 107, 184);">ngx.var.remote_addr</font>
直接获取客户端IP。 - Redis 计数:每次请求都会增加计数器,如果超出阈值,则将IP加入黑名单。
- 黑名单封禁:Redis的
<font style="color:rgb(30, 107, 184);">expire</font>
功能自动处理封禁时效,不需要我们操心。
优势分析
- 轻量级:Nginx和Lua结合,性能开销极小。
- 动态性强:IP黑名单动态更新,无需重启服务。
- 可扩展:只需修改脚本逻辑,就可以实现更复杂的规则,比如白名单、访问频率统计等。
应用场景扩展
除了拦截爬虫和恶意用户,这套方案还能应用在:- 防DDoS攻击:结合Nginx的限速模块。
- 防止数据滥用:比如对API接口进行访问频率限制。
- 异常检测:进一步扩展,可以接入日志分析系统。
<font style="color:rgb(30, 107, 184);">X-Forwarded-For</font>
头获取真实IP。
最后,动态封禁IP虽然不能防住“所有坏人”,但就像“锁车门不能防小偷,但能让他去偷别人的车”,还是很有必要的!