nginx Ingress 限流Lua脚本

可用于生产的版本配置已经添加好并且已经测试过功能。

针对客户端IP对单个域名的分钟级访问。

ingress-nginx cm配置文件

添加下面的配置,主要作用是把xforwardfor的客户端真实IP地址传给remote_address,脚本中的限频就是根据这个客户端的真实IP地址来的,添加set-real-ip-from可以防止伪造。

rate_limit_dict在http字段中声明一个桶50m,用来存放每个请求的访问记录信息。

  http-snippet: |
    lua_shared_dict rate_limit_dict 50m;
  real-ip-header: X-Forwarded-For
  set-real-ip-from: |
    10.0.0.0/8
    192.168.0.0/16
    172.30.0.0/16
  use-forwarded-headers: "true"

ingress配置

脚本中可以设置白名单,每个请求会先检查客户端的IP地址,然后判断是否在白名单中,如果没有在白名单中则进行key的判断。首先判断"IP:domain"的字符串key在桶中的统计,如果不在就在字典中设置该key并指定生命周期值,如果在检查该key,在生命周期内的统计是否超过了限制的阈值。超过返回429,没有超过就把命中次数在桶中+1.

直接通过片段的方式叠加在想要的ingress注释中。

    nginx.ingress.kubernetes.io/configuration-snippet: |
      access_by_lua_block {
          local bit = require "bit"
          local function ip_to_int(ip)
              local o1, o2, o3, o4 = ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
              if not o1 then return nil end
              return bit.lshift(o1, 24)
                   + bit.lshift(o2, 16)
                   + bit.lshift(o3, 8)
                   + o4
          end
          local function cidr_match(ip, cidr)
              local base, mask = cidr:match("(.+)/(%d+)")
              if not base then return false end
              local ip_int   = ip_to_int(ip)
              local base_int = ip_to_int(base)
              local mask_num = tonumber(mask)
              if not ip_int or not base_int then return false end
              local mask_int = bit.lshift(0xffffffff, 32 - mask_num)
              return bit.band(ip_int, mask_int) == bit.band(base_int, mask_int)
          end
          local white_ips = {
              ["120.10.211.89"] = true,
              ["120.10.52.221"]  = true,
              ["110.10.83.75"]  = true,
              ["127.0.0.1"]  = true,
          }
          local white_cidrs = {
              "172.30.0.0/16",
              "192.168.0.0/16",
              "10.0.0.0/8",
          }
          local client_ip = ngx.var.remote_addr
          if white_ips[client_ip] then
              return
          end
          for _, cidr in ipairs(white_cidrs) do
              if cidr_match(client_ip, cidr) then
                  return
              end
          end
          local rate_limit_key = client_ip .. ":" .. ngx.var.host
          local limit_count = 1500
          local limit_time = 60
          local dict = ngx.shared.rate_limit_dict
          local current_count = dict:get(rate_limit_key)
          if not current_count then
              dict:set(rate_limit_key, 1, limit_time)
          elseif current_count >= limit_count then
              ngx.status = ngx.HTTP_TOO_MANY_REQUESTS
              ngx.header["Content-Type"] = "application/json"
              ngx.say('{"code":429,"msg":"Access too fast. Please try again later."}')
              ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS)
          else
              dict:incr(rate_limit_key, 1)
          end
      }
posted @ 2026-01-19 09:19  Gshelldon  阅读(2)  评论(0)    收藏  举报