CDN 与 WAF 精讲教程 / 第12章 自建方案
第12章 自建方案
并非所有场景都适合使用云厂商的 CDN/WAF。金融、政务、军工等行业有合规要求,或需要完全控制数据流,自建方案是必须的选择。本章讲解主流自建方案的架构和实践。
12.1 自建方案适用场景
12.1.1 何时选择自建
| 场景 | 原因 | 推荐方案 |
|---|---|---|
| 合规要求 | 数据不出境、数据主权 | 自建 + 私有化部署 |
| 内网防护 | 内部 Web 应用安全 | Nginx + ModSecurity |
| 混合云 | 多云/混合云统一策略 | Traefik / Envoy |
| 成本敏感 | 流量巨大,云 WAF 太贵 | 自建 + 规则订阅 |
| 定制需求 | 云 WAF 无法满足的特殊需求 | ModSecurity + 自定义规则 |
| 学习研究 | 深入理解 WAF 原理 | 任何开源方案 |
12.1.2 自建 vs 云方案对比
| 维度 | 自建 | 云方案 |
|---|---|---|
| 成本 | 硬件+人力(高) | 按需付费(可预测) |
| 运维 | 自己负责 | 厂商托管 |
| 扩展 | 需手动扩容 | 自动弹性 |
| 功能 | 完全可控 | 厂商定义 |
| 合规 | 完全自主 | 依赖厂商 |
| 更新 | 手动更新规则 | 自动更新 |
| DDoS | 需单独方案 | 内置防护 |
12.2 Nginx + ModSecurity
12.2.1 架构
Nginx + ModSecurity WAF 架构:
客户端
│
▼
┌─────────────────────────────────────────────────────┐
│ Nginx (反向代理) │
│ ├── SSL/TLS 终结 │
│ ├── 请求限速 │
│ ├── 负载均衡 │
│ │ │
│ ├── ModSecurity 模块 │
│ │ ├── 请求解析 │
│ │ ├── 规则引擎 │
│ │ │ ├── OWASP CRS (Core Rule Set) │
│ │ │ ├── 自定义规则 │
│ │ │ └── 虚拟补丁 │
│ │ └── 决策执行 (Block/Pass/Log) │
│ │ │
│ └── 代理到后端 │
└──────────────────────┬──────────────────────────────┘
│
▼
后端应用服务器
12.2.2 安装编译
#!/bin/bash
# Nginx + ModSecurity 编译安装脚本 (Ubuntu/Debian)
# 安装依赖
apt-get update && apt-get install -y \
build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev \
libssl-dev libxml2-dev libyajl-dev libgeoip-dev \
libcurl4-openssl-dev pkgconf libpcre2-dev
# 克隆 ModSecurity
git clone --depth 1 -b v3/master --recurse-submodules \
https://github.com/owasp-modsecurity/ModSecurity.git
cd ModSecurity
git submodule init && git submodule update
./build.sh
./configure --with-pcre2
make -j$(nproc) && make install
# 克隆 ModSecurity-nginx 连接器
cd /usr/local/src
git clone --depth 1 https://github.com/owasp-modsecurity/ModSecurity-nginx.git
# 下载并编译 Nginx
NGINX_VERSION=1.26.2
wget https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz
tar xzf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}
./configure \
--prefix=/etc/nginx \
--sbin-path=/usr/sbin/nginx \
--modules-path=/usr/lib64/nginx/modules \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_gzip_static_module \
--add-dynamic-module=/usr/local/src/ModSecurity-nginx
make -j$(nproc) && make install
echo "ModSecurity + Nginx 安装完成"
12.2.3 Nginx 配置
# /etc/nginx/nginx.conf
load_module modules/ngx_http_modsecurity_module.so;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
events {
worker_connections 10240;
}
http {
include 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" '
'rt=$request_time';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
# 限速配置
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=3r/s;
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/certs/example.com.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# === ModSecurity 启用 ===
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity/modsecurity.conf;
# 限速
limit_req zone=general burst=20 nodelay;
# 管理后台严格限速
location /admin/ {
limit_req zone=login burst=5 nodelay;
proxy_pass http://backend;
}
# API 接口
location /api/ {
proxy_pass http://backend;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# 默认
location / {
proxy_pass http://backend;
}
}
upstream backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081 backup;
keepalive 32;
}
}
12.2.4 ModSecurity 配置
# /etc/nginx/modsecurity/modsecurity.conf
# 基本配置
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess Off
SecRequestBodyLimit 13107200
SecRequestBodyNoFilesLimit 131072
# 请求体处理
SecRequestBodyInMemoryLimit 131072
SecRequestBodyLimitAction Reject
# 临时文件
SecTmpDir /tmp/modsecurity/tmp
SecDataDir /tmp/modsecurity/data
# 审计日志
SecAuditEngine RelevantOnly
SecAuditLogRelevantStatus "^(?:5|4(?!04))"
SecAuditLogParts ABIJDEFHZ
SecAuditLogType Serial
SecAuditLog /var/log/modsecurity/modsec_audit.log
# 动作
SecDefaultAction "phase:1,deny,log,status:403"
# 加载 OWASP CRS
Include /etc/nginx/modsecurity/crs/crs-setup.conf
Include /etc/nginx/modsecurity/crs/rules/*.conf
12.3 OWASP CRS 安装与配置
12.3.1 CRS 安装
# 安装 OWASP Core Rule Set
CRS_VERSION=4.7.0
cd /etc/nginx/modsecurity
git clone https://github.com/coreruleset/coreruleset.git crs
cd crs
git checkout v${CRS_VERSION}
# 复制配置模板
cp crs-setup.conf.example crs-setup.conf
12.3.2 CRS 配置
# /etc/nginx/modsecurity/crs/crs-setup.conf(关键配置)
# Paranoia Level (0-3, 推荐从 1 开始)
SecAction "id:900000,phase:1,pass,t:none,nolog,setvar:tx.blocking_paranoia_level=1"
# 异常评分阈值
SecAction "id:900110,phase:1,pass,t:none,nolog,setvar:tx.inbound_anomaly_score_threshold=5"
SecAction "id:900110,phase:1,pass,t:none,nolog,setvar:tx.outbound_anomaly_score_threshold=4"
# 允许的 HTTP 方法
SecAction "id:900200,phase:1,pass,t:none,nolog,setvar:'tx.allowed_methods=GET HEAD POST OPTIONS PUT PATCH DELETE'"
# 允许的内容类型
SecAction "id:900220,phase:1,pass,t:none,nolog,setvar:'tx.allowed_request_content_type=|application/x-www-form-urlencoded| |multipart/form-data| |multipart/related| |text/xml| |application/xml| |application/soap+xml| |application/json| |application/cloudevents+json| |application/cloudevents-batch+json|'"
# 文件扩展名限制
SecAction "id:900240,phase:1,pass,t:none,nolog,setvar:'tx.restricted_extensions=.asa/ .asax/ .ascx/ .backup/ .bak/ .bat/ .cdx/ .cer/ .cfg/ .cmd/ .com/ .config/ .conf/ .cs/ .csproj/ .csr/ .dat/ .db/ .dbf/ .dll/ .dos/ .htr/ .htw/ .ida/ .idc/ .idq/ .inc/ .ini/ .key/ .licx/ .lnk/ .log/ .mdb/ .old/ .pass/ .pdb/ .pol/ .printer/ .pwd/ .rdb/ .resources/ .resx/ .sql/ .swp/ .sys/ .vb/ .vbs/ .vbproj/ .vsdisco/ .webinfo/ .xsd/ .xsx/'"
12.4 HAProxy 作为 WAF 前端
12.4.1 HAProxy 优势
| 特性 | 说明 |
|---|---|
| 高性能 | 单实例百万级并发连接 |
| 稳定性 | 久经考验的 L4/L7 代理 |
| ACL 灵活 | 强大的访问控制列表 |
| 健康检查 | 丰富的后端健康检查 |
| 热重载 | 无中断配置重载 |
12.4.2 HAProxy WAF 配置
# /etc/haproxy/haproxy.cfg
global
maxconn 50000
log /dev/log local0
stats socket /var/run/haproxy.sock mode 600 level admin
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
log global
option httplog
# 前端(面向客户端)
frontend http-in
bind *:443 ssl crt /etc/ssl/certs/example.com.pem alpn h2,http/1.1
# ACL: 恶意 User-Agent
acl is_bad_ua hdr(User-Agent) -i -m sub sqlmap nikto nmap masscan
http-request deny deny_status 403 if is_bad_ua
# ACL: 敏感路径
acl is_sensitive_path path_end .env .git .svn .bak .sql
http-request deny deny_status 403 if is_sensitive_path
# ACL: 管理后台限制
acl is_admin path_beg /admin /wp-admin
acl is_office_ip src 203.0.113.0/24
http-request deny deny_status 403 if is_admin !is_office_ip
# 限速
stick-table type ip size 100k expire 30s store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
# 默认路由
default_backend servers
# 后端
backend servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
server web1 127.0.0.1:8080 check inter 5s fall 3 rise 2
server web2 127.0.0.1:8081 check inter 5s fall 3 rise 2
# Stats 页面
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOST
12.5 Traefik 作为现代 WAF
12.5.1 Traefik 架构
Traefik 架构:
┌──────────────────────────────────────────────────────────┐
│ Traefik │
│ ├── Providers (配置发现) │
│ │ ├── Docker │
│ │ ├── Kubernetes Ingress │
│ │ ├── File │
│ │ └── Consul/etcd │
│ ├── Entrypoints (入口) │
│ │ ├── :443 (HTTPS) │
│ │ └── :80 (HTTP → HTTPS 重定向) │
│ ├── Middlewares (中间件) │
│ │ ├── RateLimit │
│ │ ├── IPAllowList / IPWhiteList │
│ │ ├── Headers (安全头) │
│ │ ├── BasicAuth / ForwardAuth │
│ │ └── Compress │
│ └── Routers → Services (路由到后端) │
└──────────────────────────────────────────────────────────┘
12.5.2 Traefik 动态配置
# traefik-dynamic.yml
http:
middlewares:
# 速率限制
rate-limit:
rateLimit:
average: 100
burst: 50
period: 1s
# 安全头
security-headers:
headers:
frameDeny: true
contentTypeNosniff: true
browserXssFilter: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
customFrameOptionsValue: "SAMEORIGIN"
referrerPolicy: "strict-origin-when-cross-origin"
# IP 白名单
office-only:
ipAllowList:
sourceRange:
- "203.0.113.0/24"
- "198.51.100.0/24"
# 基础认证
admin-auth:
basicAuth:
users:
- "admin:$apr1$xxxxx$xxxxx"
# 路径前缀去除
strip-api:
stripPrefix:
prefixes:
- "/api/v1"
routers:
# API 路由
api-router:
rule: "Host(`api.example.com`) && PathPrefix(`/api/v1/`)"
service: api-service
middlewares:
- rate-limit
- security-headers
- strip-api
tls:
certResolver: letsencrypt
# 管理后台
admin-router:
rule: "Host(`admin.example.com`)"
service: admin-service
middlewares:
- office-only
- admin-auth
- security-headers
services:
api-service:
loadBalancer:
servers:
- url: "http://backend1:8080"
- url: "http://backend2:8080"
healthCheck:
path: /health
interval: 10s
admin-service:
loadBalancer:
servers:
- url: "http://admin-backend:8080"
12.6 规则管理
12.6.1 规则版本管理
# 使用 Git 管理 WAF 规则
/etc/nginx/modsecurity/
├── modsecurity.conf # 主配置
├── crs/ # OWASP CRS (Git submodule)
│ ├── crs-setup.conf
│ └── rules/
│ ├── REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf # 白名单
│ ├── REQUEST-901-INITIALIZATION.conf
│ ├── REQUEST-910-IP-REPUTATION.conf
│ ├── REQUEST-911-METHOD-ENFORCEMENT.conf
│ ├── REQUEST-912-DOS-PROTECTION.conf
│ ├── REQUEST-913-SCANNER-DETECTION.conf
│ ├── REQUEST-920-PROTOCOL-ENFORCEMENT.conf
│ ├── REQUEST-921-PROTOCOL-ATTACK.conf
│ ├── REQUEST-930-APPLICATION-ATTACK-LFI.conf
│ ├── REQUEST-931-APPLICATION-ATTACK-RFI.conf
│ ├── REQUEST-932-APPLICATION-ATTACK-RCE.conf
│ ├── REQUEST-933-APPLICATION-ATTACK-PHP.conf
│ ├── REQUEST-934-APPLICATION-ATTACK-GENERIC.conf
│ ├── REQUEST-941-APPLICATION-ATTACK-XSS.conf
│ ├── REQUEST-942-APPLICATION-ATTACK-SQLI.conf
│ └── ...
├── custom-rules/ # 自定义规则
│ ├── virtual-patches.conf # 虚拟补丁
│ ├── geo-block.conf # 地理封锁
│ └── rate-limit.conf # 限速规则
└── rules.d/ # 扩展规则目录
12.6.2 规则测试自动化
# .github/workflows/waf-rules-test.yml
name: WAF Rules Test
on:
push:
paths:
- 'modsecurity/crs/**'
- 'modsecurity/custom-rules/**'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup ModSecurity
run: |
sudo apt-get install -y libmodsecurity3
- name: Run CRS Tests
uses: coreruleset/go-ftw@v1
with:
config: test-config.yml
- name: Run Custom Rule Tests
run: |
for test_file in tests/*.yaml; do
go-ftw run -f "$test_file"
done
12.7 注意事项
⚠️ 性能调优:ModSecurity 正则引擎是 CPU 密集型。高流量场景需要调整 Paranoia Level(PL1 推荐),或使用专用 WAF 硬件。
⚠️ 规则更新:OWASP CRS 每季度发布更新,建议订阅 GitHub Release 通知。
⚠️ 日志存储:ModSecurity 审日志量大,需配置日志轮转和集中收集。
⚠️ 高可用:自建 WAF 需要部署至少 2 个节点,前端通过 Keepalived/VRRP 实现 VIP 漂移。
12.8 扩展阅读
- ModSecurity v3 Documentation — ModSecurity 官方文档
- OWASP CRS Documentation — CRS 规则文档
- HAProxy Configuration Manual — HAProxy 配置手册
- Traefik Documentation — Traefik 官方文档
- Coraza WAF — Go 语言 WAF 引擎
本章小结
| 主题 | 核心要点 |
|---|---|
| 适用场景 | 合规、内网、成本、定制需求 |
| Nginx + ModSecurity | 最成熟的自建方案,OWASP CRS 支持 |
| HAProxy | 高性能 L4/L7 代理,ACL 灵活 |
| Traefik | 云原生 WAF,动态配置发现 |
| 规则管理 | Git 版本控制 + 自动化测试 |
下一章:第13章 日志与分析 →