Nginx實(shí)現(xiàn)動(dòng)態(tài)攔截非法訪問(wèn)ip的方法
背景:訪問(wèn)時(shí)不時(shí)會(huì)被暴力刷量,爬蟲(chóng)和惡意攻擊導(dǎo)致數(shù)據(jù)庫(kù),服務(wù)等癱瘓
需求:在Nginx上實(shí)現(xiàn)一個(gè)動(dòng)態(tài)攔截IP的方法,具體是當(dāng)某個(gè)IP在1分鐘內(nèi)訪問(wèn)超過(guò)60次時(shí),將其加入Redis并攔截,攔截時(shí)間默認(rèn)1天。
技術(shù)選型:使用Nginx+Lua+Redis的方法。這種方案通過(guò)Lua腳本在Nginx處理請(qǐng)求時(shí)檢查Redis中的黑名單,同時(shí)統(tǒng)計(jì)訪問(wèn)頻率,超過(guò)閾值就封禁。這應(yīng)該符合用戶(hù)的需求。
需要結(jié)合Lua腳本和Redis的計(jì)數(shù)功能。安裝OpenResty,配置Nginx的Lua模塊,編寫(xiě)Lua腳本統(tǒng)計(jì)訪問(wèn)次數(shù),使用Redis存儲(chǔ)和過(guò)期鍵,以及設(shè)置攔截邏輯。連接池的使用,避免頻繁連接Redis影響性能。
一、環(huán)境準(zhǔn)備
- 安裝OpenResty
OpenResty集成了Nginx和Lua模塊,支持直接運(yùn)行Lua腳本:
# Ubuntu/Debian sudo apt-get install openresty # CentOS yum install openresty
- 安裝Redis服務(wù)
sudo apt-get install redis-server # Debian系 sudo yum install redis # RedHat系
二、Nginx配置
主配置文件(nginx.conf)在
http
塊中添加共享內(nèi)存和Lua腳本路徑:
http { lua_package_path "/usr/local/openresty/lualib/?.lua;;"; lua_shared_dict ip_limit 10m; # 共享內(nèi)存區(qū) server { listen 80; server_name _; location / { access_by_lua_file /usr/local/lua/ip_block.lua; # 核心攔截腳本 root /var/www/html; } } }
三、Lua腳本實(shí)現(xiàn)動(dòng)態(tài)攔截
腳本路徑創(chuàng)建Lua腳本:
/usr/local/lua/ip_block.lua
腳本內(nèi)容
local redis = require "resty.redis" local red = redis:new() -- Redis連接參數(shù) local redis_host = "127.0.0.1" local redis_port = 6379 local redis_timeout = 1000 -- 毫秒 local redis_auth = nil -- 無(wú)密碼留空 -- 攔截參數(shù) local block_time = 86400 -- 封禁時(shí)間(1天) local time_window = 60 -- 統(tǒng)計(jì)窗口(1分鐘) local max_requests = 60 -- 最大請(qǐng)求數(shù) -- 獲取客戶(hù)端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 connect_redis() red:set_timeout(redis_timeout) local ok, err = red:connect(redis_host, redis_port) if not ok then ngx.log(ngx.ERR, "Redis連接失敗: ", err) return nil end if redis_auth then local ok, err = red:auth(redis_auth) if not ok then ngx.log(ngx.ERR, "Redis認(rèn)證失敗: ", err) end end return ok end -- 主邏輯 local client_ip = get_client_ip() local counter_key = "limit:count:" .. client_ip local block_key = "limit:block:" .. client_ip -- 檢查是否已封禁 local is_blocked, err = red:get(block_key) if tonumber(is_blocked) == 1 then ngx.exit(ngx.HTTP_FORBIDDEN) -- 直接返回403 end -- 統(tǒng)計(jì)請(qǐng)求次數(shù) connect_redis() local current_count = red:incr(counter_key) if current_count == 1 then red:expire(counter_key, time_window) -- 首次設(shè)置過(guò)期時(shí)間 end -- 觸發(fā)封禁條件 if current_count > max_requests then red:setex(block_key, block_time, 1) -- 封禁并設(shè)置1天過(guò)期 red:del(counter_key) -- 刪除計(jì)數(shù)器 ngx.exit(ngx.HTTP_FORBIDDEN) end -- 釋放Redis連接 red:set_keepalive(10000, 100)
四、性能優(yōu)化
- Redis連接池通過(guò)
set_keepalive
復(fù)用連接,避免頻繁建立TCP連接
- 共享內(nèi)存緩存使用
lua_shared_dict
緩存高頻訪問(wèn)IP,減少Redis查詢(xún)壓力
異步日志記錄封禁操作異步寫(xiě)入日志文件,避免阻塞請(qǐng)求處理:
ngx.timer.at(0, function() local log_msg = string.format("%s - IP %s blocked at %s", ngx.var.host, client_ip, ngx.localtime()) local log_file = io.open("/var/log/nginx/blocked_ips.log", "a") log_file:write(log_msg, "\n") log_file:close() end)
五、驗(yàn)證與測(cè)試
- 手動(dòng)觸發(fā)封禁
# 模擬高頻請(qǐng)求 ab -n 100 -c 10 http://your-domain.com/ # 檢查Redis redis-cli keys "limit:block:*"
- 自動(dòng)解封驗(yàn)證
等待24小時(shí)后檢查封禁IP是否自動(dòng)刪除:
redis-cli ttl "limit:block:1.2.3.4" # 返回剩余秒數(shù)
六、擴(kuò)展方案
- 分布式封禁
在多臺(tái)Nginx服務(wù)器間共享Redis黑名單,實(shí)現(xiàn)集群級(jí)攔截 可視化監(jiān)控
通過(guò)Grafana+Prometheus展示實(shí)時(shí)攔截?cái)?shù)據(jù):
# 采集Redis指標(biāo) prometheus-redis-exporter --redis.address=localhost:6379
- 動(dòng)態(tài)調(diào)整閾值
通過(guò)Redis Hash存儲(chǔ)不同路徑的攔截規(guī)則:
local rule_key = "limit:rule:" .. ngx.var.uri local custom_rule = red:hget(rule_key, "max_requests")
引用說(shuō)明
- 核心攔截邏輯參考了Nginx+Lua+Redis的經(jīng)典架構(gòu)設(shè)計(jì)
- Redis鍵過(guò)期機(jī)制確保自動(dòng)解封
- 性能優(yōu)化方案借鑒了OpenResty最佳實(shí)踐
以上就是Nginx實(shí)現(xiàn)動(dòng)態(tài)攔截非法訪問(wèn)ip的方法的詳細(xì)內(nèi)容,更多關(guān)于Nginx動(dòng)態(tài)攔截IP的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Nginx與Lua灰度發(fā)布的實(shí)現(xiàn)
這篇文章主要介紹了Nginx與Lua灰度發(fā)布的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03Nginx的流式響應(yīng)配置實(shí)現(xiàn)小結(jié)
nginx是一款自由的、開(kāi)源的、高性能的HTTP服務(wù)器和反向代理服務(wù)器,本文主要介紹了Nginx的流式響應(yīng)配置實(shí)現(xiàn)小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04CentOS 7.0下nginx實(shí)現(xiàn)每天定時(shí)分割日志
大家都知道Nginx產(chǎn)生的日志都是存在一個(gè)文件,隨著網(wǎng)站運(yùn)行時(shí)間越長(zhǎng),日志文件的大小也在不斷增長(zhǎng),所以這個(gè)時(shí)候就需要實(shí)現(xiàn)定時(shí)分割,這篇文章主要介紹了在CentOS 7.0下nginx實(shí)現(xiàn)每天定時(shí)分割日志的相關(guān)資料,需要的朋友可以參考下。2017-04-04通過(guò)nginx實(shí)現(xiàn)訪問(wèn)服務(wù)器指定目錄下圖片資源
這篇文章為大家詳細(xì)主要介紹了如何通過(guò)nginx實(shí)現(xiàn)訪問(wèn)服務(wù)器指定目錄下圖片資源,文中通過(guò)圖文進(jìn)行了詳細(xì)的講解,有需要的小伙伴可以了解下2023-10-10nginx報(bào)錯(cuò)connect() failed(111: Connection refus
本文主要介紹了nginx報(bào)錯(cuò)connect() failed(111: Connection refused)while connecting to upstream解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06