← 返回博客
OpenClaw 沙箱:让 AI 在笼子里动手

OpenClaw 沙箱:让 AI 在笼子里动手

谁都知道 AI agent 能做事。问题是,它做的事,你兜不兜得住?

不开沙箱的时候,agent 跟你共享一台电脑。它执行的 shell 命令,直接跑在你的操作系统上。它读写的文件,就是你硬盘上的真实文件。它跑的进程,和你自己开的进程没有任何区别。模型说一句 rm -rf /important-stuff,你的文件就真没了。

沙箱做的事情只有一件:在你的电脑上造一个假的小电脑,让 agent 在里面跑。

默认后端是 Docker。开启沙箱后,OpenClaw 启动一个 Docker 容器,基于精简的 Debian 镜像 openclaw-sandbox:bookworm-slim。agent 要执行工具的时候,命令不发给你的主机,而是发到容器里面。

容器有自己独立的三样东西:

  • 文件系统:容器里的 / 是它自己的根目录,不是你主机的。你的 ~/Documents~/.ssh~/.openclaw,对它来说默认不存在
  • 进程空间:容器里的进程跟你主机上的进程互相看不到。agent 执行 ps aux,只能看到容器内自己的进程
  • 网络:默认没有网络(docker.network: "none")。agent 在沙箱里跑 curl 或者 apt-get 都会失败,它连不上任何外部地址

Gateway 进程始终在你的主机上运行,不受沙箱影响。沙箱隔离的,只是 agent 的工具执行。

这不是完美的安全边界。Docker 容器逃逸在极端情况下确实存在。但是它实实在在地把"模型犯蠢时的爆炸半径"从整台电脑缩小到了一个容器。够不够用?看你的场景。但总比裸奔强。

拿走了什么,留下了什么

理解沙箱,关键在一个字:界。什么被挡在外面了,什么还能通行。

拿走的

能力没有沙箱有沙箱
文件系统agent 看得到你主机上所有文件只看得到容器内的文件系统,你的真实文件默认不可见
网络访问agent 能访问任何地址默认无网络,需手动设 docker.network: "bridge"
进程可见性agent 看得到主机所有进程只看得到容器内自己的进程
系统命令agent 跑得了主机上任何命令只跑得了容器镜像里有的命令,默认连 Node、Python、curl 都没有
环境变量agent 读得到主机的 process.env沙箱不继承主机环境变量,API key 要通过 sandbox.docker.env 显式传入
持久化agent 写的文件留在主机上容器销毁,文件跟着没了,除非通过绑定挂载写回主机

没有拿走的

这些在沙箱内照常工作:

  • exec(执行命令):能跑,只是跑在容器里。镜像里有 bashlscatgrep,就能用
  • read(读文件):能读容器内的文件。配了 workspaceAccess: "ro""rw",也能读到挂载进来的工作区
  • write / edit / apply_patch(写文件):能写容器内的文件。workspaceAccess: "rw" 时,写操作直接反映到你的真实工作区
  • process(进程管理):容器内的进程照常管理
  • 会话工具(sessions_listsessions_send 等):Gateway 级别的东西,不受沙箱限制
  • 消息发送:agent 发 WhatsApp、Telegram 的能力不受影响,消息路由在 Gateway 侧处理
  • 模型推理:沙箱不管脑子,只管手脚

一句话讲清楚:沙箱限制的是 agent 在哪动手、能碰什么文件,不限制它说话和思考。

工作区访问的三档开关

沙箱和你真实文件之间的关系,由 workspaceAccess 精确控制。三档:

设置能看到什么能改什么
"none"(默认)只有沙箱内部的 ~/.openclaw/sandboxes只能改沙箱内部的文件
"ro"你的工作区以只读方式挂载到 /agent能读你的文件,但 write/edit/apply_patch 被禁用
"rw"你的工作区以读写方式挂载到 /workspace能读写你的真实文件,沙箱此时主要隔离进程和网络

"none" 模式下,OpenClaw 会把 agent 需要的 skills 文件镜像到沙箱内,让 read 工具照样读得到。入站媒体文件也会被复制到沙箱的 media/inbound/ 目录。

默认配置这么严,agent 不是废了?

默认开箱确实很严:没网络、看不到你的文件、镜像里连 curl 都没有。什么都不调的话,agent 能做的事非常有限。

这是故意的。OpenClaw 的思路是:默认给最小权限,你按需逐项打开。每一项限制都对应一个旋钮。

要操作你的项目文件?

workspaceAccess"none" 改成 "rw"。agent 就能读写你的工作区了。

{ sandbox: { workspaceAccess: "rw" } }

只需要读不需要写,用 "ro",更稳妥。

要装包、调 API、访问网络?

docker.network"none" 改成 "bridge"。容器就有网了。

{ sandbox: { docker: { network: "bridge" } } }

容器里缺命令?

默认 Debian 精简镜像只有最基本的 shell 工具。两条路:

  • 临时装:用 setupCommand 在容器创建时装一次,前提是先开网络
  • 换镜像:用 sandbox-common-setup.sh 构建一个带 curl、jq、nodejs、python3、git 的镜像,或者自己烤一个

需要访问主机上特定目录?

docker.binds 把指定目录挂进容器。

{ sandbox: { docker: { binds: ["/home/user/data:/data:ro"] } } }

有一个命令确实需要在主机上跑?

用 elevated 模式。沙箱里的 agent 可以临时把 exec 提升到主机执行:

/elevated on    # 当前会话的 exec 跑在主机上,需要审批
/elevated full  # 跳过审批

实际用起来的典型搭配

大部分人不会用默认最严的配置。根据用途选一个合理的组合就好:

用途workspaceAccessnetwork说明
家庭群机器人"none""none"纯聊天,不碰文件不碰网络
帮你读代码的助手"ro""none"能读项目文件,不能改,不能联网
跑测试的开发助手"rw""bridge"能读写项目、装依赖、跑测试
全功能个人助手"rw""bridge" + 自定义镜像接近不开沙箱的体验,但进程和系统层面仍然隔离

所以沙箱不是"开关一扔,agent 变废物"。它是一套可调的限制,默认最严,你按信任程度逐项放开。核心价值在于:即使你放开了文件读写和网络,agent 的进程空间、系统目录、其他用户的文件,仍然是看不到的。

三个核心配置维度

沙箱的行为由三个维度决定:模式、范围、后端。搞清楚这三个,配置就不会乱。

模式(mode):什么时候启用沙箱

通过 agents.defaults.sandbox.mode 控制:

模式行为
"off"不沙箱,所有工具直接在主机跑
"non-main"只对非主会话沙箱。群组和频道的会话天然是非主会话,会被沙箱
"all"所有会话都沙箱

有一个坑要留意:"non-main" 模式判断的是 session.mainKey(默认 "main"),不是 agent id。群组和频道的会话有自己的 key,会被当作 non-main 而隔离。你在群组里发现工具被意外限制了,原因就在这里。想让某个 agent 永远不沙箱,直接写 mode: "off"

范围(scope):创建多少个容器

通过 agents.defaults.sandbox.scope 控制:

范围行为
"session"(默认)每个会话一个容器
"agent"每个 agent 一个容器
"shared"所有被沙箱的会话共享一个容器

怎么选看隔离需求。"session" 最安全,会话间完全隔离。"shared" 最省资源。多 agent 场景下,"agent" 通常最合理。

后端(backend):用什么跑沙箱

通过 agents.defaults.sandbox.backend 控制:

后端适用场景
"docker"(默认)本地 Docker 容器,完全隔离
"ssh"任意 SSH 可达的远程主机
"openshell"OpenShell 托管的远程沙箱

大部分人用 Docker 就够了。

从零开始设置 Docker 沙箱

第一步:构建沙箱镜像

最小镜像:

想看更多实战拆解?

AI、工程与实验,每月 1–2 封。

无垃圾邮件,随时取消。

scripts/sandbox-setup.sh

出来的是 openclaw-sandbox:bookworm-slim,基于 Debian,非常精简。

需要常用工具(curl、jq、nodejs、python3、git)的话:

scripts/sandbox-common-setup.sh

出来的叫 openclaw-sandbox-common:bookworm-slim

第二步:最小配置

~/.openclaw/openclaw.json 里写:

{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main",
        scope: "session",
        workspaceAccess: "none",
      },
    },
  },
}

这是最保守的配置:只沙箱非主会话,每会话一个容器,不让容器看到你的真实文件。

第三步:验证

openclaw sandbox explain

这条命令打印出当前生效的沙箱模式、范围、工具策略。对不对,看这里就知道。

自定义 Docker 镜像和 setupCommand

默认的 bookworm-slim 镜像很精简,连 Node 都没有。你的 skill 需要特定运行时,两条路。

第一条路:setupCommand

容器创建后运行一次(不是每次执行都运行):

{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        docker: {
          network: "bridge",  // setupCommand 需要网络来装包
          setupCommand: "apt-get update && apt-get install -y nodejs npm curl",
        },
      },
    },
  },
}

几个坑:

  • 默认 docker.network"none",没有网络,apt-get 会失败。装包之前先把 network 改成 "bridge"
  • 设了 readOnlyRoot: true 的话写操作会失败
  • 容器用户必须是 root 才能装包。省略 user 配置或者写 user: "0:0"
  • 沙箱不继承主机的 process.env。skill 需要的 API key 通过 sandbox.docker.env 传入

第二条路:烤一个自定义镜像

需求比较固定的话,直接做一个包含所有依赖的镜像更干净:

{
  agents: {
    defaults: {
      sandbox: {
        docker: {
          image: "my-custom-sandbox:latest",
        },
      },
    },
  },
}

自定义绑定挂载

docker.binds 把主机目录挂进容器:

{
  agents: {
    defaults: {
      sandbox: {
        docker: {
          binds: [
            "/home/user/source:/source:ro",
            "/var/data/myapp:/data:ro",
          ],
        },
      },
    },
  },
}

格式是 host:container:mode。不写 mode 默认是 rw

安全方面几条规矩:

  • 绑定会穿透沙箱文件系统,小心使用
  • 密钥文件、SSH key、凭证用 :ro
  • OpenClaw 会阻止挂载 docker.sock/etc/proc/sys/dev 这些危险路径
  • scope: "shared" 时忽略每 agent 的绑定,只看全局配置

沙箱内浏览器

沙箱也能跑一个隔离的浏览器。先构建浏览器沙箱镜像:

scripts/sandbox-browser-setup.sh

相关配置在 agents.defaults.sandbox.browser 下:

  • autoStart:浏览器工具需要时自动启动,默认开启
  • network:浏览器容器用专用 Docker 网络 openclaw-sandbox-browser
  • cdpSourceRange:CIDR 白名单,限制 CDP 连接来源
  • allowHostControl:是否允许沙箱会话控制主机浏览器

浏览器沙箱的 noVNC 访问有密码保护,OpenClaw 生成的是带密码的短期 token URL。

工具策略:沙箱之外的第二道防线

沙箱管的是"工具在哪里跑",工具策略管的是"工具能不能跑"。两个独立的系统。

三个控制层

OpenClaw 的安全控制分三层:

  1. 沙箱(sandbox.*):决定工具在 Docker 容器里跑还是在主机上跑
  2. 工具策略(tools.*):决定哪些工具可用、哪些被禁止
  3. Elevated(tools.elevated.*):exec 专用的逃生通道,允许被沙箱的会话在主机上跑命令

工具策略的规矩

  • deny 永远赢。一个工具被 deny 了,沙箱加不回来,elevated 也救不了
  • allow 列表非空的话,不在列表里的工具全部被阻止
  • 工具策略是硬限制,/exec 命令不能覆盖被 deny 的 exec 工具

工具组快捷方式

不用一个个写工具名,可以用组:

{
  tools: {
    sandbox: {
      tools: {
        allow: ["group:runtime", "group:fs", "group:sessions", "group:memory"],
      },
    },
  },
}

可用的组:

包含的工具
group:runtimeexec, bash, process
group:fsread, write, edit, apply_patch
group:sessionssessions_list, sessions_history, sessions_send, sessions_spawn, session_status
group:memorymemory_search, memory_get
group:uibrowser, canvas
group:automationcron, gateway
group:messagingmessage
group:nodesnodes
group:openclaw所有内置 OpenClaw 工具,不含 provider 插件

Elevated:exec 的逃生通道

沙箱启用后,有时你确实需要在主机上跑一个命令。Elevated 干的就是这件事:

  • /elevated on:让当前会话的 exec 在主机上跑,仍需审批
  • /elevated full:跳过审批

Elevated 只影响 exec,不给额外工具权限。exec 被工具策略 deny 了,elevated 也没用。

使用条件:

  • tools.elevated.enabled 必须为 true
  • 发送者必须在 tools.elevated.allowFrom.<provider> 白名单里

几个典型配置

只读 agent

只能读文件,不能改任何东西:

{
  tools: {
    allow: ["read"],
    deny: ["exec", "write", "edit", "apply_patch", "process"],
  },
}

安全执行 agent(能跑命令但不能改文件)

{
  tools: {
    allow: ["read", "exec", "process"],
    deny: ["write", "edit", "apply_patch", "browser", "gateway"],
  },
}

纯通信 agent(只能收发消息)

{
  tools: {
    sessions: { visibility: "tree" },
    allow: ["sessions_list", "sessions_send", "sessions_history", "session_status"],
    deny: ["exec", "write", "edit", "apply_patch", "read", "browser"],
  },
}

多 agent 差异化配置

个人 agent 全权限在主机跑,家庭 agent 沙箱隔离且只读:

{
  agents: {
    list: [
      {
        id: "personal",
        workspace: "~/.openclaw/workspace-personal",
        sandbox: { mode: "off" },
      },
      {
        id: "family",
        workspace: "~/.openclaw/workspace-family",
        sandbox: {
          mode: "all",
          scope: "agent",
        },
        tools: {
          allow: ["read"],
          deny: ["exec", "write", "edit", "apply_patch", "process", "browser"],
        },
      },
    ],
  },
}

SSH 后端:远程主机当沙箱

不想在本地跑 Docker,可以把沙箱放到远程服务器上:

{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        backend: "ssh",
        scope: "session",
        workspaceAccess: "rw",
        ssh: {
          target: "user@remote-host:22",
          workspaceRoot: "/tmp/openclaw-sandboxes",
          strictHostKeyChecking: true,
          identityFile: "~/.ssh/id_ed25519",
        },
      },
    },
  },
}

工作原理四步:

  1. OpenClaw 在远程主机上创建一个按 scope 隔离的目录
  2. 首次使用时把本地工作区同步到远程
  3. 之后 exec、read、write 等操作都在远程执行
  4. OpenClaw 不会自动把远程改动同步回本地

这是一个"远程即权威"的模型。远程工作区在初始同步后就是沙箱的真实状态。你在本地改了文件,远程看不到,除非用 openclaw sandbox recreate 重新同步。

SSH 后端不支持沙箱浏览器。

调试沙箱问题

最有用的命令:

# 查看当前生效的沙箱配置
openclaw sandbox explain

# 查看特定会话的沙箱状态
openclaw sandbox explain --session agent:main:main

# 查看特定 agent 的配置
openclaw sandbox explain --agent work

# JSON 输出,方便脚本处理
openclaw sandbox explain --json

常见问题

"工具 X 被沙箱策略阻止了"

两个解法:关掉沙箱(agents.defaults.sandbox.mode: "off"),或者在沙箱内允许该工具(加到 tools.sandbox.tools.allow 或从 deny 中移除)。

"我以为这是主会话,怎么被沙箱了?"

"non-main" 模式下,群组和频道的会话不算 main。用 sandbox explain 看一下实际的会话 key。要么切到 "off" 模式,要么接受群组会话就是会被沙箱。

"设了 mode: all 但 agent 没被沙箱"

查一下是不是有全局的 agents.defaults.sandbox.mode 在覆盖。Agent 级配置优先级更高,确认没被冲掉。

"setupCommand 运行失败"

十有八九是默认网络 "none" 造成的。装包需要网络,把 docker.network 改成 "bridge"。另外检查 readOnlyRoot 是不是 true,用户是不是 root。

"容器没有按 agent 隔离"

scope 默认是 "session",要每 agent 一个容器就设 scope: "agent"

小结

OpenClaw 的沙箱分三层:

  1. 沙箱(sandbox):决定工具在哪里跑。Docker 容器、SSH 远程、还是主机
  2. 工具策略(tool policy):决定哪些工具可用。allow 和 deny 列表
  3. Elevated:被沙箱后仍需在主机跑 exec 时的逃生通道

三者独立但叠加。deny 永远赢,沙箱不能绕过工具策略,elevated 只管 exec。搞明白这三层,配置起来就不会犯糊涂。

核心三步:建镜像、在 openclaw.json 里写配置、用 sandbox explain 验证。

新文章,直接发到你的邮箱。

AI、工程与实验,每月 1–2 封。

无垃圾邮件,随时取消。