mortennordbye/lawless-waf

GitHub: mortennordbye/lawless-waf

一款本地运行的 Azure WAF 误报调优工具,利用 DuckDB 直接查询已归档的 Blob 日志,在不产生 Log Analytics 费用的前提下完成日志分析与排除规则上下文生成。

Stars: 0 | Forks: 0

# lawless-waf ### 在你的笔记本电脑上调优 Azure WAF 误报,无需支付 Log Analytics 的价格。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/mortennordbye/lawless-waf/actions/workflows/ci.yml) [![Scorecard](https://api.securityscorecards.dev/projects/github.com/mortennordbye/lawless-waf/badge)](https://scorecard.dev/viewer/?uri=github.com/mortennordbye/lawless-waf) [![License](https://img.shields.io/github/license/mortennordbye/lawless-waf?style=flat-square)](LICENSE) [![Last Commit](https://img.shields.io/github/last-commit/mortennordbye/lawless-waf?style=flat-square)](https://github.com/mortennordbye/lawless-waf/commits/main)
一个小型 Web 应用 + 本地 API,用于调优 Azure WAF 的误报,而无需支付 Log Analytics 的价格。它会拉取你的 Front Door 或 Application Gateway 已经归档到存储账户中的原始 WAF 日志 blob,在你的笔记本电脑上使用 **DuckDB** 进行查询,将漏洞扫描器的噪声与真正的误报区分开来,并为你(或 AI 编程代理)提供在 `waf-exclusions.tf` 中编写排除规则所需的确切事实。 它**不会**生成 Terraform。它返回结构化的上下文 —— 规则 id/group、 `matchVariable` → Terraform `match_variable` + `selector` 映射、样本值、受影响的 URI、命中次数,以及扫描器/误报(FP)/攻击分类 —— 然后我自己编写 HCL(或者 让代理根据这些事实来编写)。 ## 我为什么开发它 WAF 日志嘈杂且数据量大,查看它们的通常方式是使用 Log Analytics / Sentinel 和 KQL。这行得通,但摄取是按 GB 计费的,而且对于我只有在追查误报时才会用到的东西来说,费用累积得非常快。这些日志*已经*被 归档到存储账户中以供保留,所以如果我还为了仅仅运行少量查询而摄取它们,我就等于付了两次钱。 这个工具直接读取那些已归档的 blob。DuckDB 在本地运行查询,因此分析 本身是免费的。对于每天约 5 GB 的 WAF 日志的大致标价估算(请查看 Azure 定价计算器以了解你的区域和承诺层级 —— 这些仅用于说明): | | 花费 | 备注 | | --- | --- | --- | | Log Analytics 摄取 | 摄取约 $2.5–2.8 / GB → 150 GB **约 $400+/月** | 加上免费期之后的保留费用 | | Blob 存储(归档) | 约 $0.02 / GB-月 → **每月几美元** | 你通常已经在支付这笔费用了 | | DuckDB 查询(本工具) | **$0** | 在你的笔记本电脑上针对你下载的 blob 运行 | 因此,我不是为了可能会去查询它而将所有内容都摄取到 Log Analytics 中,而是 下载我关心的那一天(或一小时)并在本地查询。权衡之处在于,这是 按需且面向单操作员的,而不是始终在线的 SIEM —— 这正是我想要的调优 工作模式。 另一个原因:WAF 调优中无聊的部分是*判断*(这是真正的攻击还是我 自己应用的流量?),而危险的部分是为攻击者在 WAF 中打洞。 这个工具前置了扫描器分割,这样单个嘈杂的扫描器 IP 就无法欺骗你(或 代理)为一个攻击者编写 30 个排除规则,并且它将整个闭环 —— 查找 → 分类 → 编写 → 验证 —— 保留在一个地方。 ## 截图 (所有截图均使用合成样本数据 —— 没有真实流量。) **概述 + 范围。** Action 组合、活动时间线、热门规则 / IP / 主机,以及一个范围 栏,用于按 WAF 策略进行过滤或一次性分析多天。 ![概述](https://raw.githubusercontent.com/mortennordbye/lawless-waf/main/docs/screenshots/overview.png) **排除上下文 —— 交付成果。** 针对每个匹配变量:Terraform 的 `match_variable` + `selector`、分类(误报 / 扫描器噪声 / 不可排除)、样本 值,以及受影响的 URI。 ![排除上下文](https://raw.githubusercontent.com/mortennordbye/lawless-waf/main/docs/screenshots/exclusion-context.png) **前 / 后 diff。** 比较两个时间窗口,以确认排除规则确实阻止了规则的 触发(`resolved`),或者发现刚刚开始触发的规则。 ![Diff](https://raw.githubusercontent.com/mortennordbye/lawless-waf/main/docs/screenshots/diff.png) **完整请求检查器。** 一个请求触发的每一条规则、匹配的变量,以及 从拦截评估消息中解析出的异常分数。 ![请求详情](https://static.pigsec.cn/wp-content/uploads/repos/cas/21/2100aadaf3ea1082f705394867df0ec6a00aff6d13cf05517465a388f3b45653.png) **现有排除规则覆盖率。** 粘贴你的 `waf-exclusions.tf`,以查看哪些触发中的规则 已经被覆盖、哪些仍未被覆盖,以及任何重复 / 冲突 / 过期的条目。 ![覆盖率](https://raw.githubusercontent.com/mortennordbye/lawless-waf/main/docs/screenshots/coverage.png) ## 快速开始(离线,无需 Azure) 你的笔记本电脑上唯一需要的是 **Docker** —— 依赖项、测试、lint、API, 和 UI 全都在容器中运行。 ``` make up # run it: API + web UI on http://localhost:5173 (hot reload) make seed # optional: generate a synthetic sample dataset for an offline trial make test # run the test suite make e2e # full offline pipeline test against the sample dataset make # list all commands ``` `.env` 会在首次运行时从 `.env.example` 创建,镜像会在首次运行时构建。打开 **http://localhost:5173** 并使用 Settings / Download / Analyze 选项卡。不需要登录 —— 应用绑定到 localhost,真正的门禁是 Azure。 ## 在真实的 Azure 环境中运行 应用本身不保存 Azure 密钥。它会复用你本地的 `az` 会话,所以在宿主机上: 1. 使用 `az login` 登录(如果你的存储账户需要,请先激活 PIM 并连接 VPN)。 2. 设置 `OFFLINE=false`。 3. 在 **Settings** 选项卡上,选择 订阅 → 存储账户 → 容器。一旦你登录, 这些就是从你的会话中填充的下拉列表。默认容器是 Front Door WAF 日志名称;对于 Application Gateway,请使用 `insights-logs-applicationgatewayfirewalllog`。 4. 在 **Download** 上,选择日期范围(或“This hour”),检查大小/时间估算,然后 拉取 blob。缓存的日期会被重用。 `docker compose` 会将 `~/.azure` 以读写方式挂载到容器中,以便 CLI 可以刷新其 自己的 token。此仓库中不存在任何 Azure 凭据。 ## 工作流 1. **下载** 你关心的时间窗口(一天,或者对于最近发生的情况,选择单个小时)。 2. **分析**: - 首先阅读 **扫描器分割** —— 永远不要为扫描器 IP 编写排除规则。 - 查看 **按原因分类的拦截**(或者触发中的规则,如果策略处于 Detection 模式并且 实际上没有任何东西被拦截)。 - **调查** 某条规则以获取其排除上下文,并深入查看真实的请求 (URI / IP / 主机 / 匹配值)以确认它是误报。 - 使用 **搜索** 跨所有规则追踪某个特定的 IP 或 URL,并使用 **请求 检查器** 查看单个请求触发的所有内容。 3. 针对你现有的 `waf-exclusions.tf` 检查 **覆盖率**,这样你就不会重复劳动。 4. 根据返回的 `match_variable` + `selector` + operator 编写排除规则。 5. 在你应用 Terraform 之后,将一个新的时间窗口与旧窗口进行 **diff**,以确认 规则停止了触发。 对于近乎实时的工作,Analyze 选项卡有一个 **Go live** 开关,它会按计时器重新下载 当前小时的数据,并原地刷新分析结果。WAF 诊断日志会有几分钟的延迟,因此“实时”追踪也会带有这种固有的延迟。 要将整个闭环交给 AI 代理,请使用下面的 MCP server。 ## 从 AI 代理使用它 (MCP) 代理通过原生 **MCP 工具** 驱动整个闭环 —— `refresh_live`、 `scanner_report`、`blocks_by_cause`、`exclusion_context`、`search`、`coverage`、`firing_diff`, 以及其他工具(`src/lawless_waf/mcp_server.py`)。服务器复用与 REST API 相同的 `service` 层,并在应用容器内运行(它拥有数据集缓存和你挂载的 `az` 会话),通过 stdio 进行 MCP 通信。应用必须处于运行状态(`make up`)。 **Claude Code:** ``` make mcp # claude mcp add lawless-waf -- docker compose -f /compose.yaml exec -T api python -m lawless_waf.mcp_server ``` **任何其他客户端**(Cursor、Claude Desktop、Windsurf……)—— 打印配置并将其粘贴到 该客户端的 `mcpServers` 配置中: ``` make mcp-config ``` 它会输出: ``` { "mcpServers": { "lawless-waf": { "command": "docker", "args": ["compose", "-f", "/abs/path/to/compose.yaml", "exec", "-T", "api", "python", "-m", "lawless_waf.mcp_server"] } } } ``` 然后让代理去调优一个时间窗口 —— 它会直接调用工具。服务器在此边界处验证每个 输入(它不再处于 FastAPI 的查询验证之后),并且适用相同的范围 选项:大多数工具接受 `dataset_id` 加上可选的 `datasets=[…]` 和 `policy=…`。 ## API(也是 UI 调用的接口) 所有内容都在 `/api` 下(无需身份验证 —— 仅限 localhost 的工具;Azure 是真正的门禁)。每个 分析 endpoint 都接受一个可选的范围:`?policy=` 限制为单个 WAF 策略,并且 重复 `&dataset=` 可以将多天合在一起分析。 1. `GET/PUT /api/config` —— Azure 目标;`GET /api/azure/status` 以及 Settings 下拉列表背后的 订阅 / 存储账户 / 容器 查找。 2. `POST /api/datasets {date, hour?, force?}` —— 下载一天/一小时(已缓存;仅在 缺失时调用 Azure)。`POST /api/datasets/estimate` 获取大小 + 预计到达时间,`POST /api/datasets/speedtest` 测量实际吞吐量,`DELETE /api/datasets[/{id}]` 清除缓存。 3. `GET /api/datasets/{id}/summary` —— 概述(action 组合、基数、策略模式、热门 主机/IP、时间线)。`GET …/policies`、`GET …/search?q=`(自由文本深挖)、 `GET …/requests/{trackingRef}`(一个完整的请求 + 异常分数)。 4. `GET /api/datasets/{id}/scanner-report` —— **首先阅读**:扫描器与误报候选者。 5. `GET /api/datasets/{id}/blocks-by-cause?exclude_scanners=true` —— 拦截真实流量的原因。 6. `GET /api/datasets/{id}/rules/{ruleId}/exclusion-context` —— 用于编写 排除规则的结构化事实;`…/events` 获取规则背后的行级请求。 7. `GET /api/datasets/{id}/diff?against=` 和 `…/rules/{ruleId}/diff?against=` —— 前/后对比,用于验证修复。 8. `POST /api/exclusions/count {tf_content}` —— 100 槽位限制 + 合并提示; `POST /api/datasets/{id}/exclusions/coverage {tf_content}` —— 将现有的 `waf-exclusions.tf` 与当前触发中的规则进行交叉比对。 ## 注意事项 - 该应用旨在单个操作员的笔记本电脑上运行。设计上没有应用级别的身份验证; 门禁是你的 Azure 访问权限。不要将其暴露在网络上。 - 匹配值在所有返回的地方都被截断了 —— WAF 日志可能带有 token 和 PII。 - 覆盖率交叉比对了每次运行中触发次数最多的规则(在截断时会进行标记),以便在 大型数据集上保持快速。 ## 持续集成 | 工作流 | 触发器 | 目的 | | -------- | ------- | ------- | | CI | push, PR | Python lint + 测试,前端 lint + 构建,Docker 镜像构建并推送到 GHCR | | Dependency Review | PR | 阻止添加已知存在漏洞依赖项的 PR | | Scorecard | push, 每周 | OpenSSF 供应链评分 → Security 标签页 | | Container Scan | push, 每周 | Trivy 镜像扫描 → Security 标签页 | ## 架构 - `src/lawless_waf/service.py` —— 框架无关的核心,由 FastAPI 路由器和 MCP server(`mcp_server.py`)共享,因此两种传输方式运行相同的逻辑。 - `duck/` —— DuckDB 引擎(多文件 + 策略范围视图)和 runbook 的查询。 - `analysis/` —— `scanner.py`(IP 分割)、`classify.py`(攻击与应用数据对比)、 `mapping.py`(日志 → Terraform 匹配变量)、`exclusions.py`(槽位限制 + tf 解析)。 - `azure/` —— `downloader.py` / `estimate.py`(围绕文档化的 `az` 命令的 argv 包装器)、`discovery.py`(会话 + 资源查找)。 - `api/` —— 精简的 FastAPI 路由器(速率限制、边界验证);全部位于 `/api` 下。 - `frontend/` —— React + Vite Tailwind SPA;提供 UI 并将 `/api` 代理到 API。
### ⭐ 如果你觉得有用,请给这个仓库点个 Star ⭐ Star History Chart 由 [Morten Victor Nordbye](https://nordbye.it/) 制作
标签:AppImage, Azure WAF, DuckDB, ECS, Terraform, Web应用防火墙, 网络测绘, 误报调优, 请求拦截, 运维工具, 逆向工具