背景:访问时不时会被暴力刷量,爬虫和恶意攻击导致数据库,服务等瘫痪
需求:在Nginx上实现一个动态拦截IP的方法,具体是当某个IP在1分钟内访问超过60次时,将其加入Redis并拦截,拦截时间默认1天。
技术选型:使用Nginx+Lua+Redis的方法。这种方案通过Lua脚本在Nginx处理请求时检查Redis中的黑名单,同时统计访问频率,超过阈值就封禁。这应该符合用户的需求。
需要结合Lua脚本和Redis的计数功能。安装OpenResty,配置Nginx的Lua模块,编写Lua脚本统计访问次数,使用Redis存储和过期键,以及设置拦截逻辑。连接池的使用,避免频繁连接Redis影响性能。
一、环境准备
OpenResty集成了Nginx和Lua模块,支持直接运行Lua脚本:- # Ubuntu/Debian
- sudo apt-get install openresty
- # CentOS
- yum install openresty
复制代码- sudo apt-get install redis-server # Debian系
- sudo yum install redis # RedHat系
复制代码 二、Nginx配置
- 主配置文件(nginx.conf)在块中添加共享内存和Lua脚本路径:
- http {
- lua_package_path "/usr/local/openresty/lualib/?.lua;;";
- lua_shared_dict ip_limit 10m; # 共享内存区
- server {
- listen 80;
- server_name _;
- location / {
- access_by_lua_file /usr/local/lua/ip_block.lua; # 核心拦截脚本
- root /var/www/html;
- }
- }
- }
复制代码 三、Lua脚本实现动态拦截
- 脚本路径创建Lua脚本:
- /usr/local/lua/ip_block.lua
复制代码 - 脚本内容
- local redis = require "resty.redis"
- local red = redis:new()
- -- Redis连接参数
- local redis_host = "127.0.0.1"
- local redis_port = 6379
- local redis_timeout = 1000 -- 毫秒
- local redis_auth = nil -- 无密码留空
- -- 拦截参数
- local block_time = 86400 -- 封禁时间(1天)
- local time_window = 60 -- 统计窗口(1分钟)
- local max_requests = 60 -- 最大请求数
- -- 获取客户端IP
- local function get_client_ip()
- local headers = ngx.req.get_headers()
- return headers["X-Real-IP"] or headers["x_forwarded_for"] or ngx.var.remote_addr
- end
- -- 连接Redis
- local function connect_redis()
- red:set_timeout(redis_timeout)
- local ok, err = red:connect(redis_host, redis_port)
- if not ok then
- ngx.log(ngx.ERR, "Redis连接失败: ", err)
- return nil
- end
- if redis_auth then
- local ok, err = red:auth(redis_auth)
- if not ok then ngx.log(ngx.ERR, "Redis认证失败: ", err) end
- end
- return ok
- end
- -- 主逻辑
- local client_ip = get_client_ip()
- local counter_key = "limit:count:" .. client_ip
- local block_key = "limit:block:" .. client_ip
- -- 检查是否已封禁
- local is_blocked, err = red:get(block_key)
- if tonumber(is_blocked) == 1 then
- ngx.exit(ngx.HTTP_FORBIDDEN) -- 直接返回403
- end
- -- 统计请求次数
- connect_redis()
- local current_count = red:incr(counter_key)
- if current_count == 1 then
- red:expire(counter_key, time_window) -- 首次设置过期时间
- end
- -- 触发封禁条件
- if current_count > max_requests then
- red:setex(block_key, block_time, 1) -- 封禁并设置1天过期
- red:del(counter_key) -- 删除计数器
- ngx.exit(ngx.HTTP_FORBIDDEN)
- end
- -- 释放Redis连接
- red:set_keepalive(10000, 100)
复制代码 四、性能优化
- Redis连接池通过复用连接,避免频繁建立TCP连接
- 共享内存缓存使用缓存高频访问IP,减少Redis查询压力
- 异步日志记录封禁操作异步写入日志文件,避免阻塞请求处理:
- ngx.timer.at(0, function()
- local log_msg = string.format("%s - IP %s blocked at %s",
- ngx.var.host, client_ip, ngx.localtime())
- local log_file = io.open("/var/log/nginx/blocked_ips.log", "a")
- log_file:write(log_msg, "\n")
- log_file:close()
- end)
复制代码 五、验证与测试
- # 模拟高频请求
- ab -n 100 -c 10 http://your-domain.com/
- # 检查Redis
- redis-cli keys "limit:block:*"
复制代码 等待24小时后检查封禁IP是否自动删除:- redis-cli ttl "limit:block:1.2.3.4" # 返回剩余秒数
复制代码 六、扩展方案
- 分布式封禁
在多台Nginx服务器间共享Redis黑名单,实现集群级拦截
- 可视化监控
通过Grafana+Prometheus展示实时拦截数据:
- # 采集Redis指标
- prometheus-redis-exporter --redis.address=localhost:6379
复制代码
- 动态调整阈值
通过Redis Hash存储不同路径的拦截规则:
- local rule_key = "limit:rule:" .. ngx.var.uri
- local custom_rule = red:hget(rule_key, "max_requests")
复制代码 引用说明
- 核心拦截逻辑参考了Nginx+Lua+Redis的经典架构设计
- Redis键过期机制确保自动解封
- 性能优化方案借鉴了OpenResty最佳实践
以上就是Nginx实现动态拦截非法访问ip的方法的详细内容,更多关于Nginx动态拦截IP的资料请关注脚本之家其它相关文章!
来源:互联网
免责声明:如果侵犯了您的权益,请联系站长(1277306191@qq.com),我们会及时删除侵权内容,谢谢合作! |
|