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
}

浙公网安备 33010602011771号