• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    Nginx实现动态拦截非法访问ip的方法

    发布者: 皮3591 | 发布时间: 2025-8-16 15:58| 查看数: 62| 评论数: 0|帖子模式

    背景:访问时不时会被暴力刷量,爬虫和恶意攻击导致数据库,服务等瘫痪
    需求:在Nginx上实现一个动态拦截IP的方法,具体是当某个IP在1分钟内访问超过60次时,将其加入Redis并拦截,拦截时间默认1天。
    技术选型:使用Nginx+Lua+Redis的方法。这种方案通过Lua脚本在Nginx处理请求时检查Redis中的黑名单,同时统计访问频率,超过阈值就封禁。这应该符合用户的需求。
    需要结合Lua脚本和Redis的计数功能。安装OpenResty,配置Nginx的Lua模块,编写Lua脚本统计访问次数,使用Redis存储和过期键,以及设置拦截逻辑。连接池的使用,避免频繁连接Redis影响性能。

    一、环境准备


    • 安装OpenResty
    OpenResty集成了Nginx和Lua模块,支持直接运行Lua脚本:
    1. # Ubuntu/Debian
    2. sudo apt-get install openresty
    3. # CentOS
    4. yum install openresty
    复制代码

    • 安装Redis服务
    1. sudo apt-get install redis-server  # Debian系
    2. sudo yum install redis             # RedHat系
    复制代码
    二、Nginx配置


    • 主配置文件(nginx.conf)
      1. http
      复制代码
      块中添加共享内存和Lua脚本路径:
    1. http {
    2.     lua_package_path "/usr/local/openresty/lualib/?.lua;;";
    3.     lua_shared_dict ip_limit 10m;  # 共享内存区

    4.     server {
    5.         listen 80;
    6.         server_name _;

    7.         location / {
    8.             access_by_lua_file /usr/local/lua/ip_block.lua;  # 核心拦截脚本
    9.             root /var/www/html;
    10.         }
    11.     }
    12. }
    复制代码
    三、Lua脚本实现动态拦截


    • 脚本路径创建Lua脚本:
      1. /usr/local/lua/ip_block.lua
      复制代码
    • 脚本内容
    1. local redis = require "resty.redis"
    2. local red = redis:new()

    3. -- Redis连接参数
    4. local redis_host = "127.0.0.1"
    5. local redis_port = 6379
    6. local redis_timeout = 1000  -- 毫秒
    7. local redis_auth = nil       -- 无密码留空

    8. -- 拦截参数
    9. local block_time = 86400     -- 封禁时间(1天)
    10. local time_window = 60       -- 统计窗口(1分钟)
    11. local max_requests = 60     -- 最大请求数

    12. -- 获取客户端IP
    13. local function get_client_ip()
    14.     local headers = ngx.req.get_headers()
    15.     return headers["X-Real-IP"] or headers["x_forwarded_for"] or ngx.var.remote_addr
    16. end

    17. -- 连接Redis
    18. local function connect_redis()
    19.     red:set_timeout(redis_timeout)
    20.     local ok, err = red:connect(redis_host, redis_port)
    21.     if not ok then
    22.         ngx.log(ngx.ERR, "Redis连接失败: ", err)
    23.         return nil
    24.     end
    25.     if redis_auth then
    26.         local ok, err = red:auth(redis_auth)
    27.         if not ok then ngx.log(ngx.ERR, "Redis认证失败: ", err) end
    28.     end
    29.     return ok
    30. end

    31. -- 主逻辑
    32. local client_ip = get_client_ip()
    33. local counter_key = "limit:count:" .. client_ip
    34. local block_key = "limit:block:" .. client_ip

    35. -- 检查是否已封禁
    36. local is_blocked, err = red:get(block_key)
    37. if tonumber(is_blocked) == 1 then
    38.     ngx.exit(ngx.HTTP_FORBIDDEN)  -- 直接返回403
    39. end

    40. -- 统计请求次数
    41. connect_redis()
    42. local current_count = red:incr(counter_key)
    43. if current_count == 1 then
    44.     red:expire(counter_key, time_window)  -- 首次设置过期时间
    45. end

    46. -- 触发封禁条件
    47. if current_count > max_requests then
    48.     red:setex(block_key, block_time, 1)   -- 封禁并设置1天过期
    49.     red:del(counter_key)                  -- 删除计数器
    50.     ngx.exit(ngx.HTTP_FORBIDDEN)
    51. end

    52. -- 释放Redis连接
    53. red:set_keepalive(10000, 100)
    复制代码
    四、性能优化


    • Redis连接池通过
      1. set_keepalive
      复制代码
      复用连接,避免频繁建立TCP连接


    • 共享内存缓存使用
      1. lua_shared_dict
      复制代码
      缓存高频访问IP,减少Redis查询压力


    • 异步日志记录封禁操作异步写入日志文件,避免阻塞请求处理:
    1. ngx.timer.at(0, function()
    2.     local log_msg = string.format("%s - IP %s blocked at %s",
    3.         ngx.var.host, client_ip, ngx.localtime())
    4.     local log_file = io.open("/var/log/nginx/blocked_ips.log", "a")
    5.     log_file:write(log_msg, "\n")
    6.     log_file:close()
    7. end)
    复制代码
    五、验证与测试


    • 手动触发封禁
    1. # 模拟高频请求
    2. ab -n 100 -c 10 http://your-domain.com/
    3. # 检查Redis
    4. redis-cli keys "limit:block:*"
    复制代码

    • 自动解封验证
    等待24小时后检查封禁IP是否自动删除:
    1. redis-cli ttl "limit:block:1.2.3.4"  # 返回剩余秒数
    复制代码
    六、扩展方案


    • 分布式封禁
      在多台Nginx服务器间共享Redis黑名单,实现集群级拦截
    • 可视化监控
      通过Grafana+Prometheus展示实时拦截数据:
    1. # 采集Redis指标
    2. prometheus-redis-exporter --redis.address=localhost:6379
    复制代码

    • 动态调整阈值
      通过Redis Hash存储不同路径的拦截规则:
    1. local rule_key = "limit:rule:" .. ngx.var.uri
    2. local custom_rule = red:hget(rule_key, "max_requests")
    复制代码
    引用说明


    • 核心拦截逻辑参考了Nginx+Lua+Redis的经典架构设计


    • Redis键过期机制确保自动解封
    • 性能优化方案借鉴了OpenResty最佳实践
    以上就是Nginx实现动态拦截非法访问ip的方法的详细内容,更多关于Nginx动态拦截IP的资料请关注脚本之家其它相关文章!

    来源:互联网
    免责声明:如果侵犯了您的权益,请联系站长(1277306191@qq.com),我们会及时删除侵权内容,谢谢合作!

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表