Skip to content

fix(sandbox): macOS 沙盒 deny lark-cli 认证/配置路径,agent 在沙盒内无法读 auth/whoami/doctor(Full Disk Access 无法覆盖 Seatbelt) #683

Description

@alexander2618

问题

开启文件沙盒(sandbox: true / BOTMUX_SANDBOX=1 / 主机 device enroll 强制凭据隔离)后,在 macOS 上 agent 在沙盒内跑 lark-cli(以及类似把认证/配置写到用户目录下的 CLI)时,认证与配置信息读不到:

lark-cli doctor   → config_file: operation not permitted
lark-cli whoami   → not configured
lark-cli auth status → not configured
node 直接 stat/读取 lark-cli 配置文件 → EPERM

关键现象:在系统设置里给 node、codex「Full Disk Access / 全磁盘访问权限」也不起作用。

根因

macOS 有两套互相独立的强制访问控制层:

  1. TCC / Full Disk Access(系统设置 → 隐私与安全性)——管隐私弹窗层。
  2. Seatbelt(sandbox-exec / sandbox_init——内核级 MAC,botmux 用 sandbox-exec -f <profile.sb> 包住 CLI 进程强制施加。

botmux 在 src/worker.ts:7740-7751 把 spawn 改成 sandbox-exec -f <profile> <cliBin> ...,profile 由 src/adapters/cli/fs-policy.ts:500compileToSeatbelt() 生成,起手 (deny default)(line 503)后逐路径 emit (deny file-read* (subpath "..."))。这些 deny 在 vnode 层由内核拦,和 TCC 完全无关——所以给 node/codex 全磁盘访问对 seatbelt 的 deny 规则没有任何作用,读被拒路径照样返回 EPERM。这正解释了「给全磁盘访问也不行」。

涉及的 deny 规则

src/adapters/cli/fs-policy.ts baseline 里直接 deny 的 lark-cli 路径:

规则 路径
300 deny ~/.lark-cli
330 deny ~/Library/Application Support/lark-cli
326 deny ~/Library/Keychains

macOS 上 lark-cli 的用户登录态 / whoami / auth token / config_file 就落在这两个被 deny 的目录下。沙盒只开了两个极窄的只读 carve-out(fs-policy.ts:434-437):

~/Library/Application Support/lark-cli/master.key.file
~/Library/Application Support/lark-cli/appsecret_<appId>.enc

外加可写目录 ~/.lark-cli-bots/<appId>(line 383,per-bot 身份配置)。

为什么 doctor / whoami / auth status 失败

这条 carve-out 的注释(fs-policy.ts:423-433)写得很清楚:它只验证过 lark-cli auth scopes(botmux 自己的 send 链路用),设计意图是「bot 自己的 send 走 BOT_HOME 里的 send-cred.json,不读这个 key store」。即 carve-out 是按 botmux send 链路的最小需求设计的,没有覆盖 agent 直接跑 lark-cli doctor / whoami / auth status 的需求

  • whoami → 要读用户身份 token(在 deny 的 ~/.lark-cli / ~/Library/Application Support/lark-cli)→ EPERM → "not configured"
  • auth status → 要读登录/授权状态(同上被 deny)→ "not configured"
  • doctor config_file → 要读 lark-cli 配置文件(在 deny 区)→ "operation not permitted"

对「botmux send 链路」符合设计;对「agent 在沙盒内裸跑 lark-cli 自省/操作身份」是覆盖缺口。

影响面

  • 平台:仅 macOS(Linux 走 bwrap,lark-cli 密钥放 per-bot ~/.lark-cli-bots/<self>,已 readWrite,不受影响——见 fs-policy.ts:432 注释)
  • 会话类型:所有开启沙盒的 bot 会话(sandbox: true、readIsolation、device enroll 强制隔离)
  • CLI:lark-cli 及任何把认证/配置写到 ~/Library/Application Support/<app>~/.<app> 下、且未被 carve-out 显式放行的 CLI

建议的解法方向(待讨论)

  1. 关沙盒:受影响 bot 的 bots.json"sandbox": false,并确保无 BOTMUX_SANDBOX=1 全局强制、主机未 device enroll——最直接,但失去跨 bot 隔离。
  2. sandboxPaths 开洞:在 bot 配置里把所需路径加进 sandboxPaths.readOnly(user 源 rank=3 > baseline rank=0,能覆盖 line 300/330 的 deny)。但整目录开 ~/Library/Application Support/lark-cli 会把所有 bot 的 appsecret 密文 + master key 暴露给当前 bot 的 agent,等于回到重构前的跨 bot 泄漏面(line 327-330 注释专门提到的那条),是安全回退。
  3. 扩 carve-out(推荐):先查清 lark-cli 在 macOS 上 whoami / auth status / doctor 实际读哪几个文件,把本 bot 自己需要的文件以本 bot 限定方式补成只读 carve-out(同 appsecret_<appId>.enc 做法),不破坏跨 bot 隔离。

环境

  • 平台:macOS
  • 沙盒:开启
  • 相关代码:
    • src/adapters/cli/fs-policy.ts:300,326,330(deny)
    • src/adapters/cli/fs-policy.ts:434-437(lark-cli carve-out)
    • src/adapters/cli/fs-policy.ts:500-560compileToSeatbelt
    • src/worker.ts:7740-7751sandbox-exec -f 包 spawn)
    • src/adapters/backend/sandbox.ts:232sandboxEnabled

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions