Commit 85b3cc2c by luoqi

fix(security): Bull Board 面板默认不挂载 —— 它是公网无鉴权的可写入口

bull-board.module.ts 原注释称「本路由由 NestJS 路由系统接管;需要走全局
JwtAuthGuard」—— 不成立。@bull-board/nestjs 用 ExpressAdapter 自己挂独立
Express handler,不经过 Nest 的路由与守卫。

2026-08-05 从公网实测旧生产(网关把 /admin/queues 转到了 3101):
  GET /admin/queues              → 200,面板 HTML
  GET /admin/queues/api/queues   → 200,队列数据
返回体带 "readOnlyMode":false / "allowRetries":true —— 未鉴权即可翻 job
payload(含 patientId / hostId / tenantId)并重试、清理任务。

加守卫要下沉到 Express 中间件层;而这个面板平时没人用(前端无任何入口链接,
只在 docs/monitoring 里作为排障手段提过),故取成本最低的解法:默认不挂载,
排障时 PAC_BULL_BOARD=1 临时开。

关掉的只是**面板**:队列本身、定时任务、job 消费全部照常,它只是查看器。

同步订正三处文档里「需登录 admin」「豁免前缀」的旧描述。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
parent 918a2844
Pipeline #3539 failed in 0 seconds
...@@ -155,7 +155,7 @@ cd apps/pac-web && pnpm start & ...@@ -155,7 +155,7 @@ cd apps/pac-web && pnpm start &
curl -s http://localhost:3101/health # → {"status":"ok","timestamp":"..."} curl -s http://localhost:3101/health # → {"status":"ok","timestamp":"..."}
``` ```
`GET /health` 免鉴权、且**不走** `pac/v1` 全局前缀,适合做负载均衡探针 / 容器 healthcheck / 外部拨测,返回 `{ "status": "ok", "timestamp": "<ISO>" }`。(同样豁免前缀的还有 Swagger `/api/docs`、Bull Board `/admin/queues`。) `GET /health` 免鉴权、且**不走** `pac/v1` 全局前缀,适合做负载均衡探针 / 容器 healthcheck / 外部拨测,返回 `{ "status": "ok", "timestamp": "<ISO>" }`。(同样豁免前缀的还有 Swagger `/api/docs`、Bull Board `/admin/queues` —— 后者**默认不挂载**,需 `PAC_BULL_BOARD=1`,见[监控](/docs/monitoring)。)
随后即可首次导入数据 —— 见 [数据摄入](/docs/ingestion)。 随后即可首次导入数据 —— 见 [数据摄入](/docs/ingestion)。
......
...@@ -342,7 +342,7 @@ GET /pac/v1/admin/host/self/push-logs?limit=50 ...@@ -342,7 +342,7 @@ GET /pac/v1/admin/host/self/push-logs?limit=50
| 交互式 API 文档(可试调) | `/api/docs` | | 交互式 API 文档(可试调) | `/api/docs` |
| 本契约文档 | `/docs/integration/friday-push-payload` | | 本契约文档 | `/docs/integration/friday-push-payload` |
| 宿主自助页(患者数 / 24h 交易 / 上次 push / 失败率) | `/admin/host` | | 宿主自助页(患者数 / 24h 交易 / 上次 push / 失败率) | `/admin/host` |
| 队列面板(看 push 触发的画像重算) | `/admin/queues` | | 队列面板(看 push 触发的画像重算) | `/admin/queues`(默认不挂载,需 `PAC_BULL_BOARD=1`)|
> **常见错误码**:`10106` 缺 header / 签名不匹配(或时间戳偏差 >5min)· `10001` source 拼错 > **常见错误码**:`10106` 缺 header / 签名不匹配(或时间戳偏差 >5min)· `10001` source 拼错
> (响应会列出可选源表名)· `10002` body 校验失败 · `10003` 并发超限(退避重发即可,幂等安全) > (响应会列出可选源表名)· `10002` body 校验失败 · `10003` 并发超限(退避重发即可,幂等安全)
......
...@@ -46,7 +46,15 @@ monitoring: ...@@ -46,7 +46,15 @@ monitoring:
## 三、队列健康(Bull Board) ## 三、队列健康(Bull Board)
`/admin/queues`(需登录 admin)实时查看三条队列 —— `persona-recompute` / `plan-recompute` / `plan-asset-generate` 的处理速度、失败重试与死信。 `/admin/queues` 实时查看三条队列 —— `persona-recompute` / `plan-recompute` / `plan-asset-generate` 的处理速度、失败重试与死信。
<Callout type="warn">
**默认不挂载**,要用得启动时设 `PAC_BULL_BOARD=1`。
此前文档写的「需登录 admin」**不成立** —— `@bull-board/nestjs` 自己挂独立 Express handler,不经过 Nest 守卫,面板与其数据接口都是免鉴权的,且 `readOnlyMode:false`(能重试 / 清理任务)。2026-08-05 实测生产上经网关可从公网直接打开,故改为默认关闭。
关掉的只是**面板**;队列本身、定时任务、job 消费全部照常 —— 它只是个查看器。排障时临时开:`PAC_BULL_BOARD=1`,用完记得关。
</Callout>
--- ---
......
...@@ -44,7 +44,9 @@ import { HealthController } from './health.controller'; ...@@ -44,7 +44,9 @@ import { HealthController } from './health.controller';
PrismaModule, PrismaModule,
RedisModule, RedisModule,
QueuesModule, QueuesModule,
QueuesBullBoardModule, // Bull Board 队列面板 —— **默认不挂载**,设 PAC_BULL_BOARD=1 才开(见 bull-board.module.ts)。
// 2026-08-05:实测它在生产上是公网无鉴权可读可写的入口,而平时没人用 → 默认关掉最省事。
...(process.env.PAC_BULL_BOARD === '1' ? [QueuesBullBoardModule] : []),
AlertingModule, AlertingModule,
AuthModule, AuthModule,
FactsModule, FactsModule,
......
...@@ -8,8 +8,20 @@ import { QueueName } from './queue-names'; ...@@ -8,8 +8,20 @@ import { QueueName } from './queue-names';
/** /**
* Bull Board UI — 挂到 /admin/queues 路径,可视化队列状态(active/waiting/failed)。 * Bull Board UI — 挂到 /admin/queues 路径,可视化队列状态(active/waiting/failed)。
* *
* 安全:本路由由 NestJS 路由系统接管;需要走全局 JwtAuthGuard。 * ⚠️ **默认不挂载**;要用得显式设 `PAC_BULL_BOARD=1`(见 app.module.ts 的条件 import)。
* 后续可加 @RequirePermission(Permission.ADMIN_PLATFORM_*) 收口管理员可见。 *
* 【为什么默认关】原注释写的是「本路由由 NestJS 路由系统接管;需要走全局 JwtAuthGuard」——
* **这条不成立**。`@bull-board/nestjs` 用 ExpressAdapter 自己挂了一个独立的 Express handler,
* 压根不经过 Nest 的路由与守卫。2026-08-05 从公网实测旧生产(网关把 /admin/queues 转到了 3101):
* GET /admin/queues → 200,面板 HTML
* GET /admin/queues/api/queues → 200,队列数据,且 `readOnlyMode:false` `allowRetries:true`
* 即:未鉴权就能翻 job payload(含 patientId / hostId / tenantId)并重试、清理任务。
*
* 加守卫要在 Express 中间件层做,而这个面板平时没人用(前端没有任何入口链接,只在
* docs/monitoring 里作为排障手段提过),所以选了成本最低的解法:**默认不挂载**。
* 本地 / 测试排查队列时 `PAC_BULL_BOARD=1 pnpm dev` 即可。
*
* ⚠️ 注意:关掉的只是这个**面板**。队列本身、定时任务、job 消费全部照常 —— 它只是个查看器。
*/ */
@Module({ @Module({
imports: [ imports: [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment