隔离模型、权限模型与安全边界
这一章专门回答“隔离做到哪一层”“权限到底有多强”“哪些能力是声明性元数据,哪些能力已经强制生效”。
1. 隔离粒度:当前分成三层
当前代码里实际上存在三种不同层次的隔离。
1.1 user_id(用户所有权边界)是对象级隔离
后端要求所有用户态 API 都带 x-user-id。FileBackedStore(文件型状态存储)在读取 SessionRecord、ArtifactRecord、MountGrantRecord、RunRecord 时都会调用 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: falsecapabilities.drop = ["ALL"]runAsNonRoot: truerunAsUser: 1000runAsGroup: 1000fsGroup: 1000seccompProfile.type = RuntimeDefaultautomountServiceAccountToken: falserestartPolicy: 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 还是 mountpermissionProfile:当前只是一段包策略元数据,没有被后端强制映射成 Linux/K8s/工具权限
所以目前真正“强制落地”的权限主要来自:
- Pod 安全上下文
- 工作区 copy / mount 选择
- 挂载白名单
readOnly/readWrite挂载模式
permissionProfile 当前承担的是描述和标识作用。
3. 一个 session、一个用户、一个 run,分别隔离什么
| 隔离单元 | 当前是否独立容器 | 当前是否独立工作区 | 当前是否独立历史 | 当前用途 |
|---|---|---|---|---|
| 用户 | 否 | 否 | 否 | 作为所有权边界 |
| Session | 否 | 否 | 是 | 作为多轮上下文边界 |
| Run | 是 | 是 | 只记录本轮 | 作为实际执行边界 |
因此如果要回答“目前是一个会话一个隔离环境,还是一个用户一个隔离环境”,答案是:
- 逻辑隔离按 session
- 物理隔离按 run
4. copy 和 mount 两种工作区模式的安全差异
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(挂载路径校验)会做三件事:
- 把用户给的路径 canonicalize
- 确认它是一个已存在目录
- 确认它落在
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 容器内可以访问哪些“外部工具”
当前真正可执行的工具只有三类:
- 基础镜像里自带的工具,例如
bash、node inlineNpm(运行时内联 npm 安装)装出来的 CLI- 容器内通过 HTTP 访问的模型 API
7.2 build_in_tools.yaml 当前是提示词工具清单
build_in_tools.yaml(内置工具清单)虽然声明了 builtin.read、builtin.write、builtin.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 默认值合并器)会合并三层来源:
- backend 进程环境变量
RuntimeProfile里的默认baseUrl/model- 单次请求的
provider_env
合并后得到的环境变量会被写成每 run 独立的 K8s Secret。渲染返回给前端时,providerSecretYaml 里的值会被替换成 <redacted>。
这说明当前 provider 凭据隔离粒度是:
- 按 run 注入
- 按 Secret 隔离
- 按用户 API 读取权限隔离
9. 当前安全边界的真实结论
如果只基于代码,不做理想化表述,可以下结论:
- 当前已经有“每 run 独立 Pod + 非 root + drop capabilities + seccomp + 禁止自动 SA token”这一层容器安全基线。
- 当前默认更安全的输入方式是
copy,mount属于更强权限能力。 - 当前 mount 具备白名单和只读/读写控制,但仍然属于较强权限能力,应谨慎开放。
- 当前
permissionProfile、networkProfile主要还是描述性元数据,不应误认为强制安全策略。 - 当前没有把本地 macOS 工具或 backend 宿主机工具安全地暴露给容器的机制。