为什么要动态封禁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配置更加方便。
# 安装OpenRestysudo yum install -y yum-utilssudo yum-config-manager --add-repo https://openresty.org/package/centos/openresty.reposudo yum install -y openresty# 安装Redissudo yum install -y epel-releasesudo yum install -y redissudo systemctl start redissudo 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 thenngx.log(ngx.ERR, "Failed to connect to Redis: ", err)return nilendreturn redend-- 获取客户端IPlocal client_ip = ngx.var.remote_addr-- Redis存储逻辑local red = connect_redis()if not red thenngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end-- 检查是否在黑名单local is_blocked = red:get("block:" .. client_ip)if is_blocked == "1" thenngx.exit(ngx.HTTP_FORBIDDEN) -- 返回403end-- 记录访问次数local count, err = red:incr("count:" .. client_ip)if not count thenngx.log(ngx.ERR, "Failed to increment count: ", err)ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR)end-- 设置访问次数过期时间if count == 1 thenred:expire("count:" .. client_ip, 10) -- 10秒过期end-- 如果超过阈值,加入黑名单if count > 100 thenred: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虽然不能防住“所有坏人”,但就像“锁车门不能防小偷,但能让他去偷别人的车”,还是很有必要的!
