Files
blackbird/waf
2026-07-25 11:22:51 +08:00
..
2026-07-25 11:22:51 +08:00
2026-07-25 11:22:51 +08:00
2026-07-25 01:38:42 +08:00
2026-07-25 01:27:08 +08:00
2026-07-25 01:54:43 +08:00
2026-07-25 01:27:08 +08:00
2026-07-25 01:27:08 +08:00
2026-07-25 01:54:43 +08:00
2026-07-25 01:54:43 +08:00
2026-07-25 01:27:08 +08:00
2026-07-25 01:27:08 +08:00

waf — Web 应用防火墙网关

基于 Go + coraza 的 WAF 网关,部署在 gateway 之前,终止 TLS 并按 SecLang 规则库匹配请求,未命中则转发到 gateway。

客户端 ──HTTPS──▶ waf:443 (终止 TLS + coraza 规则匹配) ──明文 HTTP──▶ gateway:8443 (鉴权 + 反代)

为什么是 Go

  • coraza-waf/coraza 是 ModSecurity SecLang 的 Go 实现,100% 兼容 OWASP CRS v4
  • net/http/httputil.ReverseProxy 标准库反向代理,零第三方依赖
  • goroutine + net/http 吞吐量比 Python 高一个数量级
  • go build 出单二进制,core/init.txt 直接拉起

极客友好的规则系统

操作 命令
编辑规则 vim rules/local/custom.conf
校验语法 ./waf.bin -test rules/local/custom.conf
列出所有规则 ./waf.bin -list
测试输入 ./waf.bin -check "/?id=1' OR '1'='1"
热加载(不重启) kill -HUP $(pgrep waf)
临时禁用某文件 mv rules/local/x.conf x.conf.bak && kill -HUP $(pgrep waf)

规则文件组织

rules/
├── waf.conf              # 主入口,Include 各子文件
├── local/                # 本地自定义规则(ID 段 100000-199999)
│   ├── custom.conf       # 示例:SQL注入/XSS/路径遍历/命令注入/扫描器UA
│   └── exceptions.conf   # 误报白名单(SecRuleRemoveById / ctl:ruleRemoveById)
└── crs/                  # OWASP CRS(git clone,不入库,ID 段 900000-999999)

规则 ID 分段

  • 100000-199999 本地自定义
  • 900000-999999 OWASP CRS

规则语法(SecLang)

SecRule REQUEST_URI|ARGS "@rx (?i)union[\s/*]+select" \
    "id:100001,phase:1,deny,status:403,log,msg:'SQL injection: UNION SELECT'"

字段说明:

  • REQUEST_URI|ARGS 匹配目标(URI / 参数 / Header / Cookie 等)
  • @rx ... 正则匹配(也支持 @pm、@contains、@beginsWith 等)
  • phase:1 请求头阶段 / phase:2 请求体阶段 / phase:3 响应头 / phase:4 响应体
  • deny,status:403 命中后动作
  • msg 命中日志描述

部署形态

WAF 必须终止 TLS 才能看到明文 body,因此证书需从 gateway 迁到 WAF:

  1. 把 gateway 的 full_chain.crt / server.key 复制到 waf/certs/
  2. gateway 改为监听 127.0.0.1:8443,关闭 TLS(仅本机)
  3. waf 监听 :443,转发到 http://127.0.0.1:8443

配置

config.yaml:

listen: ":443"
tls_cert: "./certs/full_chain.crt"
tls_key:  "./certs/server.key"
upstream: "http://127.0.0.1:8443"
rules_dir: "./rules"

目录结构

waf/
├── main.go                  # 入口:服务模式 + CLI 子命令
├── config.yaml              # 配置
├── go.mod
├── internal/
│   ├── engine/
│   │   └── engine.go        # coraza 封装:加载/热加载/列举/单测
│   └── proxy/
│       └── proxy.go         # ReverseProxy + WAF 事务接入
├── rules/
│   ├── waf.conf
│   └── local/
│       ├── custom.conf
│       └── exceptions.conf
├── _set.sh                  # 编译
├── _run.sh                  # 运行
├── .gitignore
└── README.md

安装

# 1. 安装 Go 1.21+
brew install go    # macOS
# 或 sudo apt install golang-go  # Debian/Ubuntu

# 2. 编译
bash _set.sh

# 3. 准备证书
mkdir certs && cp /path/to/full_chain.crt /path/to/server.key certs/

# 4. 运行
bash _run.sh

接入 core 自启动

在 core/init.txt 增加:

bash /opt/micro_servers/waf/_run.sh

并把 gateway 改为内部 HTTP 端口(不再监听 443)。

命中日志

命中规则时输出 JSON 一行,便于 jq 分析:

{"ts":"2026-07-25T10:00:00Z","action":"block","rule_id":100001,"ip":"1.2.3.4","method":"GET","path":"/","query":"id=1'+OR+'1'='1","ua":"curl/8.0","cost_ms":0}

聚合统计:

journalctl -u Nidus -o cat | grep '"action":"block"' | \
  jq -r '.rule_id' | sort | uniq -c | sort -rn

启用 OWASP CRS(可选)

cd rules
git clone --depth 1 https://github.com/coreruleset/coreruleset.git crs

在 rules/waf.conf 取消注释:

Include crs/coraza.conf
Include crs/rules/*.conf

然后 kill -HUP $(pgrep waf)。

已知限制

  • 请求体全量读入内存检测,10MB 上限(SecRequestBodyLimit)。大文件上传场景需调大或加白名单。
  • 响应体检测未启用(需缓冲整个响应,延迟成本高)。
  • 限流/黑白名单未实现,可后续作为 coraza 规则或独立中间件加。

依赖

  • Go 1.21+
  • github.com/corazawaf/coraza/v3 v3.7.0
  • gopkg.in/yaml.v3

许可证

MIT License