最开始参考 https://blog.csdn.net/sdmei/article/details/88320516 这篇文章做的,但是因为几个组件的版本总是出问题,就使用了OpenResty,总算是可以用了,记录下来可供大家参考;
安装OpenResty
中文官网
安装很简单,我的编译配置:
./configure --with-http_stub_status_module --with-http_v2_module --with-http_ssl_module --with-stream --with-ipv6 --with-http_gzip_static_module --with-http_realip_module --with-http_flv_module
不需要指定目录,默认是/usr/local/openresty,是我习惯使用的目录;
配置nginx
首先在conf目录中添加/lua文件夹,新建三个lua文件:
- 检测IP 的脚本 check_realip.lua
if ngx.shared.ip_blacklist:get(ngx.var.real_ip) then
return ngx.exit(ngx.HTTP_FORBIDDEN);
- 获取黑名单信息 get_ipblacklist_info.lua
require "resty.core.shdict"
ngx.say("total space: " .. ngx.shared.ip_blacklist:capacity() .. "<br/>");
ngx.say("free space: " .. ngx.shared.ip_blacklist:free_space() .. "<br/>");
ngx.say("last update time: " .. os.date("%Y-%m-%d %H:%M:%S",ngx.shared.ip_blacklist:get("last_update_time")) .. "<br/>");
ngx.say("first 100 keys: <br/>");
ngx.say("--------------------------<br/>");
ip_blacklist = ngx.shared.ip_blacklist:get_keys(100);
for key, value in pairs(ip_blacklist) do
ngx.say(key .. ": " .. value .. "<br/>");
- 同步ip黑名单 sync_ipblacklist.lua
local mysql_host = "192.168.50.8"
local mysql_port = 3306
local database = "tes_ip"
local username = "root"
local password = "123456"
-- update ip_blacklist from mysql once every cache_ttl seconds
local cache_ttl = 1
local mysql_connection_timeout = 5000
local client_ip = ngx.var.real_ip
local ip_blacklist = ngx.shared.ip_blacklist
local last_update_time = ip_blacklist:get("last_update_time");
if last_update_time == nil or last_update_time < ( ngx.now() - cache_ttl ) then
ngx.say("will conn mysql");
local mysql = require "resty.mysql";
local red = mysql:new();
red:set_timeout(mysql_connect_timeout);
local ok, err, errcode, sqlstate = red:connect{
host = mysql_host,
port = mysql_port,
database = database,
user = username,
password = password,
charset = "utf8",
max_packet_size = 1024 * 1024,
}
if not ok then
ngx.log(ngx.ERR, "mysql connection error while retrieving ip_blacklist: " .. err);
else
new_ip_blacklist, err, errcode, sqlstate = red:query("select ip_addr from ip_blacklist where status = 0 order by create_time desc limit 10000", 100)
if not new_ip_blacklist then
ngx.log(ngx.ERR, "bad result. errcode: " .. errcode .. " sqlstate: " .. sqlstate .. " err: " .. err);
return
end
ip_blacklist:flush_all();
for k1, v1 in pairs(new_ip_blacklist) do
for k2, v2 in pairs(v1) do
ip_blacklist:set(v2,true);
end
end
ip_blacklist:set("last_update_time", ngx.now());
end
end
ngx.say("sync successful");
然后就是配置 nginx.conf
server {
listen 80;
server_name localhost;
set $real_ip $remote_addr;
if ( $http_x_forwarded_for ~ "^(\d+\.\d+\.\d+\.\d+)" ) {
set $real_ip $1;
}
# 管理信息,访问该URL可以查看nginx中的IP黑名单信息
location /get-ipblacklist-info {
access_by_lua_file conf/lua/get_ipblacklist_info.lua;
}
# 同步URL,通过定时任务调用该URL,实现IP黑名单从mysql到nginx的定时刷新
location /sync-ipblacklist {
access_by_lua_file conf/lua/sync_ipblacklist.lua;
}
# 生产域名配置,所有需要IP黑名单控制的location,都要包含以下语句
location / {
access_by_lua_file conf/lua/check_realip.lua;
proxy_pass http://test_server;
proxy_read_timeout 60s;
proxy_set_header Host $http_host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-for $remote_addr;
proxy_http_version 1.1;
}
}
启动 nginx !
这里从数据库到nginx的数据同步是通过 /sync-ipblacklist 触发的,所以可以使用定时器定时同步进行
例如可以添加一个linux定时任务,或者在系统中定时任务调用;
* * * * * /usr/bin/curl -o /dev/null -s http://127.0.0.1/sync-ipblacklist > /dev/null 2>&1
mysql建表
CREATE TABLE `ip_blacklist` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ip_addr` varchar(15) COLLATE utf8mb4_bin DEFAULT NULL,
`status` int(11) DEFAULT '0' COMMENT '0: valid 有效, 1: invalid 失效',
`effective_hour` decimal(11,2) DEFAULT '24' COMMENT '有效期,单位:小时',
`ip_source` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '黑名单来源',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`modify_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`remark` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
CREATE PROCEDURE proc_ip_blacklist_status_update()
-- 将过期的IP状态改为失效
begin
update ip_blacklist
set status=1
where date_add(create_time,INTERVAL effective_hour hour) < now();
commit;
end;
CREATE EVENT job_ip_blacklist_status_update
ON SCHEDULE EVERY 1 MINUTE
ON COMPLETION PRESERVE
ENABLE
DO
call proc_ip_blacklist_status_update();
剩下的就是生成黑名单了!