Agent Box 初探:从 OpenClaw 小龙虾安全问题谈 Agent Sandbox

Posted on 三 29 4月 2026 in Tech

Abstract Agent Box 初探:从 OpenClaw 小龙虾安全问题谈 Agent Sandbox
Authors Walter Fan
Category tech note
Status v1.0
Updated 2026-04-29
License CC-BY-NC-ND 4.0

先说一个不太浪漫的判断

AI Agent 最迷人的地方,是它终于不只是“嘴上很会”。它可以写代码、跑命令、装依赖、打开浏览器、读文件、改配置,甚至自己失败了还能重试。听起来像一个很积极的实习生。

问题也在这里:一个积极但偶尔幻觉的实习生,如果直接坐在生产机器上敲命令,那就不是生产力工具,而是运维惊悚片。老程序员都懂,rm -rf 的杀伤力不取决于是谁敲的,取决于它敲在哪里。

最近 OpenClaw 小龙虾相关的安全讨论,就像给这个问题打了一束很刺眼的追光。公开 issue 和官方博客里反复出现几类风险:外部消息被拼进 agent 上下文导致 prompt injection;本地 Gateway / WebSocket 过度信任 localhost;sandboxed agent 仍然能从配置里读到解析后的 API key;工具执行缺少 allowlist、审批和参数校验。很多问题后来已经修,但它们共同说明一件事:

Agent 的风险,不在于它会不会犯错,而在于它犯错时手里拿着什么权限、站在什么地方。

所以我认为 Agent 时代真正缺的不是又一个更会聊天的窗口,而是一个有边界、有状态、有生命周期的工作间。你可以把它叫 Agent Box,也可以按 Kubernetes SIGs 项目的名字叫 Agent Sandbox。这篇文章基于我在 2026-04-29 查阅的官方文档,聊聊它的 why、what、how,再给一个小例子。

一句话结论:

Agent Sandbox 的价值,不是让 Agent 更聪明,而是让 Agent 更敢动手,同时让平台更敢放手。

Why:为什么 Agent 需要一个 Box

过去我们写服务,大多数工作负载可以粗略分两类:

  • 无状态服务:开多个副本,用 Deployment 管,坏了重启,流量继续。
  • 有状态服务:用 StatefulSet、PVC、Service,一套标准组合拳。

AI Agent 不完全属于这两类。它更像一个临时工位:今天要分析一个 CSV,明天要修一个 repo,后天要开一个浏览器查页面。它需要隔离,也需要保留现场;它应该可以快速创建,也应该能在不用时休眠;它要有自己的文件系统、进程和网络边界,还最好有一个稳定身份,方便后续连接。

这就尴尬了。

如果每次都临时起一个普通 Pod,状态容易丢,冷启动也慢。如果给每个 Agent 手工拼一个 StatefulSet、Service、PVC,平台同学会在 YAML 里提前退休。如果直接让 Agent 在共享环境里跑代码,那就更刺激了,刺激到安全团队半夜能梦见审计日志。

Agent Sandbox 要解决的,就是这个中间地带:

短命一次性执行  <---->  长期有状态服务
                  ^
                  |
            Agent 的工作间

官方文档里提到几个动机,我翻译成工程师能立刻感受到的语言:

痛点 没有 Sandbox 时 有 Sandbox 后
执行不可信代码 容易碰到宿主机、共享网络或敏感数据 在隔离 Pod 里执行,可叠加 gVisor 或 Kata
多轮 Agent 工作 每轮重新安装依赖、重新拉代码 文件、依赖和中间结果可以留在同一个环境
启动延迟 冷启动等 Pod 调度和镜像拉取 SandboxWarmPool 可预热环境
生命周期 谁创建、谁清理、谁恢复都要自己写 Controller 负责创建、删除、休眠和恢复
平台治理 开发者各写各的 Kubernetes 资源 用 CRD 抽象成统一 API

这背后的深层变化是:Agent 不是一个 API 调用,而是一段会持续活动的运行时。 既然它会活动,就应该被放进合适的容器里。

OpenClaw 小龙虾给 Agent Sandbox 上的一课

我不想把 OpenClaw 小龙虾写成“反面教材”。恰恰相反,一个真实流行的 Agent 框架越多人用,就越容易被安全研究者、攻击者和热心用户从各种角度拧螺丝。问题暴露出来并被修掉,是开源生态成熟的必经阶段。

但这些问题确实给 Agent Sandbox 提了一个醒:沙箱不是一个可选插件,而是 Agent runtime 的基本盘。

看公开资料里几类典型问题,背后的模式很清楚。

1. 外部输入不是“用户说的话”,而是潜在指令注入

OpenClaw 的一个公开 issue 提到,来自飞书、Telegram、Slack 等外部 channel 的消息如果被直接拼进 agent context,就可能出现 prompt injection:攻击者在公开群里发一段看起来像系统指令的文本,诱导主 agent 调用更高权限的 sub-agent 或执行工具。

这类问题的根不是“模型太笨”,而是可信上下文和不可信输入混在一起

Agent Sandbox 给不了 prompt 注入的万能解药,但它能帮你把伤害关小:

  • 外部 channel 输入必须标记为 untrusted content,不能和 system prompt 混写。
  • 跨 agent 调用默认关闭,只有明确 allowlist 才能打开。
  • 被外部输入触发的任务,应该进入低权限 Sandbox,而不是直接调用高权限工具。
  • tool invocation 不能只靠模型“理解不要执行”,要靠平台策略硬拦。

一句话:prompt 边界和执行边界要同时存在。

2. 本地 Gateway 不是天然安全区

ClawJacked 这类问题提醒我们:localhost 不是护身符。浏览器页面、恶意广告、被污染的网页脚本,都可能尝试连接本地服务。如果本地 Gateway 对 localhost 连接过度信任,又缺少 origin 校验、速率限制和设备确认,Agent 就可能被“隔壁网页”接管。

这对 Agent Box 的启发很直接:

  • Sandbox 控制面 API 不要裸露在用户浏览器可随便碰到的位置。
  • WebSocket / local gateway 要校验 Origin、鉴权、限速和设备注册。
  • “来自本机”不等于“来自可信用户”。
  • Agent runtime 和控制面要分离,执行容器不能顺手拥有控制面管理权限。

很多传统桌面软件喜欢相信 localhost。Agent 不行。Agent 手里有 shell、文件系统、浏览器会话和各种 API,localhost 一旦失守,就像把门禁卡贴在门口写着“自取”。

3. 沙箱里不能放明文钥匙

另一个公开 issue 提到,sandboxed agent 可以通过配置命令读到解析后的 API secrets。表面上 agent 被关进 sandbox 了,实际上钥匙串也被放进房间了。

这说明一个常见误区:进程隔离不等于凭证隔离。

如果 Sandbox 里有长期 API key、云账号 token、GitHub PAT、Notion token,攻击者只要通过 prompt injection 让 agent 执行 cat configconfig get,沙箱就变成自动提款机。

更靠谱的模式是:

Sandbox -> Gateway Tool / Credential Broker -> Target API

Agent 只看到工具能力和返回结果,不直接看到长期凭证。短期 token 也应该绑定 task_idscopettl。任务结束,权限自然消失。

4. 工具权限不能靠“默认善良”

还有一类问题是工具治理:没有 allowlist / denylist,没有 per-agent permission,没有参数校验,没有 rate limit。任何进入 agent 的消息,都可能触发任意注册工具。

这和 Kubernetes 里把所有 Pod 都绑到 cluster-admin 差不多。平时很顺,出事很响。

Agent Sandbox 应该把工具权限变成平台对象,而不是 prompt 里的温馨提示:

  • 模型看到哪些工具,先由 policy 过滤。
  • 每次 tool call 进入 before_tool_call 这类硬门禁。
  • 高风险工具需要 approval gate。
  • destructive operation 默认 deny,除非任务类型明确需要。
  • 工具参数要校验,不允许模型随便拼 shell、URL、路径。

这也是为什么我前面强调 KSA/RBAC、NetworkPolicy、admission policy 和 credential broker。它们听起来不是“AI 功能”,但正是 Agent 能安全动手的前提。

OpenClaw 的这些讨论,本质上不是某个项目“写错几行代码”。它们暴露的是 Agent 工程的共同病灶:

Agent 把自然语言、外部输入、工具调用、凭证、文件系统和网络访问揉在一起。如果没有强边界,聪明会变成放大器,错误也会变成放大器。

What:Agent Sandbox 到底是什么

Agent Sandbox 是一个 Kubernetes-native 平台,核心是 Sandbox 这个 CRD。官方对它的定位很明确:管理隔离的、有状态的、单例的工作负载,特别适合 AI agent runtime、开发环境、Notebook、代码执行等场景。

别被 CRD 吓到。它的想法其实很朴素:

你别再手工拼 StatefulSet、Service、PVC 了。你告诉 Kubernetes“我想要一个 Sandbox”,剩下的交给 controller。

核心组件可以这样理解:

组件 作用 人话解释
Sandbox 声明一个单 Pod、有状态、有稳定身份的环境 一个具体工位
SandboxTemplate 复用运行时配置 工位装修模板
SandboxClaim 面向用户的申请入口 “给我来一个 Python 工位”
SandboxWarmPool 预热一批环境 提前把工位打开、电脑开机
Python / Go SDK 程序化创建、查询、操作 Sandbox Agent 调用 Box 的遥控器

架构上,它走的是 Kubernetes controller pattern:用户创建 SandboxSandboxClaim,controller 再去管理底层 Pod 和 runtime。这个设计很 Kubernetes,也很现实。它不试图重造一套调度系统,而是把 Agent 需要的“单例、有状态、可隔离、可恢复”抽象出来,放回 Kubernetes 生态。

一个最小的 Sandbox YAML 长这样:

apiVersion: agents.x-k8s.io/v1alpha1
kind: Sandbox
metadata:
  name: my-sandbox
spec:
  podTemplate:
    spec:
      containers:
        - name: my-container
          image: python:3.13-slim

这段配置的重点不是“又多了一个 YAML”,而是:平台开始拥有一个统一的对象来表达 Agent 的工作空间。

How:怎么把它用起来

我建议按三层来理解 Agent Sandbox。

第一层:平台层,先把 Box 管起来

平台侧先安装 controller 和 CRD。官方文档给的方式是基于 release manifest:

export VERSION="vX.Y.Z"

kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/manifest.yaml
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${VERSION}/extensions.yaml

这里我建议生产环境一定要 pin 具体版本,不要图省事写 main。基础设施最怕“昨天还好好的,今天上游给你加了点惊喜”。

平台层还要做几件事:

  • 配好 namespace、RBAC、ResourceQuota。
  • 定义 SandboxTemplate,比如 Python、Node、Browser、Jupyter、Coding Agent。
  • 根据风险选择 runtime:普通容器、gVisor 或 Kata Containers。
  • 配网络策略,默认不要让 Sandbox 随便访问内网。
  • 配 TTL、休眠、恢复和清理策略。

这一层的目标不是“让开发者自由发挥”,而是把自由关进一个合理的边界里。

第二层:模板层,把常见环境做成套餐

Agent 最讨厌“每次从零开始”。今天要 pandas,明天要 playwright,后天要 git、ripgrep、node、python、chromium。如果每个调用都现装依赖,用户等得心平气和,账单先不平静。

SandboxTemplate 的价值就是把常见运行时沉淀下来:

  • python-sandbox-template:适合代码解释器、数据分析、脚本执行。
  • browser-sandbox-template:适合 computer use、网页自动化。
  • coding-agent-template:带 repo、编译工具、测试工具和缓存。
  • jupyter-template:适合交互式分析和研究。

这和云主机镜像、CI runner image 的道理差不多。环境标准化之后,Agent 的行为才容易复现,问题也容易定位。

第三层:Agent 层,把 Sandbox 当成工具

真正有意思的是这一层。Agent 不应该知道底层有多少 Service、PVC、Pod,它只需要一个工具:

create sandbox -> write file -> run command -> read result -> keep or terminate

官方 Python SDK 的最小用法大概是这样:

from k8s_agent_sandbox import SandboxClient

client = SandboxClient()

sandbox = client.create_sandbox(
    template="python-sandbox-template",
    namespace="default",
)

try:
    sandbox.files.write(
        "run.py",
        "print(sum(range(1, 101)))\n",
    )
    result = sandbox.commands.run("python3 run.py")
    print(result.stdout)
finally:
    sandbox.terminate()

这段代码很小,但它背后的边界很重要:Agent 生成的代码没有在你的应用进程里跑,也没有在共享机器上裸奔,而是在一个可管理的 Sandbox 里执行。

如果任务是一次性的,跑完就 terminate()。如果任务是多轮 coding agent,可以保留 Sandbox,让它在同一个工作目录里反复生成、执行、修错。两种模式都合理,关键是生命周期要明确。

Example:一个“数据分析 Agent”的小场景

假设我们要做一个数据分析 Agent。用户上传一个 CSV,然后问:

“帮我看一下最近 30 天哪些接口错误率最高,画个图,再给排查建议。”

一个不负责任的实现可能是:把 CSV 传给应用服务,让 LLM 生成 Python,然后在应用服务容器里 exec。这就像在餐厅后厨修摩托车,理论上空间够,实践上厨师会报警。

更像样的流程应该是:

  1. 应用服务收到用户问题和 CSV。
  2. Agent 创建一个 python-sandbox-template
  3. 应用把 CSV 写入 Sandbox 文件系统。
  4. LLM 生成分析脚本。
  5. Sandbox 执行脚本,返回 stdout、stderr、exit code 和图表文件。
  6. Agent 根据结果整理解释。
  7. 如果脚本报错,把 stderr 反馈给 LLM,最多重试 2-3 次。
  8. 任务结束后按策略删除或休眠 Sandbox。

伪代码可以写成这样:

def analyze_csv(question: str, csv_bytes: bytes) -> str:
    client = SandboxClient()
    sandbox = client.create_sandbox(
        template="python-sandbox-template",
        namespace="agent-runtime",
    )

    try:
        sandbox.files.write("input.csv", csv_bytes.decode("utf-8"))

        code = generate_python_code(question, "input.csv")
        sandbox.files.write("analysis.py", code)

        for _ in range(3):
            result = sandbox.commands.run("python3 analysis.py")
            if result.exit_code == 0:
                chart = sandbox.files.read("chart.png")
                return summarize_result(result.stdout, chart)

            code = fix_code_with_error(code, result.stderr)
            sandbox.files.write("analysis.py", code)

        return "脚本连续失败,请人工检查数据格式和生成代码。"
    finally:
        sandbox.terminate()

这段代码不是完整生产实现,但表达了一个关键模式:

LLM 可以犯错,Sandbox 负责把错误关在房间里;Agent 可以重试,平台负责把重试变成可治理的动作。

这里还可以加几个工程细节:

  • CSV 大小限制,避免一上来把存储打爆。
  • 命令超时,避免死循环。
  • 网络默认关闭或只允许访问白名单。
  • 输出文件类型检查,避免把奇怪文件当图片返回。
  • 日志脱敏,避免用户数据进入模型日志或平台日志。
  • 每个 Sandbox 打上 user_idtask_idttl 标签,方便审计和清理。

这些东西不酷,但救命。

有深度的地方:它改变的是责任边界

很多人看 Agent Sandbox,第一反应是:“这不就是包了一层 Kubernetes 吗?”

这话对一半。它确实没有逃离 Kubernetes,但重点不是包装,而是重新划分责任。

以前做 Agent 执行环境,常见责任边界是这样的:

应用开发者:写 prompt、调模型、跑代码、管容器、管存储、管清理
平台团队:给一个 Kubernetes 集群
安全团队:上线前来皱眉

Agent Sandbox 想变成这样:

应用开发者:申请一个合适的 Sandbox,调用 SDK
平台团队:定义模板、runtime、配额、生命周期
安全团队:制定隔离级别、网络策略、审计规则

这才是它真正有启发的地方。

好平台不是把所有复杂度消灭掉,而是把复杂度放到该放的位置。开发者不该每次都手写 PVC;安全团队也不该每次靠人工 review 祈祷;平台团队更不该把“执行不可信代码”当成普通 Pod 来糊弄。

Agent 时代,where to run 会变成和 which model to use 一样重要的问题。

和普通方案比一比

方案 适合场景 问题
直接在应用容器里执行 本地 demo、极可信脚本 风险最高,边界最差
每次起普通 Pod 一次性短任务 状态弱、冷启动慢、生命周期要自己管
StatefulSet + Service + PVC 长期稳定服务 对临时 Agent 太重,模板化差
独立 VM 强隔离、重任务 启动和资源成本高,Kubernetes 集成弱
Agent Sandbox Agent runtime、代码执行、Notebook、开发环境 需要 Kubernetes 能力和平台治理

所以它不是银弹。它适合的是这类场景:

  • Agent 会生成并执行代码。
  • 任务需要多轮迭代,而不是一次命令结束。
  • 运行环境需要保留文件、依赖或缓存。
  • 安全边界比裸跑重要。
  • 团队已经有 Kubernetes 基础设施,或者愿意为 Agent runtime 建平台。

如果你只是做一个 FAQ bot,Agent Sandbox 可能太重。如果你让 Agent 修代码、跑测试、开浏览器、分析数据,它就开始有味道了。

认证和授权:给 Agent 发“临时工牌”,不是万能钥匙

Agent Box 真正难的地方,不是把代码关进容器。容器只是房间,权限才是门禁。

一个成熟的 Agent Box 访问内外部服务时,应该坚持一个原则:

Agent 不是用户本人,也不是平台管理员。它只是被用户委托完成某个任务的临时执行者。

这句话很重要。很多安全事故就是从这里滑坡的:用户登录了系统,于是系统把用户的长期 token 塞给 Agent;Agent 要查一个接口,于是平台给它开了半个内网;Agent 要跑测试,于是给它一个能改集群资源的 ServiceAccount。表面上事情跑通了,实际上是在给未来的自己埋彩蛋,还是那种拆开彩纸会爆的。

我更建议把访问模型拆成五层:

用户身份 -> 任务授权 -> Sandbox 身份 -> 网络边界 -> 目标服务授权

每一层只解决一件事,不要混在一起。

第一层:用户身份,回答“是谁委托了这个任务”

用户还是要按正常系统登录,比如 SSO、OIDC、企业内部 IAM。应用服务拿到用户身份后,不应该直接把用户 token 原样交给 Sandbox。原因很简单:Agent 会执行模型生成的代码,而模型生成的代码不值得拥有你的完整身份。

正确做法是由控制面记录:

  • user_id:谁发起的任务。
  • task_id:这次任务是什么。
  • template:允许使用哪种 Sandbox 模板。
  • scope:这次任务能访问哪些服务和动作。
  • ttl:权限什么时候过期。

也就是说,用户身份用于发起和审批,不直接用于执行和横向访问

第二层:任务授权,回答“这次任务能做什么”

Agent Box 不应该只问“这个用户是不是登录了”,还要问“这个任务被允许做什么”。

例如同一个用户发起两个任务:

任务 合理权限 不该给的权限
分析 CSV 读写当前 Sandbox 文件、调用 Python、访问模型网关 访问生产数据库、访问 Kubernetes API
修复 repo 测试 读写指定 repo、跑测试、访问包管理源 访问用户私有文档、扫描内网
生成日报 读取指定工单系统和指标 API 执行 shell、访问任意外部 URL

这里最好有一个 policy decision point,可以是简单配置,也可以是 OPA/Cedar/自研策略服务。它输出的不是一句“allow”,而是一组细粒度能力:

{
  "template": "python-sandbox-template",
  "ttlSeconds": 1800,
  "tools": ["files.write", "commands.run", "files.read"],
  "egress": ["https://model-gateway.example.com", "https://pypi.org"],
  "internalServices": ["metrics-reader"],
  "kubernetesApi": false
}

这份授权应该随任务生成,随任务结束回收。不要让它变成“永久有效的方便面调料包”,谁拿都能撒。

第三层:Sandbox 身份,回答“这个工作间在集群里是谁”

Kubernetes 里最自然的身份载体是 ServiceAccount。Agent Sandbox 官方也有 Sandbox with Kubernetes Service Account 示例:每个 sandboxed pod 可以绑定不同的 KSA,从而拥有不同的集群身份和 RBAC 权限。

一个最小配置大概是这样:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: sandbox-metrics-reader
  namespace: agent-runtime
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: sandbox-metrics-reader
  namespace: agent-runtime
rules:
  - apiGroups: [""]
    resources: ["configmaps"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: sandbox-metrics-reader
  namespace: agent-runtime
subjects:
  - kind: ServiceAccount
    name: sandbox-metrics-reader
    namespace: agent-runtime
roleRef:
  kind: Role
  name: sandbox-metrics-reader
  apiGroup: rbac.authorization.k8s.io

然后在 Sandbox 里指定:

apiVersion: agents.x-k8s.io/v1alpha1
kind: Sandbox
metadata:
  name: report-agent
  namespace: agent-runtime
spec:
  podTemplate:
    spec:
      serviceAccountName: sandbox-metrics-reader
      containers:
        - name: agent
          image: python:3.13-slim

几个经验规则:

  • 默认 automountServiceAccountToken: false,除非这个 Sandbox 确实需要访问 Kubernetes API。
  • 优先用 namespace 级 Role,谨慎使用 ClusterRole
  • 一个任务类型一个 KSA,不要所有 Sandbox 共用 default
  • Role 里只给必要 verbs,比如 get/list,不要顺手给 create/update/delete
  • 让 KSA 名称表达权限意图,例如 sandbox-jira-readersandbox-build-runner

换句话说,ServiceAccount 是 Agent 的“工牌”,RBAC 是工牌能刷哪些门。

第四层:网络边界,回答“它能连到哪里”

RBAC 管 Kubernetes API,不管普通 HTTP 出站。Agent 如果能随便连内网,那就算没有 K8s 权限,也可能访问到不该访问的服务。

Agent Sandbox 文档里有 Composing Sandbox with Network Policies 的例子,思路是把 SandboxNetworkPolicyIngressService 组合起来。我的建议是:默认 deny all,然后按任务放行最小出口。

例如只允许访问模型网关和一个内部 metrics 服务:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: report-agent-egress
  namespace: agent-runtime
spec:
  podSelector:
    matchLabels:
      agents.x-k8s.io/sandbox: report-agent
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              name: platform
          podSelector:
            matchLabels:
              app: model-gateway
      ports:
        - protocol: TCP
          port: 443
    - to:
        - namespaceSelector:
            matchLabels:
              name: observability
          podSelector:
            matchLabels:
              app: metrics-api
      ports:
        - protocol: TCP
          port: 8443

真实生产里,访问外部服务还可以再加一层 egress gateway 或 HTTP proxy:

Sandbox -> Egress Proxy -> External API

这样做的好处是:

  • Sandbox 不直接出公网。
  • 代理可以做域名 allowlist、速率限制、审计和脱敏。
  • 外部 API key 不必进入 Sandbox 文件系统。
  • 可以把所有 LLM API 调用收口到 model gateway,统一做预算、日志和安全过滤。

如果 Agent 需要访问内部业务服务,也不要让它直连一大片内网。更好的做法是让业务服务验证一个短期 token,token 里带上 user_idtask_idsandbox_idscopeexp。服务端只相信这些声明里的最小权限,不相信 Agent 自己说“我是来帮忙的”。

第五层:凭证发放,回答“秘密怎么进来、什么时候消失”

最危险的做法,是把长期密钥写进镜像、环境变量或工作目录。Agent 会读文件,会跑命令,会打印日志,长期密钥进去之后,就像把银行卡放在共享打印机旁边。

更稳的做法是引入一个 credential broker:

Agent Runtime -> Credential Broker -> Token Exchange -> Target Service

它负责几件事:

  • 根据 user_id/task_id/scope 发放短期凭证。
  • 凭证只对特定服务、特定动作有效。
  • 凭证 TTL 短,任务结束立即撤销或自然过期。
  • 不把 refresh token、长期 API key 暴露给 Sandbox。
  • 所有发放和使用都进入审计日志。

如果是云环境,可以用 Workload Identity、IRSA、SPIFFE/SPIRE、Vault dynamic secrets 或内部 STS 做这件事。具体用哪套不重要,关键是不要把“长期秘密”交给“会执行不确定代码的房间”。

准入策略:防止权限被悄悄加大

上面这些设计,如果只靠开发者自觉,早晚会被“临时需求”冲垮。安全策略必须前移到 admission。

Agent Sandbox 官方文档里有两个很有参考价值的方向:

  • ValidatingAdmissionPolicy:强制 Sandbox 满足基础安全要求,例如必须使用 runtimeClassName: gvisor、禁止 hostNetwork、关闭自动挂载 ServiceAccount token、非 root 运行、drop capabilities。
  • OPA Gatekeeper:阻止给正在被 Sandbox 使用的 ServiceAccount 追加 RoleBinding 或 ClusterRoleBinding,避免运行中的 Sandbox 被悄悄提权。

这两个策略解决的是“别让权限在你没注意的时候长大”。尤其是 OPA Gatekeeper 那个例子很实用:如果一个 ServiceAccount 已经被某个 Sandbox 使用,就不要允许别人再给它绑定新权限。否则攻击路径会变成:

先创建低权限 Sandbox -> 再给它的 KSA 绑定高权限 Role -> Sandbox 立刻变身

这类“后门式提权”很隐蔽,靠人工 review 很难稳住。

一个可落地的访问流程

把上面几层串起来,一个比较靠谱的流程是:

  1. 用户通过 SSO 登录应用。
  2. 用户提交任务,例如“分析这个 CSV,并生成图表”。
  3. 控制面根据用户、任务类型、数据敏感度计算 policy。
  4. 控制面创建 SandboxClaimSandbox,绑定专用 KSA、labels、TTL。
  5. Admission policy 检查 runtime、hostNetwork、ServiceAccount token、securityContext。
  6. Controller 创建 Pod,NetworkPolicy 按 label 限制入口和出口。
  7. Agent 需要访问服务时,向 credential broker 换短期 token。
  8. 目标服务校验 token scope,只允许本次任务需要的动作。
  9. 任务结束,Sandbox terminate 或 hibernate,短期 token 过期,审计日志落库。

这套流程看起来比“给它一个 token 让它跑”麻烦,但这是必要的麻烦。工程里很多好设计,本质上都是把未来的事故提前变成今天的约束。

安全这件事,不能只靠“隔离”两个字

Agent Sandbox 支持用 gVisor 或 Kata Containers 增强隔离。gVisor 通过用户态内核拦截系统调用,Kata 则提供更接近虚拟机级别的隔离。这些都很好,但别误会:隔离不是免死金牌。

我会把安全策略分成四圈:

第一圈:输入边界

用户上传的文件、URL、代码片段、prompt 都要限制大小、类型和格式。外部 channel 消息必须标记为 untrusted content,不允许伪装成 system instruction。不要让 Agent 一边读 5GB 文件,一边说“我感觉还行”。

第二圈:执行边界

给 Sandbox 设置 CPU、内存、超时、磁盘容量。能不用 root 就不用 root。能只读挂载就只读挂载。能禁止特权容器就禁止。工具执行要有 allowlist、审批门和参数校验,不能因为模型“想调用”就真的调用。

第三圈:网络边界

默认禁止访问内网敏感服务。需要访问外部网络时,用明确白名单。Gateway / WebSocket / local API 要做 origin 校验、鉴权和限速。Agent 最容易从“帮我查资料”滑到“顺手扫一下内网”,这条线不能靠模型自觉。

第四圈:审计和清理

每次创建 Sandbox,都应该知道是谁、为了什么任务、从哪个模板来、什么时候过期、执行了哪些命令。日志里要避免打印 token、API key 和用户敏感数据。TTL 和 scheduled deletion 不是锦上添花,是防止环境越堆越多的基本卫生。

安全设计的原则很简单:

把 Agent 当成一个会犯错的自动化账号,而不是一个永远善良的小助手。

落地清单:明天怎么开始

如果我要在团队里试点 Agent Sandbox,会按这个顺序来:

  1. 先选一个低风险场景,例如 CSV 分析、测试执行、文档构建,不要一上来碰生产内网。
  2. 定义一个最小模板,例如 python-sandbox-template,只放必要依赖。
  3. 配 namespace、RBAC、ResourceQuota、NetworkPolicy。
  4. 决定隔离级别:普通容器、gVisor,还是 Kata。
  5. 给每类任务定义专用 KSA 和最小 RBAC,不要共用 default
  6. 配 admission policy,强制非 root、禁止 hostNetwork、按需关闭 ServiceAccount token 自动挂载。
  7. 用 NetworkPolicy 或 egress proxy 做默认拒绝、按需放行。
  8. 用 credential broker 发短期凭证,不把长期密钥放进 Sandbox。
  9. 把外部 channel 输入统一包成 untrusted content,禁止伪造 system / assistant / tool 指令。
  10. 本地 Gateway / WebSocket 做 origin 校验、限速和显式设备确认,不要默认相信 localhost。
  11. 用 Python SDK 做一个最小闭环:创建、写文件、执行、读结果、清理。
  12. 给每个 Sandbox 加 task_idownerttl 标签。
  13. 把失败重试次数写死在代码里,不要让 Agent 无限自我感动。
  14. 记录 stdout、stderr、exit code,但注意脱敏。
  15. 压测 WarmPool,看真实启动延迟和资源占用。
  16. 最后再接入真正的 Agent loop。

这里的关键是顺序:先把盒子做好,再把 Agent 放进去。不要反过来,先让 Agent 到处跑,等出事了再想起来买门锁。

结论:Agent 的未来,不只是大脑,还有工位

过去一年,大家讨论 Agent,最爱聊模型、工具调用、规划、记忆。这些都重要。但越往工程落地走,越会发现另一个问题更基础:

Agent 到底在哪里工作?

如果它只聊天,浏览器窗口就够了。如果它要写代码、跑测试、装依赖、处理用户文件、打开 GUI、长期保持状态,那它需要一个认真设计过的工作间。这个工作间要隔离,要持久,要能快速分配,要能休眠和清理,还要能被程序化地创建和操作。

这就是 Agent Sandbox 给我的启发。

它不是一个花哨的“AI 产品外壳”,而是 Agent 工程化里很朴素的一块地基。地基通常不好看,也不适合做宣传海报,但楼能不能盖高,最后还得看它。

参考资料