nginx集成防火墙ngx_waf的docker版
由于公网的环境越来与严峻,所以想找一个nginx带防火墙的版本
调研过openresty,大部分集成redis了,感觉还是太重了,有一个不那么重的https://github.com/unixhot/waf 但是维护没有那么勤,最后维护是5年前,倒数第二次维护是十年前。
调研过雷池,也是基于tengine做的一个版本,配置就很繁琐。最后还是考虑基于nginx原生集成一个版本
这里使用的是:https://github.com/ADD-SP/ngx_waf
一、docker编译
编写Dockerfile
# ================= Stage 1: 构建 Nginx with WAF 模块 ==================
# 使用 Alpine 3.18 基础镜像作为构建阶段
FROM alpine:3.18 AS builderLABEL maintainer="eric@slant.co"# 设置版本号
ENV NGINX_VERSION=1.22.0 \WAF_VERSION=6.1.9 \LIBINJECTION_VERSION=3.10.0 \UTHASH_VERSION=2.3.0# 替换 Alpine 的官方源为阿里云源
#RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirrors.aliyun.com/alpine|g' /etc/apk/repositories && \
# apk update && \
# apk upgrade# 安装构建依赖
RUN apk add --no-cache --virtual .build-deps \gcc \libc-dev \make \openssl-dev \pcre-dev \zlib-dev \linux-headers \curl \gnupg \libxslt-dev \gd-dev \geoip-dev \perl-dev \flex \bison \unzip \libsodium-dev \tar \gzip \zip# 创建目录并下载 Nginx 和 WAF 模块
WORKDIR /usr/src# 下载并解压 Nginx
RUN curl -fSL http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz -o nginx.tar.gz && \tar -zxC /usr/src -f nginx.tar.gz && \rm -rf nginx.tar.gz# 下载并解压 ngx_waf 模块
RUN curl -fSL https://github.com/ADD-SP/ngx_waf/archive/refs/tags/v${WAF_VERSION}.tar.gz -o ngx_http_waf_module.tar.gz && \tar -zxC /usr/src -f ngx_http_waf_module.tar.gz && \mv /usr/src/ngx_waf-${WAF_VERSION} /usr/src/ngx_http_waf_module && \rm -rf ngx_http_waf_module.tar.gz# 安装 libinjection(用于 WAF)
RUN mkdir -p /usr/src/ngx_http_waf_module/inc && \curl -fSL https://github.com/libinjection/libinjection/archive/refs/tags/v${LIBINJECTION_VERSION}.zip -o libinjection.zip && \unzip libinjection.zip -d /usr/src/ngx_http_waf_module/inc/ && \mv /usr/src/ngx_http_waf_module/inc/libinjection-${LIBINJECTION_VERSION} /usr/src/ngx_http_waf_module/inc/libinjection && \rm -rf libinjection.zip# 安装 uthash(用于 WAF)
RUN mkdir -p /usr/local/src && \curl -fSL https://github.com/troydhanson/uthash/archive/refs/tags/v${UTHASH_VERSION}.tar.gz -o uthash.tar.gz && \tar -zxC /usr/local/src -f uthash.tar.gz && \mv /usr/local/src/uthash-${UTHASH_VERSION} /usr/local/src/uthash && \rm -rf uthash.tar.gz# 编译 WAF 模块
WORKDIR /usr/src/ngx_http_waf_module
ENV LIB_UTHASH=/usr/local/src/uthash
RUN make# 配置和编译 Nginx
WORKDIR /usr/src/nginx-${NGINX_VERSION}
ENV CONFIG="--prefix=/etc/nginx \--sbin-path=/usr/sbin/nginx \--modules-path=/usr/lib/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-http_ssl_module \--with-http_realip_module \--with-http_addition_module \--with-http_sub_module \--with-http_dav_module \--with-http_flv_module \--with-http_mp4_module \--with-http_gunzip_module \--with-http_gzip_static_module \--with-http_random_index_module \--with-http_secure_link_module \--with-http_stub_status_module \--with-http_auth_request_module \--with-http_xslt_module=dynamic \--with-http_image_filter_module=dynamic \--with-http_geoip_module=dynamic \--with-http_perl_module=dynamic \--with-threads \--with-stream \--with-stream_ssl_module \--with-stream_ssl_preread_module \--with-stream_realip_module \--with-stream_geoip_module=dynamic \--with-http_slice_module \--with-mail \--with-mail_ssl_module \--with-compat \--with-file-aio \--with-http_v2_module \--add-module=/usr/src/ngx_http_waf_module"# 创建用户组和用户
RUN addgroup -S nginx && \adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx# 配置、编译安装 Nginx
RUN ./configure $CONFIG --with-debug && \make -j$(getconf _NPROCESSORS_ONLN) && \make install && \strip /usr/sbin/nginx* && \strip /usr/lib/nginx/modules/*.so# 清理无用文件
RUN rm -rf /etc/nginx/html/* && \mkdir -p /etc/nginx/conf.d && \mkdir -p /usr/share/nginx/html && \cp html/index.html /usr/share/nginx/html/ && \cp html/50x.html /usr/share/nginx/html/# ================ Stage 2: 最终运行时镜像 ==================
FROM alpine:3.18# 替换 Alpine 的官方源为阿里云源(可选)
#RUN sed -i 's|https://dl-cdn.alpinelinux.org/alpine|https://mirrors.aliyun.com/alpine|g' /etc/apk/repositories# 更新索引并安装 tzdata
RUN apk update && \apk add --no-cache tzdata && \cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \echo "Asia/Shanghai" > /etc/timezone
# 复制构建阶段中的必要文件
COPY --from=builder /usr/sbin/ /usr/sbin/
COPY --from=builder /usr/lib/nginx/ /usr/lib/nginx/
COPY --from=builder /etc/nginx/ /etc/nginx/
COPY --from=builder /usr/share/nginx/html/ /usr/share/nginx/html
# 新增:复制 ngx_waf 的 assets 资源目录
COPY --from=builder /usr/src/ngx_http_waf_module/assets /usr/local/src/ngx_waf/assets# 创建 nginx 用户和目录
RUN addgroup -S nginx && \adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx && \mkdir -p /var/log/nginx /var/cache/nginx && \ln -sf /dev/stdout /var/log/nginx/access.log && \ln -sf /dev/stderr /var/log/nginx/error.log# 安装运行时依赖
RUN apk add --no-cache \libgcc \libstdc++ \pcre \openssl \zlib \libxslt \gd \geoip \perl \libsodium# 安装 Bash
RUN apk add --no-cache bash bash-doc bash-completion# 设置默认的shell为Bash
RUN echo "/bin/bash" >> /etc/shells && \sed -i 's|/bin/ash$|/bin/bash|' /etc/passwd# 可选:复制配置文件
COPY nginx.conf /etc/nginx/nginx.conf
COPY default.conf /etc/nginx/conf.d/default.conf# 开放端口
EXPOSE 80 443# 设置停止信号
STOPSIGNAL SIGTERM# 启动命令
CMD ["nginx", "-g", "daemon off;"]
使用到的文件
nginx.conf
user nginx;
worker_processes auto;error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;events {worker_connections 1024;
}http {include /etc/nginx/mime.types;default_type application/octet-stream;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 /var/log/nginx/access.log main;sendfile on;#tcp_nopush on;keepalive_timeout 65;#gzip on;include /etc/nginx/conf.d/*.conf;
}
default.conf
server {listen 80;listen [::]:80;server_name localhost;#access_log /var/log/nginx/host.access.log main;location / {root /usr/share/nginx/html;index index.html index.htm;}#error_page 404 /404.html;# redirect server error pages to the static page /50x.html#error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}# proxy the PHP scripts to Apache listening on 127.0.0.1:80##location ~ \.php$ {# proxy_pass http://127.0.0.1;#}# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000##location ~ \.php$ {# root html;# fastcgi_pass 127.0.0.1:9000;# fastcgi_index index.php;# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;# include fastcgi_params;#}# deny access to .htaccess files, if Apache's document root# concurs with nginx's one##location ~ /\.ht {# deny all;#}
}
编译
docker build -t nginx-waf:1.22.0 .
运行
docker run -itd --name nginx-waf -p80:80 nginx-waf:1.22.0
访问
http://ip:80
二、防火墙功能简单验证
简单的防火墙测试
进入容器,修改default.conf文件 在第一个server增加如下内容
docker exec -it nginx-waf sh
cd /etc/nginx/conf.d
vi default.conf
waf on;# 规则文件所在目录的绝对路径,必须以 / 结尾。waf_rule_path /usr/local/src/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;
进入容器的/usr/local/src/ngx_waf/assets/rules目录,修改user-agent文件内容,在最后添加
cd /usr/local/src/ngx_waf/assets/rules
vi user-agent
(?i)(?:Edg)
最后退出容器,然后重启容器,这样edge浏览器访问会报403,chrome浏览器访问正常
具体文档参考:配置 | ngx_waf
在封禁ipv4时有时候会有一些手误造成ip填写不规范,可以用下面的脚本检测IPv4的CIDR地址。
check_cidr.sh
#!/bin/bash# 检查参数
if [ $# -ne 2 ]; thenecho "Usage: $0 <input_file> <output_file>"exit 1
fiINPUT_FILE="$1"
OUTPUT_FILE="$2"# 清空输出文件
> "$OUTPUT_FILE"# 检查是否安装了 ipcalc
if ! command -v ipcalc &> /dev/null; thenecho "Error: 'ipcalc' is not installed."echo "Please install it first (e.g., sudo apt install ipcalc)"exit 1
fi# 逐行处理输入文件
while IFS= read -r line; docidr=$(echo "$line" | sed 's/[[:space:]]//g') # 去除所有空白字符if [[ -z "$cidr" ]]; thencontinuefi# 使用 ipcalc 验证 CIDR 合法性(无参数模式)if ipcalc "$cidr" 2>/dev/null | grep -q "^Network:"; thenecho "$cidr" >> "$OUTPUT_FILE"elseecho "Invalid CIDR removed: $cidr"fi
done < "$INPUT_FILE"echo "Valid CIDRs saved to: $OUTPUT_FILE"
执行方法
chmod +x check_cidr.sh
./check_cidr.sh ipv4 output.txt