当前位置: 首页 > news >正文

Nginx + ModSecurity + OWASP CRS + Lua + GEOIP2 构建传统WAF

Nginx + ModSecurity + OWASP CRS + Lua + GEOIP2 构建传统WAF

  • 一、环境介绍
  • 二、编译工具安装
  • 三、编译安装ModSecurity v3
  • 四、ModSecurity-Nginx 连接器下载
  • 五、编译安装LuaJIT
  • 六、ngx_devel_kit下载
  • 七、lua-nginx-module下载
  • 八、编译安装lua-resty-core
  • 九、编译安装lua-resty-lrucache
  • 十、OWASP Core Rule Set (CRS)下载
  • 十一、下载geoip2模块和数据库
  • 十二、编译libmaxminddb
  • 十三、编译nginx
  • 十四、集成部署
    • 1、主要目录
    • 2、ModSecurity V3配置文件modsecurity.conf配置
    • 3、owasp crs 配置文件crs-setup.conf配置
    • 4、ModSecurity V3+OWASP CRS集成配置文件main.conf(modsec目录)
    • 5、开启modsecurity 模块配置文件modsec.conf(modsec目录),这里统一写,在server 中引用就可开启
    • 6、误报白名单 whitelist.conf(modsec目录)
    • 7、lua和geoip2(可选)
      • (1)lua cookie 转发脚本,结合hash 做基于cookie 的转发
      • (2)使用geoip2 做地区性l拦截
    • 8、nginx主配置文件nginx.conf
    • 9、默认配置文件default.conf
    • 10、vhost 配置文件样例
    • 11、测试
  • 十五、日志分析工具建议
  • 十六、后话

一、环境介绍

操作系统:龙蜥 OS 8.9
服务器:nginx:10.99.99.99
nginx: 1.25.5
https://github.com/nginx/nginx/releases/tag/release-1.25.5
LuaJIT:v2.1-20250529
https://github.com/openresty/luajit2
ngx_devel_kit: 0.3.4
https://github.com/vision5/ngx_devel_kit
lua-nginx-module: 0.10.28
https://github.com/openresty/lua-nginx-module
lua-resty-core:v0.1.31
https://github.com/openresty/lua-resty-core
lua-resty-lrucache:v0.15
https://github.com/openresty/lua-resty-lrucache
ModSecurity v3(libmodsecurity):3.0.14
https://github.com/owasp-modsecurity/ModSecurity
ModSecurity-Nginx 连接器:1.0.4
https://github.com/owasp-modsecurity/ModSecurity-nginx/
OWASP Core Rule Set (CRS):4.15.0
https://github.com/coreruleset/coreruleset
geoip2:3.4
https://github.com/leev/ngx_http_geoip2_module
libmaxminddb:1.12.2
https://github.com/maxmind/libmaxminddb
GeoLite2 数据库
官方(需要注册):https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/
github分享:https://github.com/P3TERX/GeoLite.mmdb?tab=readme-ov-file

二、编译工具安装

dnf install epel-release -y
dnf install gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel make GeoIP-devel flex bison yajl yajl-devel curl-devel curl doxygen
dnf install -y autoconf automake libtool m4 git

可能会用到代理

git config --global http.proxy socks5h://x:10808
git config --global https.proxy socks5h://x:10808

三、编译安装ModSecurity v3

git clone --recursive https://github.com/owasp-modsecurity/ModSecurity ModSecurity
cd ModSecurity
git submodule update --init --recursive
./build.sh
./configure
make -j$(nproc)
make install

在这里插入图片描述
添加环境变量

tee /etc/profile.d/modsecurity.sh >/dev/null <<EOF
export PKG_CONFIG_PATH=/usr/local/modsecurity/lib/pkgconfig:$PKG_CONFIG_PATH
EOF

加载环境变量

source /etc/profile.d/modsecurity.sh

验证

pkg-config --modversion modsecurity

在这里插入图片描述

四、ModSecurity-Nginx 连接器下载

git clone https://github.com/owasp-modsecurity/ModSecurity-nginx.git

五、编译安装LuaJIT

git clone https://github.com/openresty/luajit2.git
cd luajit2/
make -j$(nproc)
make install PREFIX=/usr/local/luajit

添加环境变量

tee /etc/profile.d/luajit.sh >/dev/null <<EOF
export LUAJIT_LIB=/usr/local/luajit/lib
export LUAJIT_INC=/usr/local/luajit/include/luajit-2.1
export LD_LIBRARY_PATH=/usr/local/luajit/lib:\$LD_LIBRARY_PATH
EOF

加载环境变量

source /etc/profile.d/luajit.sh

创建软连接

ln -sf /usr/local/luajit/bin/luajit /usr/local/bin/luajit

在这里插入图片描述

六、ngx_devel_kit下载

git clone https://github.com/vision5/ngx_devel_kit.git

七、lua-nginx-module下载

git clone https://github.com/openresty/lua-nginx-module.git

八、编译安装lua-resty-core

git clone https://github.com/openresty/lua-resty-core.git
cd lua-resty-core
make install

在这里插入图片描述

九、编译安装lua-resty-lrucache

git clone https://github.com/openresty/lua-resty-lrucache.git
cd lua-resty-lrucache
make install

十、OWASP Core Rule Set (CRS)下载

git clone https://github.com/coreruleset/coreruleset.git

十一、下载geoip2模块和数据库

git clone https://github.com/leev/ngx_http_geoip2_module.git

登陆后下载
https://www.maxmind.com/en/home
在这里插入图片描述

在这里插入图片描述
数据文件是这个三个
在这里插入图片描述

十二、编译libmaxminddb

读取 geoip2 数据库用

./configure
make
make check
make install
ldconfig

创建环境变量

tee /etc/profile.d/maxmind.sh >/dev/null <<EOF
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
EOF

加载环境变量

source /etc/profile.d/maxmind.sh

验证

pkg-config --modversion libmaxminddb

在这里插入图片描述

十三、编译nginx

git clone https://github.com/nginx/nginx.git -b release-1.25.5
./auto/configure \--prefix=/usr/local/nginx \--with-threads \--with-http_ssl_module \--with-http_v2_module \--with-http_realip_module \--with-http_gunzip_module \--with-http_gzip_static_module \--with-http_stub_status_module \--with-http_sub_module \--with-stream \--with-stream_ssl_module \--with-stream_realip_module \--with-stream_ssl_preread_module \--add-dynamic-module=/usr/local/src/ModSecurity-nginx \--add-module=/usr/local/src/ngx_devel_kit \--add-module=/usr/local/src/lua-nginx-module \--add-dynamic-module=/usr/local/src/ngx_http_geoip2_module
make
make install

十四、集成部署

注意:免费证书可以参考此文章申请

1、主要目录

# 虚拟主机配置文件目录,就是server
mkdir /usr/local/nginx/conf/vhost
# stream 流配置文件目录
mkdir /usr/local/nginx/conf/stream
# modsec crs 等安全模块目录
mkdir /usr/local/nginx/conf/modsec

2、ModSecurity V3配置文件modsecurity.conf配置

# 修改SecRuleEngine为On
SecRuleEngine On
# 日志文件位置
SecAuditLogFormat JSON
SecAuditLogType Serial
SecAuditLog /var/log/modsec_audit.json
# 正则匹配深度
SecPcreMatchLimit 100000
SecPcreMatchLimitRecursion 100000

3、owasp crs 配置文件crs-setup.conf配置

# 注释这两行
# SecDefaultAction "phase:1,log,auditlog,pass"
# SecDefaultAction "phase:2,log,auditlog,pass"
# 打开这两行注释,nolog不会记录日志到nginx日志里
# 日志记录位置为modsec_audit.log,根据评分拦截,修改如下
SecDefaultAction "phase:1,nolog,auditlog,pass"
SecDefaultAction "phase:2,nolog,auditlog,pass"

4、ModSecurity V3+OWASP CRS集成配置文件main.conf(modsec目录)

# ModSecurity V3 配置文件
Include /usr/local/src/ModSecurity/modsecurity.conf
# 误报白名单设置
Include /usr/local/nginx/conf/modsec/whitelist.conf
# OWASP CRS 初始化设置
Include /usr/local/src/coreruleset/crs-setup.conf
# OWASP 提供的具体防护规则
Include /usr/local/src/coreruleset/rules/*.conf

5、开启modsecurity 模块配置文件modsec.conf(modsec目录),这里统一写,在server 中引用就可开启

# 开启WAF
modsecurity on;
# 加载WAF规则文件
modsecurity_rules_file /usr/local/nginx/conf/modsec/main.conf;

6、误报白名单 whitelist.conf(modsec目录)

# 这只是示例 根据审计日志/var/log/modsec_audit.log情况写
SecRule REQUEST_URI "@beginsWith /zabbix/jsrpc.php" "id:1000001,phase:1,nolog,pass,ctl:ruleRemoveById=920420"

7、lua和geoip2(可选)

注:lua可以做一些对modsec检测前或者检测后的 功能增强,也可以结geoip2 做限制访问

(1)lua cookie 转发脚本,结合hash 做基于cookie 的转发

local ck = require "resty.cookie"
local cookie, err = ck:new()
if not cookie thenngx.log(ngx.ERR, "failed to instantiate cookie: ", err)return ngx.exit(500)
end-- 尝试获取名为 route_key 的 cookie
local route_key, err = cookie:get("route_key")
if not route_key then-- 生成唯一值(优先用 ngx.var.request_id,否则用更安全的随机数)local new_routeif ngx.var.request_id thennew_route = ngx.var.request_idelse-- 更安全的随机数(非加密安全,但比 math.random 好)new_route = ngx.time() .. ngx.md5(ngx.var.connection .. math.random())end-- 设置 cookie,客户端会收到这个 cookielocal ok, err = cookie:set({key = "route_key",value = new_route,path = "/",httponly = true,max_age = 3600,  -- 1小时(单位:秒)})if not ok thenngx.log(ngx.ERR, "failed to set cookie 'route_key': ", err)-- 可以选择继续执行(即使 Cookie 设置失败),或返回错误-- return ngx.exit(500)end-- 把生成的新值赋给 nginx 变量供 hash 使用ngx.var.route_key = new_route
else-- 已有 cookie,赋值给 nginx 变量ngx.var.route_key = route_key
end

(2)使用geoip2 做地区性l拦截

vi /usr/local/nginx/conf/modsec/geoip_rules.conf
SecGeoLookupDb /usr/local/src/geoip2-db/GeoLite2-Country.mmdbSecRule GEO:COUNTRY_CODE "@streq CN" \"id:1001,phase:1,deny,status:403,log,msg:'Blocked access from China'"

在 main.conf 中添加规则引用

#加载 GeoIP2 国家限制规则
Include /usr/local/nginx/conf/modsec/geoip_rules.conf

8、nginx主配置文件nginx.conf

# 加载模块写在最顶端
load_module /usr/local/nginx/modules/ngx_http_geoip2_module.so;
load_module /usr/local/nginx/modules/ngx_http_modsecurity_module.so;
load_module /usr/local/nginx/modules/ngx_stream_geoip2_module.so;
# 普通用户启动 (useradd nginx -s /sbin/nologin -M)
user nginx;# 配置nginx worker进程个数
#worker_processes 8;
#worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 #01000000 10000000;
#worker_cpu_affinity 0001 0010 0100 1000 0001 0010 1000 0001 0010 0100 1000;worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;#worker_processes 2;
#worker_cpu_affinity 0101 1010;# 配置日志存放路径
error_log   logs/error.log error;
pid logs/nginx.pid;# nginx事件处理模型优化
events {worker_connections 51200; # 当个进程允许的客户端最大连接数use epoll;}# 配置nginx worker进程最大打开文件数
worker_rlimit_nofile 65535;http {# lua配置lua_package_path "/usr/local/lib/lua/?.lua;/usr/local/lib/lua/?/init.lua;;";lua_package_cpath "/usr/local/lib/lua/?.so;;";init_by_lua_block {require "resty.core"collectgarbage("collect")}# 加载GeoiIP2 数据库geoip2 /usr/local/src/geoip2-db/GeoLite2-City.mmdb {auto_reload 5m;$geoip2_data_country_code country iso_code;$geoip2_data_city_name city names en;$geoip2_data_subdivision subdivision 0 names en;$geoip2_data_latitude location latitude;$geoip2_data_longitude location longitude;}# 隐藏版本号server_tokens off;# 设置日志格式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/accesslog.log  main;        # 开启高效文件传输模式include mime.types;                         # 媒体类型default_type    application/octet-stream;   # 默认媒体类型charset  utf-8;                             # 默认字符集sendfile    on;tcp_nopush  on;                             # 只有在sendfile开启模式下有效# 设置连接超时时间keepalive_timeout  65;      # 设置客户端连接保持会话的超时时间,超过则服务器会关闭该连接tcp_nodelay on;             # 打开tcp_nodelay,在包含了keepalive参数才有效果client_header_timeout 15;    # 设置客户端请求有超时时间,该时间内客户端未发送数据,nginx将返回‘Request time out(408)’错误client_body_timeout 15;    # 设置客户端请求体超时时间,同上send_timeout 15;            # 设置相应客户端的超时时间,超时nginx将会关闭连接# 上传文件大小设置(动态引用)client_max_body_size 10m;# 数据包头部缓存大小client_header_buffer_size    1k;        #默认请求包头信息的缓存    large_client_header_buffers  4 4k;      #大请求包头部信息的缓存个数与容量# 压缩处理gzip on;                           #开启压缩gzip_min_length 1k;                #小文件不压缩gzip_comp_level 4;                 #压缩比率gzip_buffers 4 16k;                #压缩缓冲区大小,申请4个单位为16K的内存作为亚索结果流缓存    gzip_http_version 1.1;             # 默认压缩版本#对特定文件压缩,类型参考mime.typesgzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;gzip_vary on;gzip_disable "MSIE[1-6]\."; # 设置fastcgifastcgi_cache_path /usr/local/nginx/fastcgi_cache levels=1:2keys_zone=TEST:10minactive=5m;       # 为FastCGI缓存指定一个路径,目录结构等级,关键字区域存储时间和非活动删除时间fastcgi_cache_key "$scheme$request_method$host$request_uri";fastcgi_connect_timeout 300;   # 指定连接到后端FastCGI的超时时间fastcgi_send_timeout 300;      # 向FastCGI传送请求的超时时间,这个值是指已经完成两次握手后向FastCGI传送请求的超时时间   fastcgi_read_timeout 300;      # 接收FastCGI应答的超时时间,这个值是指已经完成两次握手后接收FastCGI应答的超时时间    fastcgi_buffer_size 16k;       # 缓冲区大小 fastcgi_buffers 16 16k;fastcgi_busy_buffers_size 16k;      fastcgi_temp_file_write_size 16k;   # 在写入fastcgi_temp_path时将用多大的数据块,默认值是fastcgi_buffers的两倍  fastcgi_cache TEST;                 # 开启FastCGI缓存并且为其制定一个名称fastcgi_cache_valid 200 302 1h;fastcgi_cache_valid 301 1d;fastcgi_cache_valid any 1m;         # 为指定的应答代码指定缓存时间,上例中将200,302应答缓存一小时,301应答缓存1天,其他为1分钟fastcgi_cache_min_uses 1;           # 5分钟内某文件1次也没有被使用,那么这个文件将被移除fastcgi_cache_use_stale error timeout invalid_header http_500;# 内存缓存open_file_cache   max=2000  inactive=20s; #设置服务器最大缓存文件数量,关闭20秒内无请求的文件open_file_cache_valid    60s;             #文件句柄的有效时间是60秒,60秒后过期     open_file_cache_min_uses 5;               #只有访问次数超过5次会被缓存  open_file_cache_errors   off;# 引入子配置文件     include vhost/*.conf;}#配置tcp代理,需要额外加载stream相关模块
stream {
#
#    upstream cloudsocket {
#       hash $remote_addr consistent;
#      # $binary_remote_addr;
#      server 192.168.182.155:3306 weight=5 max_fails=3 fail_timeout=30s;
#    }
#    server {
#       listen 3306;#数据库服务器监听端口
#       proxy_connect_timeout 10s;
#       proxy_timeout 300s;#设置客户端和代理服务之间的超时时间,如果5分钟内没操作将自动断开。
#       proxy_pass cloudsocket;
#    }# 引入子配置文件     include stream/*.conf;
}

9、默认配置文件default.conf

server {listen 80 default_server;server_name _; # 匹配所有域名include /usr/local/nginx/conf/modsec/modsec.conf;location / {# 返回 403 错误,禁止 IP 地址访问return 403;}
}server {listen 443 ssl default_server;server_name _;include /usr/local/nginx/conf/modsec/modsec.conf;ssl_certificate    /usr/local/nginx/conf/cert/gubeisz.net/fullchain.cer;ssl_certificate_key      /usr/local/nginx/conf/cert/gubeisz.net/gubeisz.net.key;# 其他 SSL 配置location / {return 403;}
}

10、vhost 配置文件样例

upstream zabbix{server 10.99.50.110:80 weight=1 max_fails=2 fail_timeout=10;
#server 192.168.2.101 down;#标记为down 剔除负载均衡队列
}server
{listen 80;server_name zabbix.xxx.net;include /usr/local/nginx/conf/modsec/modsec.conf;#access_log logs/example-access.log main;#error_log logs/example-error.log  error;proxy_buffering on; #开启buffer缓存,异步应答客户端请求,效率高proxy_buffer_size 4k;proxy_buffers 2 4k;proxy_busy_buffers_size 4k;#proxy_temp_path /tmp/nginx_proxy_tmp 1 2;proxy_max_temp_file_size 20M;proxy_temp_file_write_size 8k;location /{proxy_pass      http://zabbix;proxy_method $request_method;proxy_set_header Host   $host;proxy_set_header X-Real-IP      $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
}server {listen 443 ssl;server_name  zabbix.xxx.net;include /usr/local/nginx/conf/modsec/modsec.conf;#access_log logs/example-access.log main;#error_log logs/example-error.log  error;ssl_certificate    /usr/local/nginx/conf/cert/xxx.net/fullchain.cer;ssl_certificate_key      /usr/local/nginx/conf/cert/xxx.net/xxx.net.key;ssl_session_timeout 5m;ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;#ssl_ciphers   ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL;ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!3DES:!ADH:!RC4:!DH:!DHE;ssl_prefer_server_ciphers  on;location /{proxy_pass      http://zabbix;proxy_method $request_method;proxy_set_header Host   $host;proxy_set_header X-Real-IP      $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
}

11、测试

sql注入

curl 'http://zabbix.xxx.net/zabbix?id=1%20or%201=1'

在这里插入图片描述
显示403,被拦截了,日志显示如下
在这里插入图片描述

十五、日志分析工具建议

ModSecurity Audit Log + ELK Stack (Elasticsearch + Logstash + Kibana)

十六、后话

我在测试部署的过程中,发现最大的问题就是误报;升高crs的评分如下,会导致真正的攻击也不被拦截,做法是使用modsec 观察模式+ crs评分机制,找到误报,手动加白名单,再开启modsec 拦截模式

#SecAction \
#    "id:900110,\
#    phase:1,\
#    pass,\
#    t:none,\
#    nolog,\
#    tag:'OWASP_CRS',\
#    ver:'OWASP_CRS/4.16.0-dev',\
#    setvar:tx.inbound_anomaly_score_threshold=5,\
#    setvar:tx.outbound_anomaly_score_threshold=4"

手动处理加白名单比较痛苦,谁也没时间整天盯着这个玩意,因此转战下一个测试
Coraza + KoaLA

http://www.dtcms.com/a/266522.html

相关文章:

  • 【ACP】阿里云云计算高级运维工程师--ACP
  • 服务器的IO性能怎么看?
  • 【机器学习3】机器学习(鸢尾花分类)项目核心流程与企业实践差异分析
  • 【问题记录】MySQL错误1140深度解析:聚合查询中的GROUP BY问题解决方案
  • PP-OCR:一款实用的超轻量级OCR系统
  • LINUX74 LAMP5
  • Poetry 在 Linux 和 Windows 系统中的安装步骤
  • 基于开源AI智能名片链动2+1模式S2B2C商城小程序的超级文化符号构建路径研究
  • WPF学习笔记(23)Window、Page与Frame、ViewBox
  • OneCode采用虚拟DOM结构实现服务端渲染的技术实践
  • 负载均衡--四层、七层负载均衡的区别
  • java中this. 和 this::的区别和用法
  • Apache RocketMQ进阶之路阅读笔记和疑问
  • RabbitMQ用法的6种核心模式全面解析
  • 论文解析:AutoMedPrompt框架的核心与实现示例
  • 【Qt】在windows环境下,配置QtCreator中的clang-format
  • P/Invoke 在默认封送(marshalling)规则下,常见托管 ⇄ 非托管类型的对应关系
  • Jenkins-Publish HTML reports插件
  • Oracle DB和PostgreSQL,OpenGauss主外键一致性的区别
  • 强化学习 (10)蒙特卡洛
  • SRE - - PV、UV、VV、IP详解及区别
  • Web基础关键_008_JavaScript 的 BOM、ES6、构造函数、原型
  • 利用 AI 打造的开发者工具集合
  • 【Unity笔记02】订阅事件-自动开门
  • 模型部署与推理--利用libtorch模型部署与推理
  • Redisearch接入SpringBoot项目使用
  • MySQL 中 -> 和 ->> 操作符的区别
  • github上部署自己的静态项目
  • 【狂飙AGI】第7课:AGI-行业大模型(系列1)
  • jsonCPP 开源库详解