Nginx實現(xiàn)動態(tài)攔截非法訪問ip的方法
背景:訪問時不時會被暴力刷量,爬蟲和惡意攻擊導致數(shù)據庫,服務等癱瘓
需求:在Nginx上實現(xiàn)一個動態(tài)攔截IP的方法,具體是當某個IP在1分鐘內訪問超過60次時,將其加入Redis并攔截,攔截時間默認1天。
技術選型:使用Nginx+Lua+Redis的方法。這種方案通過Lua腳本在Nginx處理請求時檢查Redis中的黑名單,同時統(tǒng)計訪問頻率,超過閾值就封禁。這應該符合用戶的需求。
需要結合Lua腳本和Redis的計數(shù)功能。安裝OpenResty,配置Nginx的Lua模塊,編寫Lua腳本統(tǒng)計訪問次數(shù),使用Redis存儲和過期鍵,以及設置攔截邏輯。連接池的使用,避免頻繁連接Redis影響性能。
一、環(huán)境準備
- 安裝OpenResty
OpenResty集成了Nginx和Lua模塊,支持直接運行Lua腳本:
# Ubuntu/Debian sudo apt-get install openresty # CentOS yum install openresty
- 安裝Redis服務
sudo apt-get install redis-server # Debian系 sudo yum install redis # RedHat系
二、Nginx配置
主配置文件(nginx.conf)在
http塊中添加共享內存和Lua腳本路徑:
http {
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_shared_dict ip_limit 10m; # 共享內存區(qū)
server {
listen 80;
server_name _;
location / {
access_by_lua_file /usr/local/lua/ip_block.lua; # 核心攔截腳本
root /var/www/html;
}
}
}
三、Lua腳本實現(xiàn)動態(tài)攔截
腳本路徑創(chuàng)建Lua腳本:
/usr/local/lua/ip_block.lua腳本內容
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 -- 無密碼留空
-- 攔截參數(shù)
local block_time = 86400 -- 封禁時間(1天)
local time_window = 60 -- 統(tǒng)計窗口(1分鐘)
local max_requests = 60 -- 最大請求數(shù)
-- 獲取客戶端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認證失敗: ", 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)計請求次數(shù)
connect_redis()
local current_count = red:incr(counter_key)
if current_count == 1 then
red:expire(counter_key, time_window) -- 首次設置過期時間
end
-- 觸發(fā)封禁條件
if current_count > max_requests then
red:setex(block_key, block_time, 1) -- 封禁并設置1天過期
red:del(counter_key) -- 刪除計數(shù)器
ngx.exit(ngx.HTTP_FORBIDDEN)
end
-- 釋放Redis連接
red:set_keepalive(10000, 100)
四、性能優(yōu)化
- Redis連接池通過
set_keepalive復用連接,避免頻繁建立TCP連接
- 共享內存緩存使用
lua_shared_dict緩存高頻訪問IP,減少Redis查詢壓力
異步日志記錄封禁操作異步寫入日志文件,避免阻塞請求處理:
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)
五、驗證與測試
- 手動觸發(fā)封禁
# 模擬高頻請求 ab -n 100 -c 10 http://your-domain.com/ # 檢查Redis redis-cli keys "limit:block:*"
- 自動解封驗證
等待24小時后檢查封禁IP是否自動刪除:
redis-cli ttl "limit:block:1.2.3.4" # 返回剩余秒數(shù)
六、擴展方案
- 分布式封禁
在多臺Nginx服務器間共享Redis黑名單,實現(xiàn)集群級攔截 可視化監(jiān)控
通過Grafana+Prometheus展示實時攔截數(shù)據:
# 采集Redis指標 prometheus-redis-exporter --redis.address=localhost:6379
- 動態(tài)調整閾值
通過Redis Hash存儲不同路徑的攔截規(guī)則:
local rule_key = "limit:rule:" .. ngx.var.uri local custom_rule = red:hget(rule_key, "max_requests")
引用說明
- 核心攔截邏輯參考了Nginx+Lua+Redis的經典架構設計
- Redis鍵過期機制確保自動解封
- 性能優(yōu)化方案借鑒了OpenResty最佳實踐
以上就是Nginx實現(xiàn)動態(tài)攔截非法訪問ip的方法的詳細內容,更多關于Nginx動態(tài)攔截IP的資料請關注腳本之家其它相關文章!
相關文章
nginx配置域名(ssl和非ssl形式)的實現(xiàn)示例
本文主要介紹了nginx配置域名(ssl和非ssl形式)的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-07-07
在Linux中查看Apache或Nginx服務狀態(tài)的詳細步驟
在Linux中,查看Apache或Nginx服務的狀態(tài)通常涉及到使用系統(tǒng)管理工具或特定于這些Web服務器的命令,以下是如何查看Apache和Nginx服務狀態(tài)的詳細步驟,需要的朋友可以參考下2024-03-03
Apache和Nginx的優(yōu)缺點詳解_動力節(jié)點Java學院整理
Nginx和Apache一樣,都是HTTP服務器軟件,在功能實現(xiàn)上都采用模塊化結構設計,都支持通用的語言接口。下面通過本文給大家分享Apache和Nginx比較 功能對比,感興趣的朋友參考下吧2017-08-08

