0.openresty-1.25.3.2使用
环境:openresty-1.25.3.2 、redis-7.0.15
lua 脚本用途解释:
对于 1 分钟内访问超过 300 的 IP 进行封停 5 分钟
1.统计访问IP,redis 中: 1) "RATE_LIMIT:45.140.167.195"
2.将1 分钟超过 300 的 IP加入黑名单: 1) "BLACKLIST:34.174.231.221"
3.避免频繁访问 reids,设置为 10s访问一次,并将黑名单中的 IP先放置在 openresty所在服务器缓存中:
nginx.conf :lua_shared_dict ip_blacklist 10m;
yum install -y gperftools gperftools-devel perl-ExtUtils-Embed gcc make pcre-devel openssl-devel zlib-devel
git clone https://github.com/vozlt/nginx-module-vts.git ./configure --prefix=/usr/local/openresty-1.25.3.2 --with-http_ssl_module --with-compat --with-debug --with-file-aio --with-google_perftools_module --with-http_degradation_module --with-http_realip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_perl_module=dynamic --with-http_slice_module --with-http_stub_status_module --with-luajit --with-http_iconv_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-threads --with-http_gunzip_module --with-http_mp4_module --with-http_dav_module --with-http_sub_module --with-http_random_index_module --with-http_flv_module --with-http_auth_request_module --with-http_addition_module --with-http_v2_module --with-pcre --with-pcre-jit --without-http_redis2_module --with-http_geoip_module=dynamic --with-stream_geoip_module=dynamic --with-http_secure_link_module --with-mail --with-mail_ssl_module --with-http_postgres_module --add-module=/usr/local/src/openresty-1.25.3.2/ngx_cache_purge-2.3 --add-module=/usr/local/src/nginx-module-vtsgmake
gmake install
[root@ip-172-31-33-75 lua]# cat ip_count_noredispass_cache.lua -- 初始化模块 local redis = require "resty.redis" local ngx = ngx local shared_cache = ngx.shared.ip_blacklist -- nginx.conf中需配置共享内存 -- 配置参数 -- local REDIS_HOST = "rm-cache2.gnzg6z.ng.0001.apne1.cache.amazonaws.com" local REDIS_HOST = "172.31.32.167" local REDIS_PORT = 6379 local IP_BLOCK_TIME = 300 -- 封禁时间(秒) local RATE_LIMIT = 300 -- 单位时间请求限制 local TIME_WINDOW = 60 -- 时间窗口(秒) local SYNC_INTERVAL = 10 -- 同步间隔(秒) -- 获取客户端真实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 close_redis(red) if not red then return end local ok, err = red:set_keepalive(10000, 100) if not ok then ngx.log(ngx.ERR, "failed to set keepalive: ", err) red:close() end end -- 同步Redis黑名单到共享内存 local function sync_blacklist() local now = ngx.now() local last_sync = shared_cache:get("last_sync") or 0 if now - last_sync < SYNC_INTERVAL then return end local red = redis:new() red:set_timeout(2000) local ok, err = red:connect(REDIS_HOST, REDIS_PORT) if not ok then ngx.log(ngx.ERR, "failed to connect redis: ", err) return end -- 使用SCAN安全遍历 local cursor = "0" repeat local res, err = red:scan(cursor, "MATCH", "BLACKLIST:*") if not res then ngx.log(ngx.ERR, "scan failed: ", err) break end cursor = res[1] for _, key in ipairs(res[2]) do local ip = string.match(key, "BLACKLIST:(.+)") if ip then shared_cache:set(ip, true, IP_BLOCK_TIME) end end until cursor == "0" shared_cache:set("last_sync", now) close_redis(red) end -- 主处理逻辑 local function main() local client_ip = get_client_ip() -- 1. 检查本地黑名单 local message = "Your visit is too frequent. Please try again in 5 minutes." if shared_cache:get(client_ip) then ngx.exit(403) return end -- 2. 异步同步黑名单 ngx.timer.at(0, function(premature) if premature then return end sync_blacklist() end) -- 3. 频率控制 local red = redis:new() red:set_timeout(2000) local ok, err = red:connect(REDIS_HOST, REDIS_PORT) if not ok then ngx.log(ngx.ERR, "failed to connect: ", err) return close_redis(red) end -- 原子化操作 local key = "RATE_LIMIT:"..client_ip local current = red:incr(key) if current == 1 then red:expire(key, TIME_WINDOW) elseif current >= RATE_LIMIT then red:set("BLACKLIST:"..client_ip, 1) shared_cache:set(client_ip, true, IP_BLOCK_TIME) close_redis(red) ngx.exit(429) -- Too Many Requests end close_redis(red) end -- 执行主逻辑 main()
1.安装openresty-1.21.4.4
tar -xzvf openresty-1.21.4.4.tar.gz
cd openresty-1.21.4.4
mkdir modules
# 到 github中下载ngx_cache_purge-2.3,解压后放到 modules里面
wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz
# 编译安装 openresty
./configure --prefix=/usr/local/openresty-1.21.4.4 --with-http_ssl_module --with-compat --with-debug --with-file-aio --with-google_perftools_module --with-http_degradation_module --with-http_realip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_perl_module=dynamic --with-http_slice_module --with-http_stub_status_module --with-luajit --with-http_iconv_module --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-stream_realip_module --with-threads --with-http_gunzip_module --with-http_mp4_module --with-http_dav_module --with-http_sub_module --with-http_random_index_module --with-http_flv_module --with-http_auth_request_module --with-http_addition_module --with-http_v2_module --with-pcre --with-pcre-jit --without-http_redis2_module --with-http_geoip_module=dynamic --with-stream_geoip_module=dynamic --with-http_secure_link_module --with-mail --with-mail_ssl_module --with-http_postgres_module --add-module=/usr/local/src/openresty-1.21.4.4/ngx_cache_purge-2.3
gmake
gmake install
2.安装 redis
自己安装并设置密码
3.lua脚本设置
脚本网上一堆,自己测试下
cd /usr/local/openresty-1.21.4.4
mkdir lua
cat ipblock.lua
ip_bind_time = 30 --封禁IP多长时间
ip_time_out = 10 --指定统计ip访问频率时间范围
connect_count = 10 --指定ip访问频率计数最大值
--上面的意思就是10秒内访问超过10次,自动封 IP 30秒,实际测试下来 5 次后就封了,每次访问*2。
--连接redis
local redis = require "resty.redis"
local cache = redis.new()
local ok , err = cache.connect(cache,"10.1.1.240","6380")
-- redis密码
local res, err = cache:auth("wg1q2w3e")
cache:set_timeout(60000)
--如果连接失败,跳转到脚本结尾
if not ok then
goto Lastend
end
--查询ip是否在封禁段内,若在则返回403错误代码
--因封禁时间会大于ip记录时间,故此处不对ip时间key和计数key做处理
is_bind , err = cache:get("bind_"..ngx.var.remote_addr)
if is_bind == '1' then
ngx.exit(ngx.HTTP_FORBIDDEN)
-- 或者 ngx.exit(403)
-- 当然,你也可以返回500错误啥的,搞一个500页面,提示,亲您访问太频繁啥的。
goto Lastend
end
start_time , err = cache:get("time_"..ngx.var.remote_addr)
ip_count , err = cache:get("count_"..ngx.var.remote_addr)
--如果ip记录时间大于指定时间间隔或者记录时间或者不存在ip时间key则重置时间key和计数key
--如果ip时间key小于时间间隔,则ip计数+1,且如果ip计数大于ip频率计数,则设置ip的封禁key为1
--同时设置封禁key的过期时间为封禁ip的时间
if start_time == ngx.null or os.time() - start_time > ip_time_out then
res , err = cache:set("time_"..ngx.var.remote_addr , os.time())
res , err = cache:set("count_"..ngx.var.remote_addr , 1)
else
ip_count = ip_count + 1
res , err = cache:incr("count_"..ngx.var.remote_addr)
if ip_count >= connect_count then
res , err = cache:set("bind_"..ngx.var.remote_addr,1)
res , err = cache:expire("bind_"..ngx.var.remote_addr,ip_bind_time) --fix keys
end
end
--结尾标记
::Lastend::
local ok, err = cache:close()
另外一种方式,自己分析出 IP并加入到 redis中来禁止该 IP访问
-- ip_block.lua 核心逻辑 local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) -- 连接超时设置:ml-citation{ref="1" data="citationList"} local function get_client_ip() return ngx.var.http_x_forwarded_for or ngx.var.remote_addr end local ip = get_client_ip() local blacklist = ngx.shared.ip_blacklist -- 检查本地缓存 if blacklist:get(ip) then return ngx.exit(ngx.HTTP_FORBIDDEN) end -- Redis连接与验证 local ok, err = red:connect("10.1.1.240", 6379) if not ok then ngx.log(ngx.ERR, "Redis连接失败: ", err) return end -- 检查Redis黑名单 local is_banned, err = red:sismember("ip_blacklist", ip) if is_banned == 1 then blacklist:set(ip, true, 60) -- 缓存1小时:ml-citation{ref="1,4" data="citationList"} return ngx.exit(ngx.HTTP_FORBIDDEN) end -- 释放连接到连接池 red:set_keepalive(10000, 100) :ml-citation{ref="1" data="citationList"} ########## #添加封停IP redis-cli SADD ip_blacklist 1.2.3.4 redis-cli EXPIRE 1.2.3.4 86400 # 24小时后自动解封
4.openreststy启动
nginx.conf添加配置
http { lua_package_path "/usr/local/openresty/lualib/?.lua;;"; lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; # 其它参数,有可能用到 lua_shared_dict ip_blacklist 10m; lua_code_cache on; }
cat localhost.conf
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
access_by_lua_file "/usr/local/openresty-1.21.4.4/lua/ipblock.lua";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
access_log /data/logs/nginx/localhost_acc.log;
error_log /data/logs/nginx/localhost_err.log;
}
5.访问并查看 redis数据
http://IP 统计刷新次数,查看日志
查看 redis

浙公网安备 33010602011771号