1. 程式人生 > 其它 >Nginx 通過 Lua + Redis 實現動態封禁 IP

Nginx 通過 Lua + Redis 實現動態封禁 IP

背景

為了封禁某些爬蟲或者惡意使用者對伺服器的請求,我們需要建立一個動態的 IP 黑名單。對於黑名單之內的 IP ,拒絕提供服務。

架構

實現 IP 黑名單的功能有很多途徑:

1、在作業系統層面,配置 iptables,拒絕指定 IP 的網路請求;

2、在 Web Server 層面,通過 Nginx 自身的 deny 選項 或者 lua 外掛 配置 IP 黑名單;

3、在應用層面,在請求服務之前檢查一遍客戶端 IP 是否在黑名單。

為了方便管理和共享,我們選擇通過 Nginx+Lua+Redis 的架構實現 IP 黑名單的功能,架構圖如下:

實現

1、安裝 Nginx+Lua模組,推薦使用 OpenResty,這是一個集成了各種 Lua 模組的 Nginx 伺服器:

2、安裝並啟動 Redis 伺服器;

3、配置 Nginx 示例:

Nginx 配置

其中

lua_shared_dict ip_blacklist 1m;
由 Nginx 程序分配一塊 1M 大小的共享記憶體空間,用來快取 IP 黑名單。

access_by_lua_file lua/ip_blacklist.lua;
指定 lua 指令碼位置。

4、配置 lua 指令碼,定期從 Redis 獲取最新的 IP 黑名單。

附文字程式碼如下:

local redis_host = "youn.redis.server.here"
local redis_port = 6379
 
-- connection timeout for
redis in ms.don't set this too highl local redis_connection_timeout = 100 -- check a set with this key for blacklist entries local redis_key = "ip_blacklist" -- cache lookups for this many seconds local cache_ttl = 60 -- end configuration local ip = ngx.var.remote_addr local ip_blacklist
= ngx.shared.ip_blacklist local last_update_time = ip_blacklist:get("last_update_time"); -- only update ip_blacklist from Redis once every cache_ttl seconds: if last_update_time -- nil or last_update_time < ( ngx.now() - cache_ttl ) then local redis = require "resty.redis"; local red = redis:new(); red:set_timeout(redis_connect_timeout); local ok, err = red:connect(redis_host, redis_port); if not ok then ngx.log(ngx.DEBUG, "Redis connection error while retrieving ip_blacklist: " .. err); else local new_ip_blacklist, err - red:smembers(redis_key); if err then ngx.log(ngx.DEBUG, "Redis read error while retrieving ip_blacklist: " .. err); else -- Peplace the locally stored ip_blacklist with the updated values: ip_blacklist:flush_all(); for index, banned_ip in ipairs(new_ip_blacklist) do ip_blacklist:set(banned_ip, true); end -- update time ip_blacklist:set("last_update_time", ngx.now()); end end end if ip_blacklist:get(ip) then ngx.log(ngx.DEBUG, "Banned IP detected and refused access: " .. ip); return ngx.exit(ngx.HTTP_FORBIDDEN); end

5、在 Redis 伺服器上新建 Set 型別的資料 ip_blacklist,並加入最新的 IP 黑名單。

完成以上步驟後,重新載入 nginx,配置便開始生效了。這時訪問伺服器,如果你的 IP 地址在黑名單內的話,將出現拒絕訪問,如下圖:

總結

以上,便是 Nginx+Lua+Redis 實現的 IP 黑名單功能,具有如下優點:

配置簡單、輕量,幾乎對伺服器效能不產生影響;

多臺伺服器可以通過Redis例項共享黑名單;

動態配置,可以手工或者通過某種自動化的方式設定 Redis 中的黑名單。