背景,原来的nginx在生产环境已经正常运行,需要扩展lua实现ip黑名单拦截,原来nginx版本是1.18.0,打算使用原版本的源码重新编译nginx实现扩展。本次扩展还同时升级了OpenSSL,升级后的nginx信息如下:
nginx version: nginx/1.18.0
built by gcc 11.2.0 (Ubuntu 11.2.0-19ubuntu1)
built with OpenSSL 3.0.2 15 Mar 2022
TLS SNI support enabled
configure arguments: --with-cc-opt='-Wno-error -Wno-deprecated-declarations' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module --add-module=/usr/local/src/nginx-modules/ngx_devel_kit-0.3.1 --add-module=/usr/local/src/nginx-modules/lua-nginx-module-0.10.14
安装luajit-2.0.4
本次选用安装luajit-2.0.4:
wget -c http://luajit.org/download/LuaJIT-2.0.4.tar.gz
tar xzvf LuaJIT-2.0.4.tar.gz
cd LuaJIT-2.0.4
make install PREFIX=/usr/local/luajit
环境变量设置,在/etc/profile文件内增加如下:
export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.0
使配置生效
source /etc/profile
Nginx Module下载
NDK(nginx development kit)模块是一个拓展nginx服务器核心功能的模块,第三方模块开发可以基于它来快速实现。NDK提供函数和宏处理一些基本任务,减轻第三方模块开发的代码量。
$ mkdir -p /usr/local/src/nginx-modules
$ cd /usr/local/src/nginx-modules
$ wget https://github.com/vision5/ngx_devel_kit/archive/v0.3.1.tar.gz
# 上面的地址如果下载不了,可以使用以下加速地址
$ wget https://github.91chifun.workers.dev/https://github.com//vision5/ngx_devel_kit/archive/refs/tags/v0.3.1.tar.gz
$ tar -xvf v0.3.1.tar.gz
lua-nginx-module,将Lua的功能嵌入到Nginx HTTP服务器中。
$ cd /usr/local/src/nginx-modules
$ wget https://github.com/openresty/lua-nginx-module/archive/v0.10.14.tar.gz
# 上面的地址如果下载不了,可以使用以下加速地址
$ wget https://github.91chifun.workers.dev/https://github.com//openresty/lua-nginx-module/archive/refs/tags/v0.10.14.tar.gz
$ tar -xvf v0.10.14.tar.gz
Nginx编译配置和测试
检查当前所用的nginx版本
nginx -v
nginx version: nginx/1.18.0 (Ubuntu)
使用的是1.18.0,所以下载1.18.0
cd /usr/local/src
wget https://nginx.org/download/nginx-1.18.0.tar.gz
tar -xvf nginx-1.18.0.tar.gz && cd nginx-1.18.0
检查原来安装的nginx的config参数:
nginx -V
如下:
nginx version: nginx/1.18.0 (Ubuntu)
built with OpenSSL 3.0.2 15 Mar 2022
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -ffile-prefix-map=/build/nginx-9P0wNJ/nginx-1.18.0=. -flto=auto -ffat-lto-objects -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -flto=auto -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --add-dynamic-module=/build/nginx-9P0wNJ/nginx-1.18.0/debian/modules/http-geoip2 --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module
复制出来,修改后,增加模块
./configure --with-cc-opt='-Wno-error -Wno-deprecated-declarations' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-compat --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_sub_module --add-module=/usr/local/src/nginx-modules/ngx_devel_kit-0.3.1 --add-module=/usr/local/src/nginx-modules/lua-nginx-module-0.10.14
make,但不要make install;上面增加“–with-cc-opt=’-Wno-error -Wno-deprecated-declarations’”这个选项是因为openssl版本高,nginx编译报错.
在nginx检测配置的过程中,发现找不到lua的依赖库,直接简单粗暴:
apt-get install lua*
IP拦截
在nginx.conf的http下增”lua_shared_dict ip_blacklist 1m;”,如下:
http {
#由 Nginx 进程分配一块 1M 大小的共享内存空间,用来缓存 IP 黑名单
lua_shared_dict ip_blacklist 1m;
由 Nginx 进程分配一块 1M 大小的共享内存空间,用来缓存 IP 黑名单。
在需要拦截的server下增加如下测试:
location /ipblacklist {
access_by_lua_file /var/www/data/lua/ipblacklist.lua;
default_type 'text/html';
content_by_lua 'ngx.say("hello,lua")';
}
访问报错:
2022/10/13 10:40:43 [error] 304670#304670: *403 lua entry thread aborted: runtime error: /var/www/data/lua/ipblacklist.lua:25: module 'resty.redis' not found:
no field package.preload['resty.redis']
no file './resty/redis.lua'
no file '/usr/share/luajit-2.1.0-beta3/resty/redis.lua'
no file '/usr/local/share/lua/5.1/resty/redis.lua'
no file '/usr/local/share/lua/5.1/resty/redis/init.lua'
no file '/usr/share/lua/5.1/resty/redis.lua'
no file '/usr/share/lua/5.1/resty/redis/init.lua'
no file './resty/redis.so'
no file '/usr/local/lib/lua/5.1/resty/redis.so'
no file '/usr/lib/x86_64-linux-gnu/lua/5.1/resty/redis.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file './resty.so'
no file '/usr/local/lib/lua/5.1/resty.so'
no file '/usr/lib/x86_64-linux-gnu/lua/5.1/resty.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
到lua的share目录,下载lua-resty-redis
cd /usr/share/lua/5.1
git clone https://github.com/openresty/lua-resty-redis.git
在nginx的nginx.conf的http下增加即可:
http {
##
# Basic Settings
##
#由 Nginx 进程分配一块 1M 大小的共享内存空间,用来缓存 IP 黑名单
lua_shared_dict ip_blacklist 1m;
lua_package_path "/usr/share/lua/5.1/lua-resty-redis/lib/?.lua;;";
lua访问redis实现拦截脚本
我们这里的脚本文件放在/var/www/data/lua目录下,配置的时候根据自己的情况进行选择。此处redis中存放的ip信息是通过hkeys获取,考虑到后期可能会增加更多个性化的功能,所以在redis中使用hash存储。
lua脚本如下:
local redis_host = "redis的IP"
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 = "TRSTECH_MT_IP_GUILTY"
-- 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);
ok, err = red:auth("你的密码");
if not ok then
ngx.log(ngx.DEBUG, "Redis connection error while retrieving ip_blacklist: " .. err);
else
local new_ip_blacklist, err = red:hkeys(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