Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

隔离模型、权限模型与安全边界

这一章专门回答“隔离做到哪一层”“权限到底有多强”“哪些能力是声明性元数据,哪些能力已经强制生效”。

1. 隔离粒度:当前分成三层

当前代码里实际上存在三种不同层次的隔离。

1.1 user_id(用户所有权边界)是对象级隔离

后端要求所有用户态 API 都带 x-user-idFileBackedStore(文件型状态存储)在读取 SessionRecordArtifactRecordMountGrantRecordRunRecord 时都会调用 ensure_owner(所有者校验)。

这意味着:

  • 一个用户看不到另一个用户的 session / run / artifact / mount grant
  • 即使知道 UUID,跨用户读取也会返回 “not found”

这是一层应用层所有权隔离。当前代码没有扩展成完整身份认证体系。

1.2 SessionRecord(逻辑会话记录)是上下文隔离层

一个 SessionRecord 拥有自己的:

  • history
  • 默认 package
  • 关闭状态

它能保证多轮对话上下文按 session 归组。一个 session 可以连续创建多个 run,这些 run 共享逻辑历史,容器实例按 run 单独创建。

1.3 RunRecord(一次运行记录)是物理执行隔离

每次 create_run 都会创建:

  • 一个独立工作区目录
  • 一个独立 ConfigMap
  • 一个可选独立 Secret
  • 一个独立 K3s Job
  • 一个独立 Pod

所以当前最强的实际隔离边界是:

一次 run = 一个独立容器执行环境

2. 当前隔离强度怎么理解

如果用“隔离强度”来拆,当前可以分成四类。

2.1 进程与文件系统隔离:中等偏强

每个 run 都在独立 Pod 中运行,工作区也是独立目录,默认路径是:

  • 宿主机:backend/data/workspaces/<run-id>/
  • 容器内:/workspace

因此不同 run 的输出文件天然分目录隔离。

当前工作区挂载方式是 hostPath(宿主机目录直挂)。当前隔离强度属于较轻量的容器级隔离;如果后续要提升隔离,可以继续引入 PVC、CSI 或更强沙箱方案。

2.2 Linux 权限隔离:中等

Job 里的容器安全上下文当前明确设置了:

  • allowPrivilegeEscalation: false
  • capabilities.drop = ["ALL"]
  • runAsNonRoot: true
  • runAsUser: 1000
  • runAsGroup: 1000
  • fsGroup: 1000
  • seccompProfile.type = RuntimeDefault
  • automountServiceAccountToken: false
  • restartPolicy: Never

这说明当前 Pod 默认不会拿到:

  • root 身份
  • Linux capabilities
  • 自动挂载的 K8s API token

这部分是代码里真实生效的安全边界。

2.3 网络隔离:弱

AgentPackageManifest(Agent 包清单)里虽然有 networkProfile(网络档位),例如 restricted-egress,但当前 backend 不会依据这个字段创建 NetworkPolicy

  • networkProfile 现在只是包元数据和提示信息
  • 真正能否访问外网,取决于集群网络、节点出网能力、镜像内工具和 provider API 可达性

所以当前网络隔离强度偏弱,不能把 networkProfile 当成已落地的强制策略。

2.4 能力权限隔离:部分生效,部分只是声明

PackagePolicy(Agent 包策略)里的两个字段要分开看:

  • workspaceMode:真实生效,会影响输入文件是 copy 还是 mount
  • permissionProfile:当前只是一段包策略元数据,没有被后端强制映射成 Linux/K8s/工具权限

所以目前真正“强制落地”的权限主要来自:

  • Pod 安全上下文
  • 工作区 copy / mount 选择
  • 挂载白名单
  • readOnly / readWrite 挂载模式

permissionProfile 当前承担的是描述和标识作用。

3. 一个 session、一个用户、一个 run,分别隔离什么

隔离单元当前是否独立容器当前是否独立工作区当前是否独立历史当前用途
用户作为所有权边界
Session作为多轮上下文边界
Run只记录本轮作为实际执行边界

因此如果要回答“目前是一个会话一个隔离环境,还是一个用户一个隔离环境”,答案是:

  • 逻辑隔离按 session
  • 物理隔离按 run

4. copymount 两种工作区模式的安全差异

4.1 WorkspaceMode::Copy(复制模式)

复制模式下,后端会把 ArtifactRecord 对应的文件从 uploads/ 复制到当前 run 的 /workspace/in

优点:

  • 每次 run 的输入快照固定
  • 容器只看到本轮复制进去的文件
  • 不会直接暴露宿主机原始目录树

代价:

  • 大目录会产生重复拷贝
  • 用户本地后续改动不会自动映射到已提交的 run

4.2 WorkspaceMode::Mount(挂载模式)

挂载模式下,必须先创建 MountGrantRecord。后端会把 grant 的 host_path 作为额外 hostPath volume 挂到容器内的 mount_path

优点:

  • 不需要复制大目录
  • 容器看到的是宿主机目录的实时内容

风险:

  • 隔离性明显弱于 copy
  • 一旦授权 readWrite,容器就可以直接修改宿主机目录

因此 mount 模式本质上是“受控直通”能力。

5. 挂载授权当前怎么控

validate_mount_path(挂载路径校验)会做三件事:

  1. 把用户给的路径 canonicalize
  2. 确认它是一个已存在目录
  3. 确认它落在 AGENT_STORE_BACKEND_ALLOWED_MOUNT_ROOTS 白名单内

默认白名单是:

backend/data/workspaces

这说明当前 mount grant 的默认设计目标是:

  • 给 backend 自己管理的目录做受控回挂
  • 默认不开放整个宿主机文件系统

6. 容器内到底能访问什么文件

当前容器内常见文件可分成四类。

6.1 始终存在的工作区

  • /workspace/in
  • /workspace/out
  • /workspace/tmp

这三者都来自 run 对应的宿主机工作区目录。

6.2 始终存在的 package 目录

  • /opt/agent/package

这里挂的是 ConfigMap,里面是 package 的文本资产。

6.3 可选的 provider 凭据

provider 凭据不会作为文件卷挂进去,而是通过 envFrom.secretRef 注入环境变量。

6.4 可选的用户授权挂载

如果本次 run 绑定了 MountGrantRecord,还会额外挂一个 user-mount volume 到指定路径。

7. 容器如何访问外部工具、宿主机工具和本地工具

7.1 容器内可以访问哪些“外部工具”

当前真正可执行的工具只有三类:

  • 基础镜像里自带的工具,例如 bashnode
  • inlineNpm(运行时内联 npm 安装)装出来的 CLI
  • 容器内通过 HTTP 访问的模型 API

7.2 build_in_tools.yaml 当前是提示词工具清单

build_in_tools.yaml(内置工具清单)虽然声明了 builtin.readbuiltin.writebuiltin.ask_user 等工具,但当前实现只是把它们写进 run-context.json 供模型参考。

后端目前没有

  • 独立的 tool call RPC 通道
  • tool execution dispatcher
  • tool result callback 回路

当前实现里,这一层承担的是提示词上下文拼装职责。

7.3 容器不能直接访问 backend 主机任意工具

当前没有实现:

  • 宿主机命令代理
  • SSH proxy 执行
  • helper 本地工具转发
  • 本地 IDE / Git / shell 代理

除非某个宿主机目录被显式 mount 进来,否则容器看到的只是:

  • 镜像内容
  • 工作区内容
  • package 内容
  • 环境变量

7.4 容器也不能直接访问 macOS 本地工具

macos-helper 当前承担的是“文件同步桥”职责。它会把本地文件上传成 artifact,当前没有把 macOS 上的 shell、git、编辑器或 CLI 映射成远端容器可调用工具。

7.5 支持本地工具调用的可行方案

如果后续要让远端 agentcore 访问本地工具,当前最可行的方案有三类:

  • macos-helper(macOS 本地桥接服务)增加本地工具 RPC 层,由 helper 执行 Git、shell、编辑器等受控工具
  • helper 与 backend 之间建立长连接工具隧道,由 backend 转发工具调用请求
  • 继续保持当前文件同步模式,把本地工具执行放在 helper 侧完成,再把结果同步成文件或补丁

无论采用哪种方案,都需要补齐:

  • 工具白名单
  • 参数校验
  • 会话级认证
  • 审批模型
  • 超时与取消
  • 审计日志

7.6 当前未实现点

当前代码还没有:

  • 远端容器到本地 helper 的工具调用协议
  • helper 侧的受控命令执行器
  • 本地工具的权限和审批模型
  • 本地工具执行结果回写到 agentcore 的标准事件流

8. provider 凭据隔离如何做

ProviderDefaults(provider 默认值合并器)会合并三层来源:

  1. backend 进程环境变量
  2. RuntimeProfile 里的默认 baseUrl / model
  3. 单次请求的 provider_env

合并后得到的环境变量会被写成每 run 独立的 K8s Secret。渲染返回给前端时,providerSecretYaml 里的值会被替换成 <redacted>

这说明当前 provider 凭据隔离粒度是:

  • 按 run 注入
  • 按 Secret 隔离
  • 按用户 API 读取权限隔离

9. 当前安全边界的真实结论

如果只基于代码,不做理想化表述,可以下结论:

  1. 当前已经有“每 run 独立 Pod + 非 root + drop capabilities + seccomp + 禁止自动 SA token”这一层容器安全基线。
  2. 当前默认更安全的输入方式是 copymount 属于更强权限能力。
  3. 当前 mount 具备白名单和只读/读写控制,但仍然属于较强权限能力,应谨慎开放。
  4. 当前 permissionProfilenetworkProfile 主要还是描述性元数据,不应误认为强制安全策略。
  5. 当前没有把本地 macOS 工具或 backend 宿主机工具安全地暴露给容器的机制。