跳转到内容

部署验证:数据隔离为什么在数据库层

docker compose up -d --build 能拉起 runtime、Postgres、MinIO 与控制台。能起来不等于起对了。下面说明安全基线里几处设计为何如此,以及如何验证生效。

Terminal window
cp docker/.env.example .env
docker 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 可达。

仅在应用层拼接 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。

触发器事件驱动、异步处理:投递告警后审计不会瞬时出现。验收脚本轮询最终一致,而不是立即断言。

Terminal window
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
fi
done

判据是最新一条为 kind==="event" 且 status==="ok",确认本次告警触发成功;最多约 10 秒后失败退出。异步系统立即断言容易偶发失败;轮询 + 超时 + 明确判据更稳。

Terminal window
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)。

隔离头来源正交,勿混用:

Terminal window
-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 回答能做什么。

单人可用 admin 自批做联调;双人分流才算 POC 成功标准:

  1. operator 驱动一次变更;
  2. 等待审批;该 operator 不能在审批中心自批(403);
  3. 同租户 approver 通过或拒绝;
  4. 审计可区分发起人与审批人(approval.* 含 requester_id / decided_by)。

本人确认(未列入 HITL、权限为 ask):执行者在会话内放行,审批中心不应出现该条,审计为 permission.*。两类事件分开,事后才能分清本人放行与他人批准。见HITL与指标。


相关阅读: