luberan/cloudflare-waf-log

GitHub: luberan/cloudflare-waf-log

一个基于 Cloudflare Worker 的可视化面板,支持在免费套餐下跨多个账户集中展示和分析 WAF 安全事件与 HTTP 流量数据。

Stars: 0 | Forks: 0

# Cloudflare WAF 仪表盘 一个 Cloudflare Worker,用于可视化跨**多个 Cloudflare 账户**(您自己的账户 + 客户账户)的 WAF / 安全事件 **以及 HTTP 流量分析**。 在**免费套餐**下完全可用(保留 24 小时的 WAF 事件)。 UI 包含两个选项卡,它们共享账户 / Zone / 时间范围选择器: - **🛡 WAF** — 防火墙 / 安全事件(原始仪表盘,见下文)。 - **📊 HTTP 流量** — 全 Zone 的 HTTP 分析:请求、数据传输、访问量、状态码、缓存命中率、热门国家 / 主机名 / 路径、内容类型、HTTP 版本,以及边缘/源站性能(TTFB 和源站响应时间)。数据范围限定为 `requestSource: eyeball`,因此它们与 Cloudflare 自己的 HTTP 流量仪表盘保持一致。根据时间范围的广度,底层会自动适配三种数据集,这与 Cloudflare 仪表盘切换分辨率的方式相同:较短的范围(约 24 小时)使用 **`httpRequestsAdaptiveGroups`**(细粒度,包含所有细分项;适用于免费计划,无需 Pro+);中等范围(约 3 天)切换为按小时的 **`httpRequests1hGroups`** 汇总;较长范围(最长约 30 天)使用按天的 **`httpRequests1dGroups`** 汇总(保留时间最长)。在汇总数据的时间范围下,无法使用按路径 / 主机名 / 性能的细分(相关面板会隐藏)——这些数据仅存在于细粒度的自适应数据集中。时间范围下拉菜单是根据每个 Zone 下所有三种数据集的真实限制(通过 GraphQL Settings 节点)构建的。 ## 功能 - **在 Cloudflare 账户之间切换** — 每个账户都有自己作为 Worker secret 存储的 API token - 在选定账户内**切换 Zone** - 筛选条件: - **action**(标签:`block`、`managed_challenge`、`jschallenge`、`challenge`、`allow`、`log`、`skip`) - **hostname**(例如 `www.example.com`) - **path**(例如 `/wp-login.php`) - **Rule ID**(防火墙规则 UUID,以逗号分隔) - **country**(ISO2 代码:`US,DE,RU,...`) - **ASN**(AS 编号,以逗号分隔:`13335,15169`) - **User-Agent**(精确匹配) - 时间范围(WAF 选项卡):过去 **1 / 6 / 24 小时**(免费套餐 — Cloudflare 不会保留超过此时间长度的 WAF 事件) - **KPI 卡片**:总计 / 已阻止 / 验证 / 允许+记录 - **图表**: - 堆叠时间序列(每小时的事件数,按 action 进行颜色区分) - 按 action 划分的环形图 - 前 15 个国家/地区(水平条形图) - 前 15 个主机名 - 按来源划分的环形图(`waf`、`firewallrules`、`botManagement`、…) - **表格**: - 前 30 个 Rule ID *(点击 = 按 Rule ID 筛选)* - 前 50 个路径 *(点击 = 按路径筛选)* - 前 50 个 ASN *(点击 = 按 ASN 筛选)* - 前 50 个 User Agent *(点击 = 按 UA 筛选)* - 最新约 500 个事件(时间、action、IP、ASN、国家/地区、主机、路径、方法、规则、ray ID) - **清除筛选** — 一个按钮即可清除所有标签和输入 - **CSV 导出** — 下载当前筛选条件下的原始事件(最多 10,000 行,UTF-8 + BOM,可直接在 Excel 中打开) ## 架构 | 部分 | 文件 | 用途 | |---|---|---| | Worker (TypeScript) | [src/index.ts](src/index.ts) | API endpoints — 代理至 Cloudflare GraphQL Analytics API | | 仪表盘标记 | [public/index.html](public/index.html) | 原生 HTML + CSS,无需构建步骤 | | 仪表盘逻辑 | [public/app.js](public/app.js) | 所有客户端 JS(fetch、图表、分面筛选) | | Chart.js (本地集成) | `public/vendor/chart.umd.min.js` | 自托管的 Chart.js — 不使用 CDN,因此 CSP 可以使用 `script-src 'self'` | | 配置 | [wrangler.jsonc](wrangler.jsonc) | Worker 入口 + assets binding + 禁用默认域名 | ### API endpoints | Endpoint | 描述 | |---|---| | `GET /api/accounts` | 已配置账户列表(仅包含 `id` + `label`,绝不包含 token) | | `GET /api/zones?account=` | 指定账户下的 Zone 列表 | | `GET /api/stats?account=&zone=&...filters` | WAF 聚合数据 + 最新 500 个事件(所有数据在一个请求中返回) | | `GET /api/http-stats?account=&zone=&since=&until=` | HTTP 流量 + 边缘性能聚合(服务器端分组;短时间范围使用 `httpRequestsAdaptiveGroups`,较长范围使用按小时/天的汇总) | | `GET /api/http-settings?account=&zone=` | 该 Zone 的 HTTP 数据集限制(保留时间 + 最大查询窗口),用于填充时间范围下拉菜单 | | `GET /api/log?account=&zone=&...filters` | 仅获取事件(可通过 `&limit=` 控制) | | `GET /api/export.csv?account=&zone=&...filters` | 原始事件的 CSV 导出(最多 10,000 行,`Content-Disposition: attachment`) | ### 关于免费套餐的说明 — 聚合在 Worker 中运行 Cloudflare 的 `firewallEventsAdaptiveGroups` 数据集(服务器端聚合)**需要 Pro+ 计划**。在免费计划下,只能使用 `firewallEventsAdaptive`(原始事件,24 小时,每次请求最多 10,000 行)。因此,Worker 会获取原始事件并在 JS 中计算所有统计数据(`byAction`、`byCountry`、`byHost`、`byPath`、`byRule`、`bySource`、`byAsn`、`byUserAgent`、`series`)。响应中包含 `totalSampled` 和 `truncated` — 如果某个 Zone 在 24 小时内超过 10,000 个事件,仪表盘会警告您统计数据是基于样本的。 ### 下钻分面 + 缓存 `country / host / path / rule / asn / ua` 等筛选条件采用**分面式多选**: - 点击表格 / 条形图中的某项会将其添加到筛选条件中,再次点击则会移除。 - 当前选中的项会被高亮显示,其他项保持可见(置灰)— 经典的分面搜索 UX。 - Worker 在 JS 中应用这些筛选,而不是在 GraphQL 查询中。对 CF 的请求仅包含 `action + source + zone + datetime`。 外部 GraphQL 获取的结果缓存在 Worker Cache API 中(TTL 为 5 分钟,以 `acc + zone + 5分钟时间桶 + action + source` 作为键)。因此,各项之间切换分面是**瞬间完成**的(缓存命中)— 您将体验到 <50 ms 的延迟,而不是 CF 带来的 500–2000 ms 延迟。仪表盘头部会显示 `⚡ cache HIT / ☁ cache MISS` 指示器及请求延迟。 ## Secret 配置 **所有敏感数据仅存在于 Worker secrets 中。**没有任何敏感信息被提交到代码库或 `wrangler.jsonc` 中。 为**每个 CF 账户**创建三个 secret: | Secret 名称 | 描述 | 示例 | |---|---|---| | `CFACC__LABEL` | UI 下拉菜单中显示的标签 | `My account` | | `CFACC__ACCOUNT` | Cloudflare Account ID(32 位十六进制字符) | `00000000000000000000000000000000` | | `CFACC__TOKEN` | Cloudflare API token(只读,见下文) | `cf_xxx...` | `` 可以是任何简短标识符(`PERSONAL`、`ACME`、`NOVA`、…)。它会出现在 URL 中,例如 `?account=`。Worker 在内部会将其统一转换为小写。 **添加新账户** = 创建三个新的 secret。现有的任何内容都不会改变,您也无需知道旧的 token。 **Token 轮换** = 只需覆盖 `CFACC__TOKEN`。 **移除账户** = 删除其对应的三个 secret。 ### Cloudflare API token — 如何创建 1. **My Profile → API Tokens → Create Token → Custom token** 2. **Token 名称**:例如 `waf-log-personal` 3. **权限策略** — 在左上角的 Resources 选择器中选择 **All Domains** *(不要选 "Entire Account" — 该范围不包含诸如 Zone:Read 和 Zone Analytics:Read 之类的 Zone 级别权限)* 4. 勾选以下类别: - **DNS & Zones → Zone : Read** - **Analytics & Logs → Analytics : Read** 5. *(可选)* 客户端 IP 过滤、TTL — 保持默认 6. **Continue → Create Token** → 复制它(仅显示一次) ## 设置 ### 本地开发 ``` npm install # 创建 .dev.vars(请勿提交——它已在 .gitignore 中)。 @' CFACC_PERSONAL_LABEL=My account CFACC_PERSONAL_ACCOUNT=00000000000000000000000000000000 CFACC_PERSONAL_TOKEN=cf_xxx '@ | Out-File -Encoding utf8 .dev.vars npm run dev ``` 打开 。 ### 生产环境 — Worker secrets 在仪表盘中:**Workers & Pages → 您的 Worker → Settings → Variables and Secrets → Add → Type: Secret** 为每个账户添加三个 secret(`CFACC__LABEL`、`CFACC__ACCOUNT`、`CFACC__TOKEN`)。 添加完所有内容后,点击 **Deploy**(只需一次 — 一次性应用所有更改)。 或者通过 CLI: ``` "My account" | npx wrangler secret put CFACC_PERSONAL_LABEL "abc123..." | npx wrangler secret put CFACC_PERSONAL_ACCOUNT "cf_xxx..." | npx wrangler secret put CFACC_PERSONAL_TOKEN ``` ### 通过 GitHub → Cloudflare Workers Builds 部署 1. 将代码库推送到 GitHub 2. **Workers & Pages → Create → Workers → Connect to Git**,选择该代码库 3. 构建设置: - **Build command**:*(留空 — 无需构建)* - **Deploy command**:`npx wrangler deploy` - **Non-production deploy command**:`npx wrangler versions upload` - **Builds for non-production branches**:保持禁用(无论如何,预览 URL 在配置中都已禁用) 4. 首次部署后添加 secret(见上文) 5. 之后每次推送到 `main` 分支都会触发自动部署 ### 自定义域名 `*.workers.dev` URL 已被禁用([wrangler.jsonc](wrangler.jsonc) — `workers_dev: false`),因此该 Worker 只能通过自定义域名访问。设置步骤: 1. **Worker → Settings → Domains & Routes → Add → Custom Domain** 2. 输入域名,例如 `waf.example.com`(必须是位于与 worker 相同 CF 账户上的 Zone) 3. CF 会自动创建 `CNAME` 并签发 TLS 证书 ### 访问保护 — Cloudflare Access (Zero Trust) 如果没有保护,该 Worker 将公开暴露,并会泄露所有客户账户的数据。**它必须位于 Access 之后:** 1. **Zero Trust 仪表盘 → Access → Applications → Add application → Self-hosted** 2. 应用程序域名:`waf.example.com`(上一步中的自定义域名) 3. 路径:留空(保护包括 `/api/*` 在内的整个主机名) 4. **Policy** → Add policy: - 操作:`Allow` - 包含:`Emails: you@example.com`(或者一个 IdP 群组,等等) 5. 保存并部署应用程序 如果没有有效的 Access 会话,Worker 将返回 302 重定向至 CF Access 登录页面。 #### 可选 — 在代码中验证 Access JWT(纵深防御) 默认情况下,Worker 信任位于其前方的 Access(网络层强制执行)。您还可以让 Worker **自行验证 Access JWT**,这样配置错误或被移除的 Access 应用程序就不会悄无声息地暴露数据。设置另外两个变量(普通变量,非 secret): | 变量 | 描述 | 示例 | |---|---|---| | `CF_ACCESS_TEAM_DOMAIN` | 您的 Zero Trust 团队域名 | `https://yourteam.cloudflareaccess.com` | | `CF_ACCESS_AUD` | Access 应用程序的 Application Audience (AUD) 标签 | `0a1b2c…` | 当**两者**都设置好后,每个 `/api/*` 请求都必须携带有效的 Access token(`Cf-Access-Jwt-Assertion` header 或 `CF_Authorization` cookie);Worker 会根据您团队的公钥以及受众、签发者和过期时间检查 RS256 签名,如果不则返回 `403`。如果其中任何一个未设置,则会跳过代码内验证(从而确保 `wrangler dev` 继续正常工作)。 ## 重要提示 — 禁用默认域名 [wrangler.jsonc](wrangler.jsonc) 中硬编码了以下内容: - `workers_dev: false` — 禁用 `..workers.dev` - `preview_urls: false` — 禁用由 `wrangler versions upload` 产生的预览 URL 如果不这样做,Wrangler 会在每次部署时重新启用默认域名,这将绕过 Access(预览 URL 没有附加 Access 策略)。如果您以后需要再次启用默认域名,请从配置中删除这些行。 ## 免费套餐限制 - **WAF 事件保留时间**:24 小时(Pro 72 小时,Biz 30 天,Ent 6 个月)— 这是 Cloudflare 的限制,并非此代码的限制 - **每次请求最多 10,000 个事件** — 如果 Zone 超过此限制,统计数据将基于样本得出(您会在响应中看到 `truncated: true`) - **Worker 配额**:每天 100,000 次请求,10 ms CPU 时间 - **GraphQL 速率限制**:每个 token 每 5 分钟约 1,200 次请求(每个账户都有自己独立的限制 → 随账户数量扩展) - **`firewallEventsAdaptiveGroups`(服务器端聚合)仅限 Pro+** — 这就是 Worker 在 JS 中聚合原始事件的原因 ## 安全说明 - Token 具有只读权限 — 即使 secret 泄露,也无法更改 CF 账户中的任何内容 - 前端永远不会接收到 token — `GET /api/accounts` 仅返回 `id` + `label` - Worker 必须位于 Cloudflare Access 之后 — 否则仪表盘将完全公开。作为纵深防御,它也可以选择在代码中[验证 Access JWT](#optional--verify-the-access-jwt-in-code-defense-in-depth) - 所有资源都带有严格的 `Content-Security-Policy`(`script-src 'self'`、`frame-ancestors 'none'` 等)以及 `X-Content-Type-Options`、`X-Frame-Options` 和 `Referrer-Policy`。所有脚本均为自托管(Chart.js 位于 `public/vendor/`,应用逻辑位于 `public/app.js`) — 不加载任何第三方源的资源 - CSV 导出会对触发公式的字符(`= + - @`)进行转义,以防止 CSV/Excel 公式注入,并且下载文件名已经过净化处理 - 发往 Cloudflare API/GraphQL 的上游调用具有硬超时限制,因此如果上游停滞将返回 `504`,而不是让 Worker 一直挂起 - `.dev.vars` 已被列入 [.gitignore](.gitignore) 中 ## 可能的扩展 - Cron Trigger → 将聚合数据存储在 D1/R2 中,以保留超过 24 小时的历史记录 - 当超过被阻止请求的阈值时触发告警 Webhook (Slack/Discord) ## 许可证 [MIT](LICENSE)
标签:WAF分析, Web安全, 互联网扫描, 可视化仪表盘, 程序员工具, 自动化攻击, 蓝队分析, 调试辅助, 运维监控, 高对比度