强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 扩展阅读


本章小结

主题核心要点
适用场景合规、内网、成本、定制需求
Nginx + ModSecurity最成熟的自建方案,OWASP CRS 支持
HAProxy高性能 L4/L7 代理,ACL 灵活
Traefik云原生 WAF,动态配置发现
规则管理Git 版本控制 + 自动化测试

下一章:第13章 日志与分析 →