内容提要
在M1 Mac上安装并实测OpenShell运行受控Coding Agent,验证了三项核心机制:程序×端点×HTTP方法三级策略、凭证以占位符形式进入沙箱、策略变更前先经形式化检查。实测遇到四个问题:gateway启动时静默拉取镜像导致假死;policy update漏带--binary会清空二进制列表;Alpine镜像下载因busybox不识别realpath -e而失败;gateway重启后所有沙箱进入Error状态且restart-policy不生效。结论:OpenShell适合高风险任务,但日常迭代仍有摩擦。
延伸解读
策略粒度是核心优势,但操作习惯决定成败
OpenShell 的程序×端点×HTTP方法三级策略在实测中确实生效:curl 可 GET GitHub API,但 POST 被 403 拦截,bash 直连同一端点则连接层拒绝。三种拒绝形态(连接层拒绝、policy_denied、上游响应)可区分,便于排障。但 policy update 漏带 --binary 会静默清空二进制列表,导致规则对一切程序失效。这意味着策略的精细度依赖使用者每次操作都完整传递参数,否则边界可能意外消失。
凭证占位符机制可靠,但绑定边界需单独理解
沙箱内环境变量只暴露不透明句柄,真实 key 由 supervisor 在转发时注入,未解析占位符一律 fail closed。实测中,即使网络规则放行 api.github.com,用同一占位符访问仍返回 credential_endpoint_mismatch 403,说明凭证可用性与网络放行是两道独立闸门。轮换和 detach 会回收句柄与 DNS 应答,但 detach 有约 5 秒传播窗口,自动化验证需留出时间,避免在窗口内误判。
gateway 是脆弱控制面,重启成本需提前计入
gateway 启动时会静默拉取受信镜像,链路慢时表现为假死,盲目重启会打断拉取进入循环。更严重的是,gateway 重启后所有沙箱进入 Error 状态,--restart-policy always 也不生效,Restart count 保持为 0,只能逐个 stop/start 恢复。恢复后工作区、策略版本和 provider 链路完整,但 Agent 会话丢失。因此 gateway 应视为尽量不动的控制面,升级前需评估停机成本。
适合高风险任务,日常迭代仍有摩擦
OpenShell 的安全模型全部兑现,但日常使用中每个新端点需一次人审,rule approve 后还有约 4 秒传播延迟,快速迭代时是纯摩擦。官方 OpenCode 镜像极简,缺少 bash、python3、git、curl,且版本落后上游,Alpine 底镜像上 sandbox download 因 busybox 不识别 realpath -e 而失败。因此它更适合跑不可信依赖或挂生产凭证的高风险任务,与 Docker Sandboxes 互补,而非替代日常本地小修小补。
Q&A
OpenShell 如何防止 Coding Agent 把敏感凭证泄露到沙箱外?
OpenShell 通过凭证占位符机制实现:真实 API key 只存在于 supervisor 中,沙箱内环境变量是形如 openshell:resolve:env:... 的不透明句柄。当 Agent 发起请求时,supervisor 在转发前将占位符替换为真实 key,且该 key 仅对 provider profile 中绑定的端点生效。若请求发往未绑定端点,会返回 403 credential_endpoint_mismatch,真实 key 永不进入沙箱。
OpenShell 的网络策略粒度有多细?能控制到程序级别吗?
OpenShell 支持程序 × 端点 × HTTP 方法三级策略。例如,可以指定只允许 /usr/bin/curl 对 api.github.com:443 进行只读访问(GET/HEAD/OPTIONS),而同一端点上的 POST 请求会被七层拦截返回 403。策略规则热生效,无需重启沙箱。
安装 OpenShell 后 gateway 启动超时,进程活着但不监听端口,怎么排查?
这是 gateway 启动时同步拉取两份受信镜像(supervisor 和 sandbox)导致的假死,拉取完成前不绑定端口且无进度日志。排查时先看日志是否停在 driver 初始化,再用 lsof 确认进程文件描述符连着 Docker socket。修法:手动预拉镜像 docker pull ghcr.io/nvidia/openshell/supervisor:v0.1.5 和 sandbox:v0.1.5,然后重启 gateway 即可快速绑定。
使用 openshell policy update 追加规则时,为什么原本能通的请求突然全被拒了?
很可能是因为漏带了 --binary 参数。对已有规则执行 policy update 时,如果未指定 --binary,合并操作会以 binaries=[] 落地,清空该规则的二进制列表,导致规则对一切程序生效为零。恢复方法:用 --remove-rule 删除整条规则重建,或用 policy set 整体替换策略。每次 policy update 都带上完整的 --binary 列表是稳妥习惯。
OpenShell 沙箱里 DNS 解析成功是否意味着网络已放行?
不是。OpenShell 使用 fake-IP 设计:沙箱内 DNS 由 supervisor 应答,无论域名是否授权都返回合成地址(如 198.18.0.2),真实 IP 从不进入沙箱。实际连接是否放行由 supervisor 在连接层决策,未授权时返回 Permission denied(EACCES)。因此 DNS 解析成功不能作为放行信号。
重启 OpenShell gateway 后所有沙箱都变成 Error 状态,如何恢复?
gateway 重启会导致旧 supervisor 消失,旧 workload 被 SIGTERM 后由新 supervisor 重建,若恢复失败则沙箱进入 Error。--restart-policy always 也不覆盖此路径。恢复方法是逐个执行 openshell sandbox stop <name> 再 openshell sandbox start <name>,单个约 6.5 秒。工作区文件、策略版本和 provider 注入链路都会保留,仅丢失 Agent 会话。
在 Alpine 镜像的沙箱中执行 openshell sandbox download 失败,报错 ssh probe exited with status 1,是什么原因?
原因是 CLI 的探针命令依赖 GNU coreutils 的 realpath -e 旗标,而 Alpine 镜像使用 busybox 的 realpath 不识别 -e,将其当作文件名导致退出码 1。修法:在镜像中安装 coreutils(apk add coreutils),或临时用 openshell sandbox exec cat 取回文件。
OpenShell 适合日常开发迭代吗?还是只适合特定场景?
OpenShell 更适合高风险任务,如运行不可信依赖、第三方代码或需要挂载生产凭证的任务,因为其程序级网络策略和凭证占位符机制能有效控制风险。但日常快速迭代时,每个新端点都需要人工审批,且官方示例镜像工具链不全(如缺少 python3、git),会带来摩擦。建议将其作为高风险任务的可选层,与 Docker Sandboxes 互补使用。