Nginx + Lua + Redis 实现动态 IP 黑名单教程

一、背景

在当今的网络环境中,恶意攻击、爬虫滥用、DDoS 攻击等问题屡见不鲜,严重影响着服务器的安全和稳定运行。静态 IP 黑名单由于无法实时更新,难以应对不断变化的网络威胁。而采用 Nginx + Lua + Redis 组合实现的动态 IP 黑名单方案,能够实时更新黑名单,有效阻挡恶意 IP 的访问,为服务器安全保驾护航。本文将详细介绍这一方案的实现过程。​

二、架构选择

在这个动态 IP 黑名单方案中,Nginx、Lua 和 Redis 各自扮演着重要角色。

  • Nginx:作为高性能的 Web 服务器和反向代理服务器,能够高效处理大量并发请求。它负责接收客户端请求,并在处理请求前通过嵌入的 Lua 脚本检查客户端 IP 是否在黑名单中。​
  • Lua:一种轻量级脚本语言,具有简洁、高效的特点。嵌入 Nginx 后,可编写灵活的逻辑来实现 IP 检查、与 Redis 交互等功能,扩展 Nginx 的能力。​
  • Redis:高性能的 key-value 数据库,支持快速的读写操作,非常适合存储黑名单数据。它能实时存储和更新黑名单 IP,保证 Nginx 能快速查询到最新的黑名单信息。​

三、实现步骤

安装 OpenResty(含 Nginx 和 Lua 模块)​

OpenResty 是基于 Nginx 扩展的 Web 平台,内置了 Lua 模块,方便实现复杂业务逻辑。

1.添加 OpenResty 仓库:​

对于 CentOS 系统,执行以下命令:

yum install -y yum-utils
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

对于 Ubuntu 系统,执行以下命令:​

apt-get update
apt-get install -y software-properties-common
add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"
apt-get update

2.安装 OpenResty

CentOS 系统:yum install -y openresty​
Ubuntu 系统:apt-get install -y openresty​
安装完成后,启动 OpenResty 服务:systemctl start openresty,
并设置开机自启:systemctl enable openresty。

4.安装 Redis

  1. 对于 CentOS 系统,执行:yum install -y redis​
  2. 对于 Ubuntu 系统,执行:apt-get install -y redis-server​
  3. 启动 Redis 服务:systemctl start redis,设置开机自启:systemctl enable redis。​
  4. 验证 Redis 是否安装成功,执行 redis-cli ping,若返回 PONG,则表示安装成功。

      配置 Nginx​

      1. 找到 OpenResty 的 Nginx 配置文件,通常位于 /usr/local/openresty/nginx/conf/nginx.conf。​
      2. 在 http 块中添加以下配置,用于定义 Lua 脚本路径和 Redis 连接信息:
      lua_package_path "/usr/local/openresty/lualib/?.lua;;";
      lua_shared_dict ip_blacklist 10m;  # 定义共享内存,用于缓存黑名单,可选
      
      server {
          listen 80;
          server_name your_domain.com;  # 替换为你的域名或服务器 IP
      
          location / {
              access_by_lua_file /usr/local/openresty/nginx/conf/lua/ip_blacklist.lua;  # 指定 Lua 脚本路径
              proxy_pass http://backend_server;  # 替换为你的后端服务地址
          }
      }

      创建 Lua 脚本存放目录:mkdir -p /usr/local/openresty/nginx/conf/lua。​

      编写 Lua 脚本

      在 /usr/local/openresty/nginx/conf/lua 目录下创建 ip_blacklist.lua 脚本,内容如下:​

      -- 获取客户端 IP
      local client_ip = ngx.var.remote_addr
      if not client_ip then
          ngx.log(ngx.ERR, "无法获取客户端 IP")
          return ngx.exit(500)
      end
      
      -- 连接 Redis
      local redis = require "resty.redis"
      local red = redis:new()
      
      -- 设置 Redis 连接超时时间
      red:set_timeout(1000)  -- 1 秒
      
      -- 连接 Redis 服务器,替换为你的 Redis 地址和端口
      local ok, err = red:connect("127.0.0.1", 6379)
      if not ok then
          ngx.log(ngx.ERR, "连接 Redis 失败: ", err)
          -- 此处可根据需求决定是否允许请求继续,这里选择允许
          return
      end
      
      -- 检查 IP 是否在黑名单中,假设黑名单的 key 为 "ip_blacklist"
      local is_blacklisted, err = red:sismember("ip_blacklist", client_ip)
      if err then
          ngx.log(ngx.ERR, "查询 Redis 失败: ", err)
          red:close()
          return
      end
      
      -- 关闭 Redis 连接
      local ok, err = red:set_keepalive(10000, 100)  -- 设置连接池,10 秒超时,最多 100 个连接
      if not ok then
          ngx.log(ngx.ERR, "设置 Redis 连接池失败: ", err)
      end
      
      -- 如果 IP 在黑名单中,拒绝请求
      if is_blacklisted == 1 then
          ngx.log(ngx.WARN, "客户端 IP ", client_ip, " 在黑名单中,拒绝请求")
          return ngx.exit(403)
      end
      
      -- IP 不在黑名单中,允许请求继续
      ngx.log(ngx.INFO, "客户端 IP ", client_ip, " 不在黑名单中,允许请求")

      代码解释:​

      • 首先获取客户端 IP 地址,如果获取失败则记录错误并返回 500 错误。​
      • 然后创建 Redis 连接对象,设置连接超时时间并连接 Redis 服务器。​
      • 连接成功后,使用 sismember 命令查询客户端 IP 是否在名为 “ip_blacklist” 的集合中。​
      • 操作完成后,将 Redis 连接放入连接池,提高性能。​
      • 如果 IP 在黑名单中,返回 403 错误拒绝请求;否则允许请求继续处理。

      Redis 黑名单管理​

      添加黑名单IP​
      使用 Redis 命令往 “ip_blacklist” 集合中添加 IP:

      redis-cli
      sadd ip_blacklist 192.168.1.100  # 添加单个 IP
      sadd ip_blacklist 192.168.1.101 192.168.1.102  # 添加多个 IP

      删除黑名单IP​

      redis-cli
      srem ip_blacklist 192.168.1.100  # 删除单个 IP

      查看黑名单IP​

      redis-cli
      smembers ip_blacklist  # 查看所有黑名单 IP
      scard ip_blacklist  # 查看黑名单 IP 数量

      设置黑名单过期(可选)​
      如果需要让黑名单 IP 在一段时间后自动失效,可以结合 Redis 的过期键功能。例如,单独记录每个 IP 的过期时间,定期清理:

      # 添加 IP 时设置过期时间为 1 小时(3600 秒)
      set ip:192.168.1.100 blacklisted EX 3600
      # 定期执行脚本将过期的 IP 从黑名单集合中移除

      测试验证

      正常IP测试
      使用客户端 IP 不在黑名单中的机器,执行 curl http://your_domain.com,应能正常获取响应。​

      黑名单IP测试​

      1. 将测试机器的 IP 添加到黑名单:redis-cli sadd ip_blacklist 测试机器IP​
      2. 执行 curl http://your_domain.com,应返回 403 Forbidden 错误。​
      3. 查看 Nginx 日志(通常位于 /usr/local/openresty/nginx/logs/error.log),应有类似 “客户端 IP 测试机器 IP 在黑名单中,拒绝请求” 的记录。​
      4. 从黑名单中移除该 IP:redis-cli srem ip_blacklist 测试机器IP​
      5. 再次执行 curl http://your_domain.com,应能正常获取响应。

              注意事项​

              • Redis 高可用性:在生产环境中,应使用 Redis 集群或主从复制,确保 Redis 服务的高可用,避免因 Redis 故障导致整个黑名单功能失效。​
              • 性能优化:可利用 Nginx 的共享内存缓存黑名单数据,减少对 Redis 的频繁查询,提高性能。但要注意缓存更新策略,保证数据一致性。​
              • Lua 脚本安全:Lua 脚本具有较高的权限,编写时要注意输入验证,防止注入攻击。​
              • 黑名单更新频率:根据实际业务需求调整黑名单的更新频率,既要及时阻挡恶意 IP,又要避免频繁操作影响性能。​
              • 日志监控:定期查看 Nginx 和 Redis 的日志,及时发现异常情况,如频繁的黑名单拦截、Redis 连接失败等。

              总结​

              通过 Nginx + Lua + Redis 实现动态 IP 黑名单,充分利用了 Nginx 的高性能、Lua 的灵活性和 Redis 的快速读写能力,能够实时、高效地阻挡恶意 IP 的访问。该方案易于实现和扩展,可根据实际需求进行定制,如结合防火墙、入侵检测系统等进一步增强服务器的安全性。在实际应用中,需注意各组件的配置优化和高可用性保障,确保方案稳定可靠运行。

              未经允许不得转载:云端研习社 » Nginx + Lua + Redis 实现动态 IP 黑名单教程