RockyLinux9.5系统编译nginx步骤并使用waf和geoip模块

这次编译新增waf防火墙模块,GeoIP模块和rtmp模块

waf防火墙信息:

网址:https://ngx-waf-docs.pages.dev/zh-cn/

使用的是长期维护版(LTS),所以配置waf使用LTS 版本的用例

1、先安装nginx

# dnf install yum-utils
# vim /etc/yum.repos.d/nginx.repo
[nginx-stable] # 整个表示的是稳定版本
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline] # 整个表示的是最新版本
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

# dnf -y install nginx # 版本是1.26.3 ,后面编译使用到的版本也下载整个使用 
# cat /usr/lib/systemd/system/nginx.service # 查看二进制文件路径
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
Environment="conffile=/etc/nginx/nginx.conf"
EnvironmentFile=-/etc/sysconfig/nginx
ExecStart=/usr/sbin/nginx -c ${conffile}
ExecReload=/bin/sh -c "/bin/kill -s HUP $(/bin/cat /var/run/nginx.pid)"
ExecStop=/bin/sh -c "/bin/kill -s TERM $(/bin/cat /var/run/nginx.pid)"

[Install]
WantedBy=multi-user.target
# 查看默认的当前nginx参数,下面会用到,届时直接替换nginx二进制文件使用
# /usr/sbin/nginx -V
nginx version: nginx/1.26.3
built by gcc 11.4.1 20231218 (Red Hat 11.4.1-3) (GCC) 
built with OpenSSL 3.0.7 1 Nov 2022 (running with OpenSSL 3.2.2 4 Jun 2024)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

2、前提介绍

# cd /usr/local/src
# wget https://nginx.org/download/nginx-1.26.3.tar.gz
# tar -zxv -f nginx-1.26.3.tar.gz
# git clone -b lts https://mirrors.chenby.cn/https://github.com/ADD-SP/ngx_waf.git

依赖组件的压缩包路径:

[root@kafka1 ~]# cd /usr/local/src/
[root@kafka1 src]# ll
total 23824
drwxr-xr-x 13 root root      4096 2025-04-03_10:08:26 libinjection-src
drwxr-xr-x 14 root root      4096 2025-04-03_10:09:57 libsodium-src
drwxr-xr-x  8  502 games      173 2025-02-05_19:22:01 nginx-1.26.3
-rw-r--r--  1 root root   1260179 2025-02-05_19:22:01 nginx-1.26.3.tar.gz
drwxrwxr-x  6 root root      4096 2021-05-25_15:42:16 nginx-rtmp-module-1.2.2
-rw-r--r--  1 root root    519934 2025-03-29_08:54:11 nginx-rtmp-module-1.2.2.tar.gz
drwxr-xr-x 10 root root      4096 2025-04-02_18:20:02 ngx_waf
drwxr-xr-x  7 root root       172 2025-04-03_10:06:32 uthash

3、安装依赖包

dnf -y install epel-release
dnf -y install bison flex libxml2 libxml2-devel libxslt libxslt-devel perl-CPAN google-perftools google-perftools-devel
dnf -y install gcc gcc-c++ make zlib zlib-devel pcre pcre-devel pcre2-devel openssl openssl-devel perl-devel perl-ExtUtils-Embed gd-devel

4、编译

./configure --prefix=/etc/nginx \
--user=nginx \
--group=nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--with-compat \
--with-file-aio \
--with-threads \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-http_v3_module \
--with-mail \
--with-mail_ssl_module \
--with-stream \
--with-stream_realip_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_degradation_module \
--with-http_perl_module \
--with-google_perftools_module \
--with-debug \
--add-module=/usr/local/src/nginx-rtmp-module-1.2.2 \
--add-module=/usr/local/src/ngx_waf

#从这行开始--with-http_xslt_module=dynamic \(含这行)到最后的部分是这次编译新增的参数
编译报错:
checking for uthash library ... not found
./configure: error: the ngx_http_waf_module module requires the uthash library.

Please run:
            cd /usr/local/src \
        &&  git clone https://github.com/troydhanson/uthash.git \
        &&  export LIB_UTHASH=/usr/local/src/uthash \
        &&  cd /usr/local/src/nginx-1.26.3
        
解决办法:
# 若是该路径下已经有整个目录了,则不用再下载了
cd /usr/local/src 
git clone https://mirrors.chenby.cn/https://github.com/troydhanson/uthash.git 
export LIB_UTHASH=/usr/local/src/uthash 
cd /usr/local/src/nginx-1.26.3
编译报错:
adding module in /usr/local/src/ngx_waf
/usr/bin/flex
/usr/bin/bison
checking for uthash library ... found
checking for C99 features ... found
checking for injection library ... not found
./configure: error: the ngx_http_waf_module module requires the injection library.

Please run
        # You can remove directories libinjection-src and libinjection after installing the ngx_http_waf_module.
        git clone https://github.com/libinjection/libinjection.git libinjection-src \
    &&  cd libinjection-src \
    &&  ./autogen.sh \
    &&  ./configure --prefix=/usr/local/src/nginx-1.26.3/libinjection \
    &&  make -j$(nproc) && make install \
    &&  export LIB_INJECTION=/usr/local/src/nginx-1.26.3/libinjection \
    &&  cd /usr/local/src/nginx-1.26.3

解决办法:
# 若是该路径下已经有整个目录了,则不用再下载了
cd /usr/local/src 
git clone https://mirrors.chenby.cn/https://github.com/libinjection/libinjection.git libinjection-src
cd libinjection-src 
./autogen.sh 
./configure --prefix=/usr/local/src/nginx-1.26.3/libinjection
make -j$(nproc) && make install 
export LIB_INJECTION=/usr/local/src/nginx-1.26.3/libinjection
cd /usr/local/src/nginx-1.26.3
编译报错:
adding module in /usr/local/src/ngx_waf
/usr/bin/flex
/usr/bin/bison
checking for uthash library ... found
checking for C99 features ... found
checking for injection library ... found
checking for sodium library ... not found
./configure: error: the ngx_http_waf_module module requires the sodium library.

Please run:
    On Ubuntu or Debian:
            apt-get update && apt-get install --yes libsodium23 libsodium-dev
    On Alpine:
            apk update && apk add libsodium libsodium-dev
    On other OS:
            # You can remove directories libsodium-src and libsodium after installing the ngx_http_waf_module.
            git clone https://github.com/jedisct1/libsodium.git --branch stable libsodium-src \
        &&  cd libsodium-src \
        &&  ./configure --prefix=/usr/local/src/nginx-1.26.3/libsodium --with-pic \
        &&  make -j$(nproc) && make check -j $(nproc) && make install \
        &&  export LIB_SODIUM=/usr/local/src/nginx-1.26.3/libsodium \
        &&  cd /usr/local/src/nginx-1.26.3

解决办法:
# 若是该路径下已经有整个目录了,则不用再下载了
cd /usr/local/src 
git clone https://mirrors.chenby.cn/https://github.com/jedisct1/libsodium.git --branch stable libsodium-src 
cd libsodium-src 
./configure --prefix=/usr/local/src/nginx-1.26.3/libsodium --with-pic
make -j$(nproc) && make check -j $(nproc) && make install 
export LIB_SODIUM=/usr/local/src/nginx-1.26.3/libsodium
cd /usr/local/src/nginx-1.26.3
# 若编译有如下提示,则按照提示进行执行
./configure: error: the ngx_http_waf_module module requires the following command to be run to generate the necessary files.

    cd /usr/local/src/ngx_waf && make && cd /usr/local/src/nginx-1.26.3

编译成功后的结果如下:
 + ngx_http_waf_module was configured
checking for PCRE2 library ... found
checking for OpenSSL library ... found
checking for OpenSSL QUIC support ... not found
checking for OpenSSL QUIC compatibility ... found
checking for zlib library ... found
checking for libxslt ... found
checking for libexslt ... found
checking for GD library ... found
checking for GD WebP support ... found
checking for perl
 + perl version: This is perl 5, version 32, subversion 1 (v5.32.1) built for x86_64-linux-thread-multi
 + perl interpreter multiplicity found
checking for GeoIP library ... found
checking for GeoIP IPv6 support ... found
checking for Google perftools ... found
creating objs/Makefile

Configuration summary
  + using threads
  + using system PCRE2 library
  + using system OpenSSL library
  + using system zlib library

  nginx path prefix: "/etc/nginx"
  nginx binary file: "/usr/sbin/nginx"
  nginx modules path: "/usr/lib64/nginx/modules"
  nginx configuration prefix: "/etc/nginx"
  nginx configuration file: "/etc/nginx/nginx.conf"
  nginx pid file: "/var/run/nginx.pid"
  nginx error log file: "/var/log/nginx/error.log"
  nginx http access log file: "/var/log/nginx/access.log"
  nginx http client request body temporary files: "/var/cache/nginx/client_temp"
  nginx http proxy temporary files: "/var/cache/nginx/proxy_temp"
  nginx http fastcgi temporary files: "/var/cache/nginx/fastcgi_temp"
  nginx http uwsgi temporary files: "/var/cache/nginx/uwsgi_temp"
  nginx http scgi temporary files: "/var/cache/nginx/scgi_temp"
# 如下这个sed命令也需要执行,waf需要的
# sed -i 's/^\(CFLAGS.*\)/\1 -fstack-protector-strong -Wno-sign-compare/' objs/Makefile

# make
# make后的成功结果如下:
sed -e "s|%%PREFIX%%|/etc/nginx|" \
        -e "s|%%PID_PATH%%|/var/run/nginx.pid|" \
        -e "s|%%CONF_PATH%%|/etc/nginx/nginx.conf|" \
        -e "s|%%ERROR_LOG_PATH%%|/var/log/nginx/error.log|" \
        < man/nginx.8 > objs/nginx.8
make[1]: Leaving directory '/usr/local/src/nginx-1.26.3'

若是编译的时候报错如下,大概率是在nginx编译的时候加了额外下载的openssl,zlib和perl,没有使用系统默认安装好的
collect2: error: ld returned 1 exit status
make[1]: *** [objs/Makefile:372: objs/nginx] Error 1
make[1]: Leaving directory '/root/nginx-1.26.3'
make: *** [Makefile:10: build] Error 2

或者是跟使用的openssl版本有关,要么降低版本,要么升级版本 (有待验证)

5、替换原有的nginx二进制文件

# ll /usr/sbin/nginx
-rwxr-xr-x 1 root root 1663672 2025-02-05_23:19:37 nginx
# cp /usr/sbin/nginx /usr/sbin/nginx.bak
# cp /usr/local/src/nginx-1.26.3/objs/nginx /usr/sbin/nginx
cp: overwrite '/usr/sbin/nginx'? y
# ll /usr/sbin/nginx
-rwxr-xr-x 1 root root 12703984 2025-04-03_11:36:20 nginx  # 可以明显看到nginx大了好多

6、启动nginx

# 启动报错
# systemctl status nginx -l
× nginx.service - nginx - high performance web server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; preset: disabled)
     Active: failed (Result: exit-code) since Thu 2025-04-03 11:38:38 CST; 7s ago
       Docs: http://nginx.org/en/docs/
    Process: 174001 ExecStart=/usr/sbin/nginx -c ${conffile} (code=exited, status=1/FAILURE)
        CPU: 11ms

Apr 03 11:38:38 kafka1 systemd[1]: Starting nginx - high performance web server...
Apr 03 11:38:38 kafka1 nginx[174001]: Can't locate nginx.pm in @INC (you may need to install the nginx module) (@INC contains: /usr/local/lib64/perl5/5.32 /usr/local/sh>Apr 03 11:38:38 kafka1 nginx[174001]: BEGIN failed--compilation aborted.
Apr 03 11:38:38 kafka1 nginx[174001]: nginx: [alert] perl_parse() failed: 2
Apr 03 11:38:38 kafka1 systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Apr 03 11:38:38 kafka1 systemd[1]: nginx.service: Failed with result 'exit-code'.
Apr 03 11:38:38 kafka1 systemd[1]: Failed to start nginx - high performance web server.

# 具体错误信息如下
# tail -n 30 /var/log/messages
Can't locate nginx.pm in @INC (you may need to install the nginx module) (@INC contains: /usr/local/lib64/perl5/5.32 /usr/local/share/perl5/5.32 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5).

解决办法:
# cd /usr/local/src/nginx-1.26.3
# mkdir -p /usr/local/lib64/perl5/5.32/auto/nginx
# cp objs/src/http/modules/perl/blib/arch/auto/nginx/nginx.so /usr/local/lib64/perl5/5.32/auto/nginx
# cp objs/src/http/modules/perl/blib/lib/nginx.pm /usr/local/lib64/perl5/5.32/

# 注:网上参考文档中还需要cp整个文件:objs/src/http/modules/perl/blib/arch/auto/nginx/nginx.bs,不过这次编译后的objs目录中没这个文件。

# pwd
/usr/local/lib64/perl5
# tree .
.
└── 5.32
    ├── auto
    │   └── nginx
    │       └── nginx.so
    └── nginx.pm

3 directories, 2 files

然后再次启动nginx就可以了

7、配置waf防火墙

```
# cp -r /usr/local/src/ngx_waf/ /etc/nginx/
# vim /etc/nginx/conf.d/default.conf
server {
    ...
    # on 表示启用,off 表示关闭。
    waf on;
    # 规则文件所在目录的绝对路径,必须以 / 结尾。
    waf_rule_path /etc/nginx/ngx_waf/assets/rules/;
    # 防火墙工作模式,STD 表示标准模式。
    waf_mode STD;
    # CC 防御参数,1000 每分钟请求次数上限,超出上限后封禁对应 ip 60 分钟。
    waf_cc_deny rate=1000r/m duration=60m;
    # 最多缓存 50 个检测目标的检测结果,对除了 IP 黑白名单检测、CC 防护和 POST 检测以外的所有检测生效。
    waf_cache capacity=50;
    ...
}

# systemctl restart nginx

1、测试waf防火墙

简易测试

运行下列命令,如果输出 403 则表示模块正常工作。

curl -I -o /dev/null --user-agent bench -s -w "%{http_code}\\n" https://example.com/
或者是
curl -I -o /dev/null --user-agent bench -s -w "%{http_code}\\n" http://127.0.0.1/

其他测试参考官方文档:https://ngx-waf-docs.pages.dev/zh-cn/guide/test.html

2、参数文档

地址:https://ngx-waf-docs.pages.dev/zh-cn/advance/directive.html

3、日志

  • 拦截日志

拦截日志存储在nginx的 error.log 中。拦截记录的日志等级为 ALERT。 格式为 ngx_waf: [规则类型][触发的具体规则]

# cat /path/to/error.log | grep ngx_waf:

比如访问:http://a.sql,就会有如下日志
# cat /var/log/nginx/error.log |grep "ngx_waf"
2025/04/09 15:17:01 [alert] 238585#238585: *1 ngx_waf: [BLACK-URL][(?i)(?:\.(?:bak|inc|old|mdb|sql|backup|java|class))$], client: 192.168.2.200, server: localhost, request: "GET /a.sql HTTP/1.1", host: "10.16.16.20"
  • 调试日志

当您在 nginx 的配置文件中将错误日志的等级调整为 debug 时,会在 error.log 中输出调试日志, 用以排障。格式为 ngx_waf_debug: 调试信息

您可以使用下列命令快速查看调试日志。

cat /path/to/error.log | grep ngx_waf_debug:

下面是一段典型的调式日志,表示了一次 CC 防御检测的流程。

2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: Start the CC inspection process.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: The module context has been obtained.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: The configuration of the module has been obtained.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: Detection has begun.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: Shared memory is locked.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: Shared memory is unlocked.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: Detection is over.
2021/02/21 20:35:33 [debug] 6666#0: *1 ngx_waf_debug: The CC detection process is fully completed.
  • 自定义日志格式

nginx 允许自定义日志格式,并且一个 server 块可以同时写入多个日志文件, 这就给我我们自定义日志格式的机会。本模块提供了几个内置变量,稍加利用即可实现。 下面是一个例子。

http {
    ...
    log_format  main    '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
    log_format yaml     '- remote_addr: "$remote_addr"\n'
                        '  remote_user: "$remote_user"\n'
                        '  time_local: "$time_local"\n'
                        '  request: "$request"\n'
                        '  status: "$status"\n'
                        '  body_bytes_sent: "$body_bytes_sent"\n'
                        '  http_referer: "$http_referer"\n'
                        '  http_user_agent: "$http_user_agent"\n'
                        '  http_x_forwarded_for: "$http_x_forwarded_for"\n'
                        '  waf_blocked: $waf_blocked\n'
                        '  waf_spend: $waf_spend\n'
                        '  waf_rule_type: "$waf_rule_type"\n'
                        '  waf_rule_details: "$waf_rule_details"\n';
    server {
        ...
        access_log  logs/access.log  main;
        access_log  logs/access.yml  yaml   if=$waf_log;
        access_log  logs/waf.yml     yaml   if=$waf_blocking_log;
    }
}

上述配置会有如下效果:

  • 常规的访问日志写入 logs/access.log 中。
  • YAML 格式的访问日志写入 logs/access.yml 中。
  • YAML 格式的拦截日志写入 logs/waf.yml 中。

4、最佳实践

针对路径或文件限流

有时你可能想要限制不同的路径或文件的请求速率,比如静态资源和动态资源使用不同的速率限制。

LTS 版本

# 将静态资源的请求速率限制到 10,000 次/分钟。
location /static/ {
    waf_cc_deny rate=10000r/m duration=1h;
}

# 将动态资源的请求速率限制到 2,000 次/分钟。
location /dynamic/ {
    waf_cc_deny rate=2000r/m duration=1h;
}

Current 版本

http {
    waf_zone name=waf size=20m;
    server {
        # 将静态资源的请求速率限制到 10,000 次/分钟。
        location /static/ {
            waf_cc_deny rate=10000r/m duration=1h zone=waf:cc_static;
        }

        # 将动态资源的请求速率限制到 2,000 次/分钟。
        location /dynamic/ {
            waf_cc_deny rate=2000r/m duration=1h zone=waf:cc_dynamic;
        }
    }
}

开启验证码(仅限最新的 Current 版本)

当你的站点受到 CC 攻击时开启验证码是不错的选择,因为验证码可以帮助你进行人机识别。

  1. 本模块目前支持三种验证码,分别是 hCaptchareCAPTCHAv2reCAPTCHAv3。你应该选择一个并从其网站上申请到 Sitekey 和 Secret。

  2. 修改 nginx 的配置文件

    • 为整个站点开启验证码。

      server {
          waf_captcha on prov=hCaptcha secret=your_secret sitekey=your_sitekey;
      }
      
    • 为某个路径开启验证码

      location {
          waf_captcha on prov=hCaptcha secret=your_secret sitekey=your_sitekey;
      }
      
    • 当访问频率过高时开启验证码

      http {
          waf_zone name=waf size=20m;
          server {
              waf_cc_deny on rate=1000r/m duration=1h zone=waf:cc;
              waf_captcha off prov=hCaptcha secret=your_secret sitekey=your_sitekey;
              waf_action cc_deny=CAPTCHA zone=waf:action;
          }
      }
      
  3. 重启 nginx。

警告

如果你配置了伪静态规则,或者配置了路径重写,你需要保证路径 /captcha 不被重写。 下面是一个修改示例。

# BEGIN 修改前
if (! -e $request_filename) {
    rewrite ^(.*)$ index.php?s=$1 last break;
}
# END   修改前

# BEGIN 修改后
try_files $uri $uri/ index.php?s=$1;
# END   修改后

SEO 优化

开启验证码会影响到搜索引擎的工作, 所有你可以使用 waf_verify_bot 来放行某些搜索引擎的爬虫。

waf_verify_bot on;

拦截请求时启用验证码 (>=v10.0.0)

如今,许多攻击者都会使用自动工具攻击服务器,这些自动工具会尝试每一个漏洞,有的会被安全措施所拦截,有的则可以躲避检测。 如果攻击者觉得你的价值比较高,可能会手动攻击你的服务。我们并不能完美地防御这些攻击,但却能很简单地提高攻击的成本。

当一个请求被拦截时,ngx_waf 会对该 IP 启用验证码,此时该 IP 想要继续访问就必须完成验证码。这基本可以废掉多数的自动攻击工具,因为这些工具会尝试每一个漏洞,而我们总能识别一些明显的攻击请求并启用验证码,而自动工具时难以通过验证的。对于手动攻击者,这也能提高他们的时间成本。

下面是一个示例配置。

http {
    waf_zone name=waf size=20m;
    
    server {
        waf_captcha off prov=xxx sitekey=xxx secret=xxx;
        waf_action blacklist=CAPTCHA zone=waf:action;
    }
}

被攻击时降低带宽占用

当你受到 CC 攻击时,攻击者的 IP 已经被 CC 防护拉黑,但是你的上下行带宽依然很高, 这是因为 CC 防护会返回一个 503 状态码,因此占用了你的带宽。

你可以使用下面的配置来降低带宽占用。

LTS 版本

waf_http_status cc_deny=444;

Current 版本

waf_action cc_deny=444;

444 状态码是 nginx 定义的一个非标准的 HTTP 状态码,其作用就是直接关闭连接,不再发送任何数据。

警告

如果你使用了 444 状态码,那么在用户看来你的网站就像是不存在一样。这是因为网站出错一般都会有 HTTP 状态码用来提示错误, 如果访问一个网站连错误提示都没有,那么大概率是这个网站不存在。

抵御分布式 CC 攻击(分布式 HTTP 洪水)

CC 攻击(HTTP 洪水)是指发送大量的 HTTP 请求来耗尽服务器的资源。

如果攻击者使用的 IP 较少则防御较为简单,因为只需要限制 IP 的请求频率, 但是如果攻击者使用大量的 IP 进行攻击,仅仅限制 IP 的请求频率是无济于事的。 这种使用大量 IP 进行 CC 攻击的方式称为分布式 CC 攻击或分布式 HTTP 洪水。

本模块提供了一些缓解方式。

  • 开启验证码

  • 降低带宽占用

  • 使用五秒盾

你可能听说过 Cloudflare 的五秒盾,本模块的五秒盾和 Cloudflare 的完全不同。 它的功能是检测客户端是否能够正确地支持 Cookie,比如发送 Cookie 和正确地处理 Set-Cookie 响应头。

你可以从本项目的 assets/ 目录下找到 under-attack.html 并将其拷贝到某个路径下,然后通过修改 nginx 的配置文件来开启五秒盾。

LTS 版本

  • 为整个网站开启五秒盾

    server {
        waf_under_attack on file=/path/to/under_attack.html;
    }
    
  • 为某个路径开启五秒盾

    location /path {
        waf_under_attack on file=/path/to/under_attack.html;
    }
    

Current 版本

  • 为整个网站开启五秒盾

    server {
        waf_under_attack on;
    }
    
  • 为某个路径开启五秒盾

    location /path {
        waf_under_attack on;
    }
    

开启 ModSecurity (仅限最新的 Current 版本)

本项目提供的默认规则十分容易被绕过,而且许多用户并没有足够的能力和时间去维护一套可靠的规则。

这种情况下你可以选择启用 ModSecurity并启用 OWSAP(Open Web Application Security Project) 核心规则集来增强防护能力。

第一步:下载规则集

cd /usr/local/src
git clone https://github.com/SpiderLabs/ModSecurity.git
git clone https://github.com/coreruleset/coreruleset.git

第二步:拷贝规则文件

mkdir -p /etc/nginx/rules/modsecurity
cp coreruleset/crs-setup.conf.example /etc/nginx/rules/modsecurity/crs-setup.conf
cp ModSecurity/modsecurity.conf-recommended /etc/nginx/rules/modsecurity/modsecurity.conf
cp ModSecurity/unicode.mapping /etc/nginx/rules/modsecurity/unicode.mapping
cp -r coreruleset/rules /etc/nginx/rules/modsecurity/owasp

第三步:修改规则文件

  1. 编辑 /etc/nginx/rules/modsecurity/modsecurity.conf,将 SecRuleEngine DetectionOnly 修改为 SecRuleEngine on

  2. 编辑/etc/nginx/rules/modsecurity/modsecurity.conf,在文件末尾追加下列内容。

Include /etc/nginx/rules/modsecurity/crs-setup.conf
Include /etc/nginx/rules/modsecurity/owasp/*.conf

第四步:加载规则

编辑 nginx 的配置文件。

waf_modsecurity on file=/etc/nginx/rules/modsecurity/modsecurity.conf;

第五步:优化性能

启用规则后会导致性能大幅度降低,这是因为 ModSecurity 的规则引擎十分复杂,但这也为其带来了强大的功能。

一般来说我们无需对一些静态文件进行检查,因此可以根据这一点来优化性能。

编辑 nginx 的配置文件。

location ~* \.(gif|jpg|png|jpeg|webp|bmp|swf)$ {
    waf_modsecurity off;
}

location ~* \.(html|htm|js|css)$ {
    waf_modsecurity off;
}

第六步:重启 nginx

nginx -s stop
nginx

为什么不用 nginx -s reload ?

因为目前 ModSecurity 存在内存泄露的 bug,直接重载 nginx 会导致内存泄露和性能降低。

第七步:测试

此时你应该检查网站的运行情况,确保不会因为误报而导致网站异常。

第八步:处理误报

处理误报需要对 ModSecurity 的规则语法有所了解,本文不提供这类信息,你可以访问下面的链接来获取相关信息。

https://www.netnea.com/cms/apache-tutorial-7_including-modsecurity-core-rules/#step_7_handling_false_positives_disabling_individual_rules)

Reference Manual (v2.x) · SpiderLabs/ModSecurity Wiki

8、国家地区IP限制访问

nginx要获取到ip地理位置,需要在nginx引用第三方ngx_http_geoip2_module模块,而ngx_http_geoip2_module模块依赖libmaxminddb;另外ip对应的地理位置离线的需要从GeoIP2站点上下载下来;最后在nginx.conf文件中引用ngx_http_geoip2_module模块,配置离线数据库才可以获取地理位置

当前网上大部分Nginx + GeoIP 的教程都是 GeoIP 老版本的已经不适用了当前最新版本的Nginx二进制编译安装

MaxMind GeoIP 已经被弃用了一段时间。对于您最新的地理定位需求,请改用 MaxMind GeoIP2(或免费版本的 GeoLite2)。

1、安装libmaxminddb

#下载包
wget https://github.com/maxmind/libmaxminddb/releases/download/1.7.1/libmaxminddb-1.7.1.tar.gz
tar -zxvf libmaxminddb-1.7.1.tar.gz
cd libmaxminddb-1.7.1/
#安装
./configure
make
make check
make install
#编译安装完成后需要将库的安装位置添加到系统路径中
sh -c "echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf"
ldconfig

2、使用http_geoip2_module 模块重新编译nginx

需要安装 http_geoip2_module 模块,注意和官方文档中的 http_geoip_module 模块是不一样的,区别在于后者不支持新的 GeoIP2 数据库。

官方默认的编译参数用的模块是:http_geoip_module,在这里使用 http_geoip2_module 模块

文档地址:https://github.com/leev/ngx_http_geoip2_module/

# cd /usr/local/src/
# wget https://github.com/leev/ngx_http_geoip2_module/archive/refs/tags/3.4.tar.gz
# tar -zxv -f 3.4.tar.gz
# cd ngx_http_geoip2_module-3.4

# 再次编译nginx,去掉参数:--with-http_geoip_module,加上参数:--add-module=/usr/local/src/ngx_http_geoip2_module-3.4
# 最终的编译参数如下:
./configure --prefix=/etc/nginx \
--user=nginx \
--group=nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--pid-path=/var/run/nginx.pid \
--lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp \
--http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp \
--with-compat \
--with-file-aio \
--with-threads \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_mp4_module \
--with-http_random_index_module \
--with-http_realip_module \
--with-http_secure_link_module \
--with-http_slice_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_sub_module \
--with-http_v2_module \
--with-http_v3_module \
--with-mail \
--with-mail_ssl_module \
--with-stream \
--with-stream_realip_module \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_degradation_module \
--with-http_perl_module \
--with-google_perftools_module \
--with-debug \
--add-module=/usr/local/src/nginx-rtmp-module-1.2.2 \
--add-module=/usr/local/src/ngx_waf \
--add-module=/usr/local/src/ngx_http_geoip2_module-3.4

# sed -i 's/^\(CFLAGS.*\)/\1 -fstack-protector-strong -Wno-sign-compare/' objs/Makefile
# make
# 然后就是备份原有的nginx,使用新编译的这个进行替换,具体参考上文

下载地址:https://github.com/P3TERX/GeoLite.mmdb

下载国家IP库(包含ipv4和ipv6):

cd /etc/nginx
wget https://github.com/P3TERX/GeoLite.mmdb/releases/download/2025.04.01/GeoLite2-Country.mmdb
wget https://github.com/P3TERX/GeoLite.mmdb/releases/download/2025.04.01/GeoLite2-City.mmdb
# 配置nginx,除CN以外国家的ip不允许访问本站点 
http {

    log_format  country '$remote_addr "$geoip2_data_country_name" "$geoip2_data_city_name" "$geoip2_data_province_name" "$time_local" "$request" '
                         '$status $body_bytes_sent "$http_referer" '
                         '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log  /var/log/nginx/access_country.log  country;
	# 获取来源真实IP
    map $http_x_forwarded_for  $clientRealIp {          
        "" $remote_addr;
        ~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
    }   
	# 根据来源真实IP,调用geoip2,得到ip的来源国家
    geoip2 /etc/nginx/GeoLite2-Country.mmdb { 
        auto_reload 5m;
        $geoip2_metadata_country_build metadata build_epoch;
        $geoip2_data_country_code default=US source=$clientRealIp country iso_code;
        $geoip2_data_country_name country names en;
    }
	# 根据ip的来源国家,做匹配,来源自中国的允许访问,其他访问报错403
    map $geoip2_data_country_code $allowed_country {
        CN yes; 
        default no;
    }   

    geoip2 /etc/nginx/GeoLite2-City.mmdb {
        $geoip2_data_city_name city names en;
        $geoip2_data_province_code subdivisions 0 iso_code;
        $geoip2_data_province_name subdivisions 0 names en;
    }

}

server {

    location / {
        if ($allowed_country = no) {
            return 403;
        }
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

}

mmdb命令行工具

借助此工具可基于.mmdb查询对应ip的地理信息,可在整个调试过程中起到一个辅助查错的作用,后续调整nginx策略时也需要用到,可以明确需要哪些字段。命令使用,举例如下:

mmdblookup --file /etc/nginx/GeoLite2-Country.mmdb  --ip $ip

9、使用nginx源码动态编译ngx_waf

# 使用第一步在线安装nginx后的参数 ==》 1、先安装nginx
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

# 切换到该目录下
cd /usr/local/src

# 下载nginx源码
wget https://nginx.org/download/nginx-1.26.3.tar.gz

# 下载ngx_waf源码
git clone -b lts https://mirrors.chenby.cn/https://github.com/ADD-SP/ngx_waf.git

# 安装依赖uthash
cd /usr/local/src \
&&  git clone https://mirrors.chenby.cn/https://github.com/troydhanson/uthash.git \
&&  export LIB_UTHASH=/usr/local/src/uthash \
&&  cd /usr/local/src/nginx-1.26.3

# 安装依赖libinjection
cd /usr/local/src \
&& git clone https://mirrors.chenby.cn/https://github.com/libinjection/libinjection.git libinjection-src \
&& cd libinjection-src \
&&  ./autogen.sh \
&&  ./configure --prefix=/usr/local/src/nginx-1.26.3/libinjection \
&&  make -j$(nproc) && make install \
&&  export LIB_INJECTION=/usr/local/src/nginx-1.26.3/libinjection \
&&  cd /usr/local/src/nginx-1.26.3

# 安装依赖libsodium
cd /usr/local/src \
&& git clone https://mirrors.chenby.cn/https://github.com/jedisct1/libsodium.git --branch stable libsodium-src \
&&  cd libsodium-src \
&&  ./configure --prefix=/usr/local/src/nginx-1.26.3/libsodium --with-pic \
&&  make -j$(nproc) && make check -j $(nproc) && make install \
&&  export LIB_SODIUM=/usr/local/src/nginx-1.26.3/libsodium \
&&  cd /usr/local/src/nginx-1.26.3

# 安装依赖

# dnf -y install zlib zlib-devel pcre pcre-devel pcre2 pcre2-devel openssl openssl-devel

# 编译,添加上ngx_waf模块
./configure --with-http_ssl_module --with-compat --add-dynamic-module=/usr/local/src/ngx_waf

> 说明:即使系统上已安装上了openssl-devel,如果./configure时没有带上--with-http_ssl_module,还是会显示OpenSSL library is not used,因此在这里需要加上这个参数

# 替换部分参数
sed -i 's/^\(CFLAGS.*\)/\1 -fstack-protector-strong -Wno-sign-compare/' objs/Makefile

# 编译模块
make modules

# 关闭 nginx,然后将动态模块拷贝到nginx的模块目录下
# systemctl stop nginx
# cp objs/*.so /etc/nginx/modules/

# 在 nginx 的配置文件(nginx.conf)顶部添加一行
vim nginx.conf
load_module "/etc/nginx/modules/ngx_http_waf_module.so";

# 在nginx的conf文件中添加开启ngx_waf

server{
    waf on;
    waf_rule_path /usr/local/src/ngx_waf/assets/rules/;
    waf_mode FULL;
}

# 启动nginx
systemctl start nginx

# 再次查看nginx编译参数,跟一开始安装的没有变化
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-http_v3_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -march=x86-64-v2 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'
posted @ 2025-04-03 15:42  哈喽哈喽111111  阅读(288)  评论(0)    收藏  举报