文章目录
-
- 架构设计
- 环境准备
- 源码编辑安装OpenResty
-
- 下载
- 安装准备依赖
- 编译
- 安装
- 配置环境变量(可选)
- [OpenResty 服务管理命令](#OpenResty 服务管理命令)
- 安装Redis
- 配置Lua脚本
- 测试
- 删除版本信息
- 清除编译安装的OpenResty
架构设计
通过 Nginx + Redis 的方案,确实可以将流量挡在 Nginx 和 Redis 层,从而利用nginx和redis适合高并发的特点,保护后端了服务。如果需要更高的性能和可靠性,推荐使用防火墙/WAF相关手段,将流量挡在网络层面。
- 动态封禁逻辑:通过Lua脚本实时分析Nginx访问日志,将高频请求的IP写入Redis黑名单。
- 自动解封机制:Redis中设置IP的TTL过期时间,到期自动释放。
- 性能优化:使用共享内存缓存黑名单,减少Redis查询压力。
环境准备
关闭防火墙和selinux,进行时间同步。
- 操作系统:两台CentOS 7.9(本文章使用)或其他操作系统
- 软件依赖:OpenResty(集成Nginx+Lua)、Redis 5.0+
- 网络权限:确保Redis端口(默认6379)可访问
主机OpenResty:
ini
[root@localhost ~]# hostnamectl set-hostname openresty
[root@localhost ~]# bash
[root@openresty ~]# ip a | grep 192.168
inet 192.168.226.133/24 brd 192.168.226.255 scope global noprefixroute ens33
[root@openresty ~]# hostnamectl
Static hostname: openresty
Pretty hostname: openresty
Icon name: computer-vm
Chassis: vm
Machine ID: 7ec089d0625e4b258d18677311cbeb31
Boot ID: 550ce2f4975f431aaf449951924e6821
Virtualization: vmware
Operating System: CentOS Linux 7 (Core)
CPE OS Name: cpe:/o:centos:centos:7
Kernel: Linux 3.10.0-1160.el7.x86_64
Architecture: x86-64
[root@openresty ~]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
主机Redis:
bash
[root@localhost ~]# hostnamectl set-hostname redis
[root@localhost ~]# bash
bash
[root@redis ~]# ip a | grep 192.168
inet 192.168.226.134/24 brd 192.168.226.255 scope global noprefixroute ens33
[root@redis ~]# hostnamectl
Static hostname: redis
Icon name: computer-vm
Chassis: vm
Machine ID: 7ec089d0625e4b258d18677311cbeb31
Boot ID: 3bc6025f09f84885a928787b996fdf28
Virtualization: vmware
Operating System: CentOS Linux 7 (Core)
CPE OS Name: cpe:/o:centos:centos:7
Kernel: Linux 3.10.0-1160.el7.x86_64
Architecture: x86-64
[root@redis ~]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"
CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"
源码编辑安装OpenResty
下载
在192.168.226.133上操作
-
官网下载:https://openresty.org/cn/download.html
上传压缩包至openresty主机中
-
使用wget下载:
bash
[root@openresty ~]# wget https://openresty.org/download/openresty-1.25.3.2.tar.gz
--2025-03-14 23:06:24-- https://openresty.org/download/openresty-1.25.3.2.tar.gz
Resolving openresty.org (openresty.org)... 47.91.165.147
Connecting to openresty.org (openresty.org)|47.91.165.147|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5837745 (5.6M) [application/x-gzip]
Saving to: 'openresty-1.25.3.2.tar.gz'
100%[==============================================================================================================================================================================================>] 5,837,745 4.46MB/s in 1.2s
2025-03-14 23:06:26 (4.46 MB/s) - 'openresty-1.25.3.2.tar.gz' saved [5837745/5837745]
# 解压
[root@openresty ~]# tar -zxvf openresty-1.25.3.2.tar.gz
安装准备依赖
bash
[root@openresty ~]# mkdir -p ./package
# 下载依赖包并缓存到当前目录下的package目录里,这个命令方便离线缓存后拿到内网主机安装
# 将 package 目录拷贝到目标机器后,通过以下命令批量安装:rpm -ivh ./package/*.rpm --nodeps --force
# --downloadonly 会下载主包及其所有依赖的 RPM 文件,但需注意:
# 如果依赖包已安装,yum 默认不会重复下载。若需强制下载,需在未安装的环境中操作
# 使用 --setopt=keepcache=1 可强制缓存,但需在 /etc/yum.conf 中设置 keepcache=1
# 不同Linux发行版依赖包名可能不同(如Ubuntu用apt安装libpcre3-dev等)
[root@openresty ~]# yum install -y --downloadonly --downloaddir=./package readline-devel pcre pcre-devel openssl openssl-devel gcc curl GeoIP-devel perl
编译
bash
[root@openresty ~]# cd openresty-1.25.3.2
[root@openresty openresty-1.25.3.2]# ll
total 104
drwxrwxr-x 46 1000 1000 4096 Jul 17 2024 bundle
-rwxrwxr-x 1 1000 1000 51486 Jul 17 2024 configure
-rw-rw-r-- 1 1000 1000 22924 Jul 17 2024 COPYRIGHT
drwxrwxr-x 2 1000 1000 4096 Jul 17 2024 patches
-rw-rw-r-- 1 1000 1000 4689 Jul 17 2024 README.markdown
-rw-rw-r-- 1 1000 1000 8974 Jul 17 2024 README-windows.txt
drwxrwxr-x 2 1000 1000 52 Jul 17 2024 util
# 自行确认依赖包都已安装
# 运行配置脚本以准备编译过程;使用./configure --help查看所有可用参数
# 若需自定义安装路径,添加--prefix=/your/path(默认路径为/usr/local/openresty)
[root@openresty openresty-1.25.3.2]#./configure \
--with-luajit \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_stub_status_module
注释:
--with-luajit:启用LuaJIT支持(高性能Lua引擎)。
--with-http_ssl_module:启用HTTPS支持。
--with-http_gzip_static_module:启用静态文件压缩。
--with-http_realip_module:获取客户端真实IP。
--with-http_stub_status_module:启用Nginx状态监控
安装
bash
[root@openresty openresty-1.25.3.2]# make -j$(nproc) # 并行编译,加速构建
[root@openresty openresty-1.25.3.2]# make install # 安装到系统目录
配置环境变量(可选)
为方便启动,可将OpenResty的Nginx路径加入PATH
: 修改~/.bashrc
或/etc/profile
:
bash
[root@openresty openresty-1.25.3.2]# export PATH=/usr/local/openresty/nginx/sbin:$PATH
[root@openresty openresty-1.25.3.2]# source ~/.bashrc
配置路径解析:
配置项 | 路径 | 作用说明 |
---|---|---|
Nginx 根目录 | /usr/local/openresty/nginx |
所有组件和文件的安装根目录 |
Nginx 可执行文件 | /usr/local/openresty/nginx/sbin/nginx |
启动、停止服务的核心二进制文件 |
动态模块目录 | /usr/local/openresty/nginx/modules |
存放通过 load_module 加载的动态模块(如第三方扩展) |
主配置文件 | /usr/local/openresty/nginx/conf/nginx.conf |
全局配置入口,建议分拆配置到 conf.d/ 目录 |
PID 文件 | /usr/local/openresty/nginx/logs/nginx.pid |
记录 Nginx 主进程的进程 ID(用于 nginx -s reload 等操作) |
错误日志 | /usr/local/openresty/nginx/logs/error.log |
服务运行时的错误日志(故障排查优先查看此文件) |
访问日志 | /usr/local/openresty/nginx/logs/access.log |
所有 HTTP 请求的访问日志(需定期清理或轮转) |
临时文件目录 | client_body_temp , proxy_temp , fastcgi_temp 等 |
处理请求时生成的临时文件(需确保磁盘空间充足和写入权限) |
OpenResty 服务管理命令
bash
# 安全启动配置-创建专用用户
[root@openresty openresty-1.25.3.2]# cd
[root@openresty ~]# useradd -M -s /sbin/nologin nginx # 创建无登录权限的用户
[root@openresty ~]# chown -R nginx:nginx /usr/local/openresty/nginx/ # 修改目录权限
命令 | 参数/选项 | 说明 |
---|---|---|
/usr/local/openresty/nginx/sbin/nginx |
-p /custom/path |
指定 OpenResty 项目根目录(默认 /usr/local/openresty ) |
-c nginx.conf |
指定配置文件路径(默认 /usr/local/openresty/nginx/conf/nginx.conf ) |
|
-s stop |
强制停止服务 | |
-s quit |
优雅停止服务(等待请求处理完成) | |
-s reload |
重载配置文件(不中断服务) | |
-t |
检查配置文件语法是否正确 | |
-V |
显示 OpenResty 版本及编译时启用的模块(如 LuaJIT、SSL 等) |
编辑 nginx.conf
修改下属配置,一般就在第2行,修改完成后保存退出
ini
[root@openresty ~]# vim /usr/local/openresty/nginx/conf/nginx.conf
user nginx;
启动
ini
# 直接启动
[root@openresty ~]# /usr/local/openresty/nginx/sbin/nginx
# 检查进程是否运行
[root@openresty ~]# ps aux | grep nginx
root 25779 0.0 0.0 55312 1708 ? Ss 00:52 0:00 nginx: master process /usr/local/openresty/nginx/sbin/nginx
nginx 25780 0.0 0.1 55736 2472 ? S 00:52 0:00 nginx: worker process
[root@openresty ~]# ss -tnlp | grep 80
LISTEN 0 128 *:80 *:* users:(("nginx",pid=25780,fd=6),("nginx",pid=25779,fd=6))
浏览器访问:http://192.168.226.133/
然后可以看到Welcome to OpenResty!的页面。
安装Redis
在192.168.226.134主机安装
redis这里直接使用yum安装了
bash
# centos7.9的默认仓库epel只有一个3.2x版本的Redis
# Remi 仓库是一个常用的第三方仓库,提供了较新的软件版本。
[root@redis ~]# yum install -y https://rpms.remirepo.net/enterprise/remi-release-7.rpm
# 查看 Remi 仓库中的 Redis 版本
[root@redis ~]# yum --enablerepo=remi list redis --showduplicates
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.aliyun.com
* remi: mirrors.tuna.tsinghua.edu.cn
* remi-safe: mirrors.tuna.tsinghua.edu.cn
* updates: mirrors.aliyun.com
remi | 3.0 kB 00:00:00
remi-safe | 3.0 kB 00:00:00
(1/2): remi-safe/primary_db | 2.6 MB 00:00:01
(2/2): remi/primary_db | 3.7 MB 00:00:02
Available Packages
redis.x86_64 3.2.12-2.el7 epel
redis.x86_64 5.0.13-1.el7.remi remi
redis.x86_64 5.0.14-1.el7.remi remi
redis.x86_64 6.0.19-1.el7.remi remi
redis.x86_64 6.0.20-1.el7.remi remi
redis.x86_64 6.2.13-1.el7.remi remi
redis.x86_64 6.2.14-1.el7.remi remi
redis.x86_64 7.0.14-1.el7.remi remi
redis.x86_64 7.0.15-1.el7.remi remi
redis.x86_64 7.2.4-1.el7.remi remi
redis.x86_64 7.2.5-1.el7.remi remi
# 选择一个版本下载安装
[root@redis ~]# yum -y --enablerepo=remi install redis-5.0.14-1.el7.remi
[root@redis ~]# systemctl enable --now redis
[root@redis ~]# redis-cli -v
redis-cli 5.0.14
# 配置连接Redis的用户名和密码
# 编辑 Redis 配置文件(通常位于 /etc/redis.conf
# 找到 requirepass 字样附近插入配置并设置密码
[root@redis ~]# vim /etc/redis.conf
requirepass "GFrc3d+76Fr*Ctpt"
# 重启Redis
[root@redis ~]# systemctl restart redis
# 使用密码连接测试
[root@redis ~]# redis-cli -h 127.0.0.1 -p 6379 -a "GFrc3d+76Fr*Ctpt"
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379>
# 配置指定IP可以连接Redis
# 编辑配置文件找到bind关键字,增加指定IP的配置
[root@redis ~]# vim /etc/redis.conf
bind 127.0.0.1 192.168.226.133 192.168.226.134
# 重启Redis,然后自行连接测试即可
[root@redis ~]# systemctl restart redis
配置Lua脚本
在192.168.226.133上操作
ini
[root@openresty ~]# cd /usr/local/openresty/nginx/conf
# 更新nginx.conf配置
[root@openresty conf]# cat nginx.conf
user nginx;
worker_processes auto;
# 错误日志设置
error_log logs/error.log warn;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
# 真实IP解析设置
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# 定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log logs/access.log main;
# Lua配置
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
lua_shared_dict ip_counters 10m; # 用于扩展速率限制
server {
listen 80;
server_name localhost;
# 在访问阶段执行IP检查
access_by_lua_file conf/lua/ip_block.lua;
location / {
root html;
index index.html index.htm;
}
# 封禁IP管理接口(生产环境需要添加鉴权!)
location /manage/blacklist {
allow 127.0.0.1; # 只允许本地访问
deny all;
# 参数示例:
# /manage/blacklist?action=add&ip=1.2.3.4
# /manage/blacklist?action=del&ip=1.2.3.4
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000)
local ip = ngx.var.arg_ip
local action = ngx.var.arg_action
if not (ip and action) then
ngx.say('{"code":400,"msg":"缺少参数"}')
return
end
-- 连接Redis
local ok, err = red:connect("192.168.226.134", 6379)
if not ok then
ngx.say('{"code":500,"msg":"Redis连接失败"}')
return
end
-- 认证
local auth_ok, auth_err = red:auth("GFrc3d+76Fr*Ctpt")
if not auth_ok then
ngx.say('{"code":500,"msg":"Redis认证失败"}')
return
end
red:select(15)
-- 执行操作
local res
if action == "add" then
res, err = red:sadd("blacklist", ip)
elseif action == "del" then
res, err = red:srem("blacklist", ip)
else
ngx.say('{"code":400,"msg":"无效操作"}')
return
end
if err then
ngx.say('{"code":500,"msg":"操作失败: '..err..'"}')
else
ngx.say('{"code":200,"msg":"操作成功","affected":'..res..'}')
end
red:close()
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
创建Lua脚本(日志记录允许通过的不允许的配置,选择一个lua脚本配置即可。)
ini
[root@openresty conf]# mkdir lua
[root@openresty conf]# cd lua/
[root@openresty lua]# cat ip_block.lua
local redis = require "resty.redis"
local cjson = require "cjson.safe"
-- 配置区域 ===========================================
local SECURITY_CONFIG = {
redis = {
host = "192.168.226.134", -- Redis 服务器地址
port = 6379, -- Redis 端口
password = "GFrc3d+76Fr*Ctpt", -- Redis 认证密码
database = 15, -- 使用的数据库编号
timeout = 1000 -- 连接超时时间(毫秒)
},
rate_limit = {
window = 5, -- 统计时间窗口(秒)
threshold = 10, -- 请求阈值(次数/窗口)
ban_duration = 60 -- 封禁时长(秒)
},
log = {
path = "/usr/local/openresty/nginx/logs/lua_combined.log" -- 日志文件路径
}
}
-- 结束配置区域 =======================================
-- 获取客户端 IP
local function get_client_ip()
local headers = ngx.req.get_headers()
return headers["X-Real-IP"] or
(headers["X-Forwarded-For"] and headers["X-Forwarded-For"]:match("([^,]+)")) or
ngx.var.remote_addr
end
-- 记录日志
local function log_event(event_type, client_ip, reason)
local log_entry = {
timestamp = ngx.localtime(),
event = event_type,
client_ip = client_ip,
reason = reason,
request = {
method = ngx.req.get_method(),
uri = ngx.var.request_uri,
args = ngx.req.get_uri_args()
}
}
local json_str, err = cjson.encode(log_entry)
if not json_str then
ngx.log(ngx.ERR, "JSON 编码失败: ", err)
return
end
-- 异步写入日志
local ok, err = ngx.timer.at(0, function(premature, path, entry)
if premature then return end
local file = io.open(path, "a")
if file then
file:write(entry, "\n")
file:close()
else
ngx.log(ngx.ERR, "无法打开日志文件: ", path)
end
end, SECURITY_CONFIG.log.path, json_str)
if not ok then
ngx.log(ngx.ERR, "无法创建日志定时器: ", err)
end
end
-- 主逻辑
local client_ip = get_client_ip()
local red = redis:new()
red:set_timeout(SECURITY_CONFIG.redis.timeout)
-- 连接 Redis
local ok, err = red:connect(SECURITY_CONFIG.redis.host, SECURITY_CONFIG.redis.port)
if not ok then
ngx.log(ngx.ERR, "Redis 连接失败: ", err)
return ngx.exit(500)
end
-- 认证
local auth_ok, auth_err = red:auth(SECURITY_CONFIG.redis.password)
if not auth_ok then
ngx.log(ngx.ERR, "Redis 认证失败: ", auth_err)
return ngx.exit(500)
end
-- 选择数据库
red:select(SECURITY_CONFIG.redis.database)
-- 检查白名单
local is_whitelisted, err = red:sismember("whitelist", client_ip)
if is_whitelisted == 1 then
log_event("WHITELIST_BYPASS", client_ip, "IP 在白名单中")
red:set_keepalive(10000, 100) -- 释放连接到连接池
return -- 白名单直接放行
end
-- 检查黑名单
local is_blacklisted, err = red:sismember("blacklist", client_ip)
if is_blacklisted == 1 then
log_event("BLACKLIST_BLOCK", client_ip, "IP 在黑名单中")
red:set_keepalive(10000, 100) -- 释放连接到连接池
return ngx.exit(403) -- 返回 403 禁止访问
end
-- 检查请求频率
local counter_key = "rate_limit:" .. client_ip
local request_count, err = red:incr(counter_key)
if not request_count then
ngx.log(ngx.ERR, "Redis 计数器增加失败: ", err)
return ngx.exit(500)
end
-- 如果是第一次访问,设置过期时间
if request_count == 1 then
red:expire(counter_key, SECURITY_CONFIG.rate_limit.window)
end
-- 触发封禁条件
if request_count > SECURITY_CONFIG.rate_limit.threshold then
local ok, err = red:sadd("blacklist", client_ip)
if not ok then
ngx.log(ngx.ERR, "添加黑名单失败: ", err)
return ngx.exit(500)
end
red:expire("blacklist", SECURITY_CONFIG.rate_limit.ban_duration)
log_event("RATE_LIMIT_BLOCK", client_ip, "请求频率超限")
red:set_keepalive(10000, 100) -- 释放连接到连接池
return ngx.exit(429) -- 返回 429 请求过多
end
-- 正常请求
log_event("ALLOWED_REQUEST", client_ip, "请求通过")
red:set_keepalive(10000, 100) -- 释放连接到连接池
日志仅记录不允许的配置
ini
local redis = require "resty.redis"
local cjson = require "cjson.safe"
-- 配置区域 ===========================================
local SECURITY_CONFIG = {
redis = {
host = "192.168.226.134", -- Redis 服务器地址
port = 6379, -- Redis 端口
password = "GFrc3d+76Fr*Ctpt", -- Redis 认证密码
database = 15, -- 使用的数据库编号
timeout = 1000 -- 连接超时时间(毫秒)
},
rate_limit = {
window = 5, -- 统计时间窗口(秒)
threshold = 10, -- 请求阈值(次数/窗口)
ban_duration = 60 -- 封禁时长(秒)
},
log = {
path = "/usr/local/openresty/nginx/logs/lua_combined.log" -- 日志文件路径
}
}
-- 结束配置区域 =======================================
-- 获取客户端 IP
local function get_client_ip()
local headers = ngx.req.get_headers()
return headers["X-Real-IP"] or
(headers["X-Forwarded-For"] and headers["X-Forwarded-For"]:match("([^,]+)")) or
ngx.var.remote_addr
end
-- 记录日志
local function log_event(event_type, client_ip, reason)
local log_entry = {
timestamp = ngx.localtime(),
event = event_type,
client_ip = client_ip,
reason = reason,
request = {
method = ngx.req.get_method(),
uri = ngx.var.request_uri,
args = ngx.req.get_uri_args()
}
}
local json_str, err = cjson.encode(log_entry)
if not json_str then
ngx.log(ngx.ERR, "JSON 编码失败: ", err)
return
end
-- 异步写入日志
local ok, err = ngx.timer.at(0, function(premature, path, entry)
if premature then return end
local file = io.open(path, "a")
if file then
file:write(entry, "\n")
file:close()
else
ngx.log(ngx.ERR, "无法打开日志文件: ", path)
end
end, SECURITY_CONFIG.log.path, json_str)
if not ok then
ngx.log(ngx.ERR, "无法创建日志定时器: ", err)
end
end
-- 主逻辑
local client_ip = get_client_ip()
local red = redis:new()
red:set_timeout(SECURITY_CONFIG.redis.timeout)
-- 连接 Redis
local ok, err = red:connect(SECURITY_CONFIG.redis.host, SECURITY_CONFIG.redis.port)
if not ok then
ngx.log(ngx.ERR, "Redis 连接失败: ", err)
return ngx.exit(500)
end
-- 认证
local auth_ok, auth_err = red:auth(SECURITY_CONFIG.redis.password)
if not auth_ok then
ngx.log(ngx.ERR, "Redis 认证失败: ", auth_err)
return ngx.exit(500)
end
-- 选择数据库
red:select(SECURITY_CONFIG.redis.database)
-- 检查白名单
local is_whitelisted, err = red:sismember("whitelist", client_ip)
if is_whitelisted == 1 then
red:set_keepalive(10000, 100) -- 释放连接到连接池
return -- 白名单直接放行
end
-- 检查黑名单
local is_blacklisted, err = red:sismember("blacklist", client_ip)
if is_blacklisted == 1 then
log_event("BLACKLIST_BLOCK", client_ip, "IP 在黑名单中")
red:set_keepalive(10000, 100) -- 释放连接到连接池
return ngx.exit(403) -- 返回 403 禁止访问
end
-- 检查请求频率
local counter_key = "rate_limit:" .. client_ip
local request_count, err = red:incr(counter_key)
if not request_count then
ngx.log(ngx.ERR, "Redis 计数器增加失败: ", err)
return ngx.exit(500)
end
-- 如果是第一次访问,设置过期时间
if request_count == 1 then
red:expire(counter_key, SECURITY_CONFIG.rate_limit.window)
end
-- 触发封禁条件
if request_count > SECURITY_CONFIG.rate_limit.threshold then
local ok, err = red:sadd("blacklist", client_ip)
if not ok then
ngx.log(ngx.ERR, "添加黑名单失败: ", err)
return ngx.exit(500)
end
red:expire("blacklist", SECURITY_CONFIG.rate_limit.ban_duration)
log_event("RATE_LIMIT_BLOCK", client_ip, "请求频率超限")
red:set_keepalive(10000, 100) -- 释放连接到连接池
return ngx.exit(429) -- 返回 429 请求过多
end
-- 正常请求,不记录日志
red:set_keepalive(10000, 100) -- 释放连接到连接池
热加载配置
bash
[root@openresty lua]# nginx -t
nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful
[root@openresty lua]# nginx -s reload
# 修改目录权限
[root@openresty lua]# chown -R nginx:nginx /usr/local/openresty/nginx/
[root@openresty lua]# chmod -R 755 /usr/local/openresty/nginx/html
附白名单操作:
如果需要加白处理,需要手动在Redis中插入
ini
SADD whitelist 192.168.226.134
测试
准备测试工具
-
使用工具模拟高频率请求。推荐使用以下工具:
-
ab
(Apache Benchmark):适用于简单的压力测试。 -
wrk
:高性能的 HTTP 压力测试工具。 -
curl
:手动测试单次请求。安装 ab
yum install httpd-tools -y
安装 wrk (第三方 RPM 包安装)
yum install -y https://github.com/scutse/wrk-rpm/releases/download/4.1.0/wrk-4.1.0-1.el7.centos.x86_64.rpm
-
一般yum
官方仓库中未提供 wrk
的预编译包。
下附wrk安装方法:
ini
# 安装依赖
yum install -y git make gcc openssl-devel
# 克隆源码并编译
git clone https://github.com/wg/wrk.git
cd wrk
make
# 安装到系统路径
cp wrk /usr/local/bin
# 验证安装,输出版本信息即表示成功
wrk --version
测试封禁逻辑
使用 ab
进行压力测试
运行以下命令,模拟高频率请求:
bash
[root@openresty ~]# curl http://localhost/
[root@openresty ~]# ab -n 100 -c 10 http://192.168.226.133/
参数说明:
- `-n 100`:总请求数为 100。
- `-c 10`:并发请求数为 10。
[root@openresty ~]# curl http://localhost/
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty</center>
</body>
</html>
观察 Nginx 日志和 Lua 日志,检查是否触发封禁逻辑:
如果封禁逻辑生效,你会看到类似以下的日志:
ini
[root@openresty ~]# tail -f /usr/local/openresty/nginx/logs/lua_combined.log # 我使用的lua配置仅记录黑名单的访问,因此就看到了拒绝的日志
{"timestamp":"2025-03-15 20:02:24","event":"RATE_LIMIT_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.1","reason":"请求频率超限"}
{"timestamp":"2025-03-15 20:02:24","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.1","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:02:24","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.1","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:02:24","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.1","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:02:25","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.1","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:03:05","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:03:05","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:03:05","event":"RATE_LIMIT_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"请求频率超限"}
{"timestamp":"2025-03-15 20:03:05","event":"RATE_LIMIT_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"请求频率超限"}
{"timestamp":"2025-03-15 20:03:05","event":"RATE_LIMIT_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"请求频率超限"}
{"timestamp":"2025-03-15 20:03:05","event":"RATE_LIMIT_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"请求频率超限"}
{"timestamp":"2025-03-15 20:03:05","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:03:05","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"IP 在黑名单中"}
{"timestamp":"2025-03-15 20:03:05","event":"BLACKLIST_BLOCK","request":{"args":{},"method":"GET","uri":"\/"},"client_ip":"192.168.226.134","reason":"IP 在黑名单中"}
删除版本信息
1.通过nginx.conf配置
关闭全局版本信息显示
当访问浏览器这个服务的一个不存在的页面时,可以看到
ini
404 Not Found
openresty/1.25.3.2
或使用
ini
[root@openresty ~]# curl -I http:192.168.226.133
HTTP/1.1 200 OK
Server: openresty/1.25.3.2
Date: Fri, 14 Mar 2025 17:29:43 GMT
Content-Type: text/html
Content-Length: 116103
Last-Modified: Fri, 14 Mar 2025 15:48:58 GMT
Connection: keep-alive
ETag: "67d44fea-1c587"
Accept-Ranges: bytes
要关闭版本信息这个功能,需要修改nginx配置
vim /usr/local/openresty/nginx/conf/nginx.conf
在http模块里配置是全局生效,在特定的server块里配置是特定server块里的生效,这里我设置全局生效。
ini
[root@openresty ~]# vim /usr/local/openresty/nginx/conf/nginx.conf
http {
...
server_tokens off; # 隐藏 Nginx 版本信息
...
}
热加载配置
ini
[root@openresty ~]# nginx -s reload
重新访问一个404页面验证
ini
404 Not Found
openresty
然后使用curl
ini
[root@openresty ~]# curl -I http://192.168.226.133
HTTP/1.1 200 OK
Server: openresty
Date: Fri, 14 Mar 2025 17:32:04 GMT
Content-Type: text/html
Content-Length: 116103
Last-Modified: Fri, 14 Mar 2025 15:48:58 GMT
Connection: keep-alive
ETag: "67d44fea-1c587"
Accept-Ranges: bytes
通过配置server_tokens off;
参数只能隐藏1.25.3.2
d而不能隐藏
openresty`信息。
2.修改编译选项
通过配置server_tokens off;
但是发现还会有openresty
字样
下述为彻底删除版本信息方式:
ini
[root@openresty ~]# ll
total 5712
-rw-------. 1 root root 1259 Feb 7 08:04 anaconda-ks.cfg
drwxrwxr-x 6 nginx nginx 159 Mar 14 23:48 openresty-1.25.3.2
-rw-r--r-- 1 root root 5837745 Jul 17 2024 openresty-1.25.3.2.tar.gz
drwxr-xr-x 2 root root 4096 Mar 14 23:27 package
[root@openresty ~]# cd openresty-1.25.3.2
[root@openresty openresty-1.25.3.2]# ll
total 116
drwxrwxr-x 47 nginx nginx 4096 Mar 14 23:47 build
drwxrwxr-x 46 nginx nginx 4096 Jul 17 2024 bundle
-rwxrwxr-x 1 nginx nginx 51486 Jul 17 2024 configure
-rw-rw-r-- 1 nginx nginx 22924 Jul 17 2024 COPYRIGHT
-rw-r--r-- 1 root root 6005 Mar 14 23:48 Makefile
drwxrwxr-x 2 nginx nginx 4096 Jul 17 2024 patches
-rw-rw-r-- 1 nginx nginx 4689 Jul 17 2024 README.markdown
-rw-rw-r-- 1 nginx nginx 8974 Jul 17 2024 README-windows.txt
drwxrwxr-x 2 nginx nginx 52 Jul 17 2024 util
# 修改源码中的版本信息:
[root@openresty openresty-1.25.3.2]# cd bundle/nginx-1.25.3/src/core/
[root@openresty core]# cat nginx.h
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGINX_H_INCLUDED_
#define _NGINX_H_INCLUDED_
#define nginx_version 1025003
#define NGINX_VERSION "1.25.3"
#define NGINX_VER "openresty/" NGINX_VERSION ".2"
#ifdef NGX_BUILD
#define NGINX_VER_BUILD NGINX_VER " (" NGX_BUILD ")"
#else
#define NGINX_VER_BUILD NGINX_VER
#endif
#define NGINX_VAR "NGINX"
#define NGX_OLDPID_EXT ".oldbin"
#endif /* _NGINX_H_INCLUDED_ */
然后自定义修改为:
ini
[root@openresty core]# cat nginx.h
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGINX_H_INCLUDED_
#define _NGINX_H_INCLUDED_
#define nginx_version 1025003
#define NGINX_VERSION "I'm a walk-on"
#define NGINX_VER "Hello! " NGINX_VERSION "_______110"
#ifdef NGX_BUILD
#define NGINX_VER_BUILD NGINX_VER " (" NGX_BUILD ")"
#else
#define NGINX_VER_BUILD NGINX_VER
#endif
#define NGINX_VAR "NGINX"
#define NGX_OLDPID_EXT ".oldbin"
#endif /* _NGINX_H_INCLUDED_ */
再次编译并安装
bash
[root@openresty core]# cd
[root@openresty ~]# ll
total 5712
-rw-------. 1 root root 1259 Feb 7 08:04 anaconda-ks.cfg
drwxrwxr-x 6 nginx nginx 159 Mar 14 23:48 openresty-1.25.3.2
-rw-r--r-- 1 root root 5837745 Jul 17 2024 openresty-1.25.3.2.tar.gz
drwxr-xr-x 2 root root 4096 Mar 14 23:27 package
[root@openresty ~]# cd openresty-1.25.3.2
[root@openresty openresty-1.25.3.2]# ll
total 116
drwxrwxr-x 47 nginx nginx 4096 Mar 14 23:47 build
drwxrwxr-x 46 nginx nginx 4096 Jul 17 2024 bundle
-rwxrwxr-x 1 nginx nginx 51486 Jul 17 2024 configure
-rw-rw-r-- 1 nginx nginx 22924 Jul 17 2024 COPYRIGHT
-rw-r--r-- 1 root root 6005 Mar 14 23:48 Makefile
drwxrwxr-x 2 nginx nginx 4096 Jul 17 2024 patches
-rw-rw-r-- 1 nginx nginx 4689 Jul 17 2024 README.markdown
-rw-rw-r-- 1 nginx nginx 8974 Jul 17 2024 README-windows.txt
drwxrwxr-x 2 nginx nginx 52 Jul 17 2024 util
# 编译
[root@openresty openresty-1.25.3.2]#./configure \
--with-luajit \
--with-http_ssl_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_stub_status_module
# 安装
[root@openresty openresty-1.25.3.2]# make -j$(nproc) # 并行编译,加速构建
[root@openresty openresty-1.25.3.2]# make install # 安装到系统目录
# 进行平滑升级
# 查看旧进程ID
[root@openresty openresty-1.25.3.2]# ps aux | grep nginx
root 33968 0.0 0.0 55312 1704 ? Ss 01:37 0:00 nginx: master process nginx
nginx 33969 0.0 0.1 55736 2712 ? S 01:37 0:00 nginx: worker process
root 51298 0.0 0.0 112812 980 pts/0 S+ 01:43 0:00 grep --color=auto nginx
# 验证配置文件无错误
[root@openresty openresty-1.25.3.2]# /usr/local/openresty/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful
# 热加载平滑升级
[root@openresty openresty-1.25.3.2]# /usr/local/openresty/nginx/sbin/nginx -s reload
# 查看新进程
[root@openresty openresty-1.25.3.2]# ps aux | grep nginx
root 33968 0.0 0.1 55448 3628 ? Ss 01:37 0:00 nginx: master process nginx
nginx 51300 0.0 0.1 55884 2656 ? S 01:43 0:00 nginx: worker process
# 此步骤利用平滑升级的思路,重新编译安装了服务,去除了版本信息。
清除编译安装的OpenResty
bash
# 停止 OpenResty 服务
[root@openresty ~]# /usr/local/openresty/nginx/sbin/nginx -s stop
# 删除 OpenResty 安装目录
[root@openresty ~]# rm -rf /usr/local/openresty
# 删除 OpenResty 的符号链接
[root@openresty ~]# rm -f /usr/local/bin/openresty
# 清理环境变量,删除之前定义的变量export PATH=/usr/local/openresty/nginx/sbin:$PATH
vi ~/.bash_profile
export PATH
# 删除后保存退出
# 清理编译源码目录
[root@openresty ~]# rm -rf ./openresty-1.25.3.2
# 检查是否清理干净
[root@openresty ~]# which openresty
[root@openresty ~]# which nginx