部署验证:数据隔离为什么在数据库层
docker compose up -d --build 能拉起 runtime、Postgres、MinIO 与控制台。能起来不等于起对了。下面说明安全基线里几处设计为何如此,以及如何验证生效。
cp docker/.env.example .envdocker compose up -d --build# http://localhost:8080 → 控制台引导flowchart LR B[浏览器] --> W[web :80] W -->|/api| R[runtime] R --> PG[(postgres)] R --> S3[(minio)]web 是对宿主暴露的入口;postgres / minio 在内部网络,不对宿主机出网,仅 runtime 可达。
隔离为何在数据库层
Section titled “隔离为何在数据库层”仅在应用层拼接 WHERE tenant_id = ? 时,任意一处漏写即串租,且难以穷举。Agent Forge 在 Postgres 上启用行级安全,策略对读写同时约束。示意:
ALTER TABLE "session" ENABLE ROW LEVEL SECURITY;ALTER TABLE "session" FORCE ROW LEVEL SECURITY;CREATE POLICY "tenant_isolation" ON "session"USING (tenant_id = current_setting('app.tenant_id', true))WITH CHECK (tenant_id = current_setting('app.tenant_id', true));同类策略覆盖会话、消息、触发器等业务表(与当前迁移中的 RLS 表清单一致)。
FORCE,而不只是 ENABLE。 Postgres 默认可对表 owner 跳过 RLS。仅 ENABLE 时,连接角色若是 owner,策略被绕过。FORCE 让 owner 也受约束。
USING 与 WITH CHECK 都要写。 USING 约束可读行;WITH CHECK 约束可写入行。只写前者时,仍可能写入其它租户行。
current_setting(..., true)。 第二参数为 true 时,设置缺失返回 NULL 而非抛错,策略变成与 NULL 比较 → 不匹配任何行。忘了绑租户时默认拒绝,而不是放行全部。
按请求绑定。 runtime 在事务内 SET LOCAL app.tenant_id;随事务结束清除,不泄漏到连接池下一请求。
连接角色 NOBYPASSRLS。 超级用户或带 BYPASSRLS 的角色会绕过策略。初始化应用角色时显式 NOBYPASSRLS,与 FORCE 成对,数据库强制隔离才成立。
CREATE ROLE agentforge LOGIN PASSWORD '…' NOBYPASSRLS;展开见多租户与 RLS。
smoke:轮询而不是立即断言
Section titled “smoke:轮询而不是立即断言”触发器事件驱动、异步处理:投递告警后审计不会瞬时出现。验收脚本轮询最终一致,而不是立即断言。
BEFORE="$(af_curl GET "/trigger/audit?limit=5" | af_json 'j.length' || echo 0)"bash "$SCRIPT_DIR/raise-alert.sh"for _ in $(seq 1 20); do sleep 0.5 AFTER="$(af_curl GET "/trigger/audit?limit=5")" if echo "$AFTER" | af_json_ok 'j[0] && j[0].kind==="event" && j[0].status==="ok"'; then ok=1; break fidone判据是最新一条为 kind==="event" 且 status==="ok",确认本次告警触发成功;最多约 10 秒后失败退出。异步系统立即断言容易偶发失败;轮询 + 超时 + 明确判据更稳。
/audit/verify 返回什么
Section titled “/audit/verify 返回什么”af_curl GET "/audit/verify?limit=50" | af_json '{ok:j.ok, checked:j.checked, broken_at:j.broken_at}'形如 { ok: true, checked: 50, broken_at: null }:checked 为已重算条数;broken_at 指向断点(null 表示完整)。只回 200 的接口无法定位断链;能返回 broken_at 才说明在逐条重算。原理见哈希链审计。
脚本用 Bun 解析 JSON,不强制依赖 jq(runtime 已依赖 Bun)。
隔离头来源正交,勿混用:
-H "X-AgentForge-Tenant-Id: ${TENANT}"-H "X-AgentForge-Owner-Id: ${OWNER}"-H "X-AgentForge-Role: ${ROLE}"tenant 进入 RLS 的 GUC;owner / role 进入身份与 RBAC。tenant 回答属于哪片数据,role 回答能做什么。
HITL 双人验收
Section titled “HITL 双人验收”单人可用 admin 自批做联调;双人分流才算 POC 成功标准:
- operator 驱动一次变更;
- 等待审批;该 operator 不能在审批中心自批(
403); - 同租户 approver 通过或拒绝;
- 审计可区分发起人与审批人(
approval.*含requester_id/decided_by)。
本人确认(未列入 HITL、权限为 ask):执行者在会话内放行,审批中心不应出现该条,审计为 permission.*。两类事件分开,事后才能分清本人放行与他人批准。见HITL与指标。
相关阅读: