OpenShell 拆解:NVIDIA 给自主 AI Agent 的沙箱——不是审批,不是容器,而是策略边界
一、速览
★★★★☆(4/5)。一个来自 NVIDIA 的 Rust 项目,Apache-2.0,8.7k star / 1.3k fork(截至 2026-09-22)。把 agentgate 那种”审批工作流” 和 SkillSpector 那种”技能审计” 都拿掉之后剩下的那个空白格 —— 给自主 AI Agent 一个带策略边界的运行时:agent 不是被审批,是被关进一个四层策略包裹的隔离容器,每次 outbound 请求都要过「允许 / 绑证 / 拒」三态闸门。扣一星是因为 0.1.0 还没发布(仓库当前 alpha,README 第一行就是 “OpenShell 0.1.0 is coming soon”),403 个 open issues 也说明这是一个正在快速迭代的项目,生产慎用,研究 / 内网 / 受控环境可以放心试。
| 主语言 | Rust(核心 + Gateway),Python / TypeScript / Go / Rust 四种 SDK |
| 许可证 | Apache-2.0 |
| Star / Fork | 8,746 / 1,269 |
| 首次发布 | 2026-02-24(半年不到) |
| 最近提交 | 2026-09-22(仍在高频迭代) |
| Open Issues | 403(早期常态,README 给的预期就是 0.1.0 前会很多) |
| 算力后端 | Docker / Podman / MicroVM / Kubernetes |
| 网络策略粒度 | L7(HTTP method + path) |
| 地址 | https://github.com/NVIDIA/OpenShell |
适合谁(按人群打分,5 分制):
| 人群 | 分 | 理由 |
|---|---|---|
| 企业安全运营 / CISO | 5 | 这是市面上少有的「给 agent 接生产系统前的那一道闸」,凭证绑定端点、网络按 method/path 收紧,比让 agent 直接拿 API key 跑得稳得多 |
| AI 安全研究员 | 5 | 四层策略模型、endpoint-bound credential、L7 策略本身都是好题材,repo 里 examples/sandbox-policy-quickstart/ 是现成的复现靶场 |
| 红队 / 渗透 | 3 | 不是攻击工具,但它能装 Claude Code / Codex / OpenCode 当对手 —— 用来做「在受限沙箱里能不能把 agent 逼出策略」的对抗测试有意思 |
| CTF 选手 | 2 | 跟比赛关系不大,但 examples/ 下几个 walkthrough 适合练 L7 策略调优手感 |
| 个人学习 / 自部署 | 3 | 装得起(openshell sandbox create 一条命令),但 alpha 阶段官方也建议在 dev 集群跑,别拿生产数据库上去 |
二次开发:Apache-2.0 三档难度(改配置 / 改集成 / 改内核):
| 难度 | 能做的事 |
|---|---|
| 改配置 | 加 provider profile(CLI: openshell provider profile import);写一份自己的 sandbox policy YAML;为支持的 agent 配 custom image |
| 改集成 | 写一个新的 compute driver(crates/ 下对接容器/MicroVM/K8s 的实现都可参考);接一个新的 SDK 语言 |
| 改内核 | 改策略引擎(policy engine 跨 filesystem / network / process / provider 四层,是最值得啃的部分);改 Gateway 控制平面 |
二、是什么,不是什么
打开 https://github.com/NVIDIA/OpenShell 之前,先把三件事说清楚,因为 OpenShell 不是这几样常见的东西:
不是审批工作流。同样是 AI Agent 管控,agentgate 那种思路是「agent 要做动作 → 走 dashboard / Slack / Discord 让人类批准」。OpenShell 走的是另一条路:默认拒,按策略放。Agent 不是在申请权限,而是在一个已经被策略围好的容器里跑;想访问 GitHub API,先看 YAML 里有没有这一条。
不是 agent 漏洞扫描器。NVIDIA/SkillSpector 是「扫 agent 的 skill 里有没有 prompt injection / 危险命令」,入口是 skill 文件。OpenShell 不扫,它管运行时:假设你的 skill 是干净的,agent 也是可靠的,但还要再加一层「万一 agent 跑飞了呢」的兜底。
不是 CALDERA 那种 BAS。CALDERA 是 MITRE 的红队自动化平台(”adversary emulation”),OpenShell 是「让 agent 在更小的可信边界里做事」的蓝队工具。两者方向相反 —— OpenShell 假设你要保护生产系统,agent 是「受信任但需要约束」的对象,而不是「想模拟攻击者」的对象。
那 OpenShell 到底是什么?看官方一句话:
OpenShell is the safe, private runtime for autonomous AI agents. It provides sandboxed execution environments that protect your data, credentials, and infrastructure — governed by declarative YAML policies that prevent unauthorized file access, data exfiltration, and uncontrolled network activity.
三个关键词:sandboxed execution / declarative YAML policies / protect data credentials infrastructure。
三、四层保护 + 四个组件
OpenShell 的架构可以拆成「四个组件管四个保护层」,但比表格更重要的是组件之间的交互时序和责任边界。
四个组件(README):
| 组件 | 角色 |
|---|---|
| Gateway | 控制平面 API,负责沙箱生命周期,也是 auth 边界 |
| Sandbox | 隔离运行时 + 容器监管 + 策略强制的出站路由 |
| Policy Engine | 在 application 层到 kernel 之间强制度约 —— filesystem / network / process |
| Provider Access | profile 定义的端点 + 二进制策略 + 端点绑定的凭证注入 |
Gateway 是控制平面,但不是策略执行点。架构文档里写得很清楚:Gateway 暴露 gRPC API(生命周期、provider 管理、策略更新、日志、watch stream、relay forwarding)和 HTTP 端点(health、WebSocket tunnel、edge-auth),但它不做 per-request 的网络策略决策 —— 那些决策在 sandbox 内部的 supervisor + proxy 里完成。Gateway 只负责存储和下发策略,真正的强制发生在 kernel 层。这个设计让 Gateway 可以水平扩展而不成为性能瓶颈,同时也避免了「策略在外部、agent 在内部」的绕路问题。
Sandbox Readiness 状态机。Gateway 把 driver 报告的状态和 supervisor 会话状态组合成对外暴露的 SandboxPhase:
1 | backend_phase = derive_phase(driver_status) |
也就是说,对于 Docker/Podman/VM 这类 supervisor-controlled driver,driver 说 Ready 还不够,必须等 supervisor gRPC 会话建立才算 Ready;对于 K8s 这类 driver-reports-runtime-readiness 的 driver,driver 的 Ready 就是最终 Ready。这个区分决定了你调 openshell sandbox create 后多久能 connect —— Docker 本地通常秒级,K8s 可能要等 Pod 调度 + 镜像拉取。
Policy Engine 的 6 步网络决策流程。所有 outbound 流量在 sandbox 内部被强制通过 proxy,proxy 按以下顺序决策(architecture/security-policy.md):
- Force proxy — 用 namespace + seccomp 控制确保流量只能过 proxy;
- Binary identity — 识别发起请求的进程二进制路径,和策略中
binaries白名单比对; - Hard-block — 拒绝危险内部 IP 段(如 169.254.x.x、10.x.x.x 等),除非显式允许;
- Policy match — 把 destination + port + binary 和 network policy block 匹配;
- L7 rules — 对启用了 protocol inspection 的 endpoint,检查 HTTP method + path;
- Action — allow / deny / audit / log。显式 deny 和 hardening 检查优先于 allow;没有匹配到任何规则 → deny。
四个保护层(defense in depth):
| 层 | 保护什么 | 何时定 |
|---|---|---|
| Filesystem | 阻止沙箱读 / 写 allowed paths 之外的文件 | 沙箱创建时锁定 |
| Network | 阻止未授权 outbound | 运行时热更新 |
| Process | 阻止权限提升与危险 syscall | 沙箱创建时锁定 |
| Providers | 授予端点绑定的凭证和网络访问 | 运行时热更新 |
为什么 Filesystem / Process 要在创建时锁、Network / Providers 可以热更?因为前两者一旦放出去就难收回来(文件可能被复制到其他位置、提权后的进程可以 spawn 任意子进程),而后两者更多是「临时通行证」—— 网络白名单补一条 GitHub API、给新模型接一个 provider profile,都能即热加载。
四、网络策略与凭证模型(L7 + endpoint-bound)
OpenShell 最值得展开的两块是网络策略和凭证模型,因为这是它和「普通 Docker 沙箱」的最大差异。
网络:L7 级别,默认最小出站
README 里的 Quickstart 直接展示了一次完整的「先拒后放」:
1 | # 1. 创建沙箱(默认最小出站) |
注意三个细节:
- 403 是从 proxy 出来的,不是从 GitHub 出来的 —— agent 出不去就是出不去,DNS / IP / HTTPS 层都拦;
- GET 通、POST 拦 —— 策略不是「域名黑 / 白」,是「到 https://api.github.com 的 GET 允许 / POST 不允许」。这是 L7 级别,对比大多数「出站白名单」产品只到 L4(IP + 端口)是一大进步;
- 热更新不需要重启沙箱 ——
openshell policy set ... --wait期间沙箱还在跑。
Policy YAML 的字段结构
examples/sandbox-policy-quickstart/policy.yaml 的完整结构如下,这是理解 L7 策略的起点:
1 | version: 1 |
关键字段:
version: 1— 当前唯一支持的版本号,parser 遇到未知字段直接拒绝,没有宽松模式;filesystem_policy— 静态文件系统策略,read_only/read_write列表决定 agent 能碰哪些路径;landlock— Linux Landlock LSM 的兼容性设置,best_effort表示如果内核不支持也不会让沙箱起不来(但会记录警告);network_policies.<key>— 命名策略块,每个块包含endpoints(目标 host/port/protocol/access)和binaries(允许发起请求的进程白名单)。
Host wildcard 规则。endpoints[].host 支持 * 通配符,但有两个硬约束:只能出现在第一个 DNS label,或作为完整的中间 label;OPA runtime 用 . 作为 label 边界匹配,所以 *.github.com 能匹配 api.github.com,但 api.*.com 不行。validator 在策略加载时就校验这个边界,避免运行时静默不匹配。
Landlock 的实现细节
OpenShell 用 Linux Landlock LSM 做文件系统强制,不是简单的 chroot:
- Inode type 区分 rights。Landlock 规则根据已打开路径描述符报告的 inode 类型来分配权限:目录保留目录权限 + 文件权限;普通文件、设备节点、socket 只保留文件兼容权限。这让混合路径策略(如
/dev下既有目录又有设备节点)不会在不 weakenhard_requirement的前提下被拒绝。 - Baseline path enrichment。在应用 Landlock 之前,supervisor 会自动补充 runtime 需要的基础路径(如
/proc、/dev/urandom)。如果某个基础路径不存在,会跳过而不是让整个规则集失效。 - GPU 特殊处理。当沙箱启用 GPU 时,supervisor 会把现有 GPU 设备节点加入 read-write 路径,并把
/proc提升为 read-write —— 因为 CUDA workload 会在/proc/<pid>/task/<tid>/comm写线程元数据。
凭证:从不落盘,按端点注入
OpenShell 把所有 API key / token / service account 抽象为 Provider(命名凭证包)。Provider 的声明来自 provider profile,以 providers/anthropic.yaml 为例:
1 | id: anthropic |
再看 providers/claude-code.yaml,它展示了 agent 型 profile 的复杂度:
1 | id: claude-code |
关键设计:
| 性质 | 含义 |
|---|---|
| 凭证从不落盘到 sandbox 文件系统 | agent 看不到文件系统里的 ~/.aws/credentials,没法 cat 出来 |
| 凭证以环境变量注入,且绑定到 profile 授权的端点 | agent 即便读到 env var,也只对 authorized endpoint 有效 —— 拿到 key 后发到别的服务用不了 |
| profile 是 import-only,gateway 不内置 profile | 部署到生产前一定 review 过 profile 才 import,没有「OpenShell 自带一份 sample credential」的安全隐患 |
| CLI 能从 shell 环境 auto-discover credential | 部署时不用手动输每个 key,但前提是 user 自己已经把它们 export 出来了 |
binaries 是最小权限控制 |
即使 endpoint 和 credential 都匹配,发起请求的进程路径不在 binaries 列表里也会被拒绝 |
「绑端点」这一条是和「普通 secret manager」最大的差异:secret manager 给 agent 完整 key 让它自己去用,OpenShell 只在策略承认的请求上临时给 agent 用一下,request 一结束回收。而 binaries 字段则是「即使 key 被读到,也只能由特定进程使用」的第二层保护。
五、SDK 与生态
四个官方 SDK
| 语言 | 包 | 装法 |
|---|---|---|
| Python | openshell (PyPI) |
uv add openshell |
| TypeScript | @nvidia/openshell-sdk (GitHub Packages) |
npm install @nvidia/openshell-sdk(需先配 @nvidia scope → npm.pkg.github.com) |
| Go | github.com/NVIDIA/OpenShell/sdk/go |
go get github.com/NVIDIA/OpenShell/sdk/go@latest |
| Rust | openshell-sdk(当前 only-from-source) |
cargo add openshell-sdk --git ... --tag <release-tag> |
注意 Rust SDK 暂时只能从 git 装(README 写的是「pin to same release as gateway」)—— 0.1.0 没发,crate 没上 crates.io。
官方支持的 Agent
| Agent | 接入方式 |
|---|---|
| Claude Code | 装进 workload image + 挂 claude-code provider 或别的带端点的 model profile |
| OpenCode | 装进 image + 挂它的 model provider + policy |
| Codex | 同上,挂 OpenAI provider |
| GitHub Copilot CLI | 装进 image + 挂 GitHub credential + policy |
| OpenClaw | 走 NemoClaw blueprint |
| Hermes Agent | 走 NemoClaw blueprint |
这些不是「OpenShell 接管了这些 agent」,而是「OpenShell 知道这些 agent 的二进制路径、需要的端点和凭证类型,能为它们预制 profile」。其他 agent(自研的、内部 SDK)也可以用 —— 自己写 image、写 profile、声明 endpoint 就行。
Agent Skills 双层体系
OpenShell 把 skills 分成两层,这是它「agent-first」设计哲学的体现:
Public skills(skills/ 目录,用户安装):
openshell-cli— CLI 工作流辅助debug-openshell-cluster— Gateway 集群排障debug-inference— 推理端点排障generate-sandbox-policy— 自动生成沙箱策略
安装方式:
1 | npx skills add NVIDIA/OpenShell |
Contributor / Maintainer skills(.agents/skills/ 目录,不随 public skills 分发):
create-spike→state:accepted→ 可选的agent:*规划与实现工作流triage-issue— agent 评估技术有效性和影响,人类决定是否排期review-security-issue/fix-security-issue— 安全评估与修复sync-agent-infra、update-docs-from-commits— 仓库维护
OpenShell 的 README 明确说这个项目是 “built agent-first” —— 不仅给用户提供了 agent 工具,它自己的开发工作流也用了 agent。Contributor skills 不面向终端用户,但体现了 NVIDIA 对「agent-driven development」的押注。
装 agent 的姿势
1 | # 默认 image 不带任何 agent CLI(仅 Ubuntu 24.04 基础环境) |
「装好 OpenShell」和「让 agent 在 OpenShell 里跑」是两件事 —— OpenShell 提供隔离与策略,你负责把 agent 装进 image。这是个常见的责任划分:runtime 不背 agent 行为不端的锅。
Terminal UI
openshell term 启动一个 k9s 风格的实时终端 dashboard,每 2 秒自动刷新:
Tab切换面板j/k上下移动Enter选中:进入命令模式
TUI 不是装饰品 —— 它是 Gateway gRPC watch stream 的终端前端,能看到 sandbox 的实时状态、provider 附着情况、策略决策日志(action=allow 绿色 / action=deny 红色)。
六、模型自由度 / 计算后端 / GPU
四种 Compute Driver 的差异
OpenShell 的 compute runtime 不自己做策略评估,只负责「按 Gateway 给的 spec 启停 workload」。但四种 driver 的实现差异很大(architecture/compute-runtimes.md):
| Driver | 隔离级别 | 特点 |
|---|---|---|
| Docker | 容器 | 最省事,单机 demo 首选;supervisor 和 workload 共享 Docker 网络,但 proxy 强制所有 outbound |
| Podman | 容器(rootless) | Paired container 设计:workload 用 network=none,supervisor 单独 join 配置网络;两者通过 named volume 上的 Unix socket gRPC 通信;都跑在非 root、drop all capabilities |
| MicroVM | 虚拟机 | 最强隔离(容器逃逸防护);用 host virtualization 启动,supervisor 在 VM 外;目前只支持早期 MicroVM 实现,Firecracker / gVisor 之外的不全 |
| Kubernetes | Pod | 通过 Helm chart 部署 Gateway;supervisor.sandboxRuntime.networkPolicyEnforced=true 启用网络策略;适合已有 k8s 集群的企业 |
Podman driver 值得单独展开。它和 Docker 不一样:Podman 用 libpod API 同时启两个容器 —— 一个是 workload(agent 跑在里面),一个是 supervisor(跑在外面)。workload 的容器完全没有网络(network=none),所有 outbound 必须经过 supervisor 容器的 proxy。两个容器只共享 user namespace(为了 volume 所有权),不共享 PID、mount、network namespace。这种设计让即使 workload 容器被攻破,攻击者也拿不到网络访问 —— 因为 workload 容器根本没有网卡。
Driver Contract 的 7 条要求。每个 compute driver 必须实现(摘自架构文档):
- 选择 sandbox image;
- 在 workload 创建前解析不可变的非 root sandbox identity;
- 为 workload 和 supervisor 分别提供 bootstrap material;
- 只把
openshell-sandbox放进 workload,openshell-supervisor只给 supervisor; - 提供受保护的 control channel(Unix socket / TLS TCP / vsock);
- 转发 exact main-process argv 和 TTY mode,不做 shell 重建;
- 报告生命周期事件并清理 runtime-owned 资源。
能改什么不能改什么
| 维度 | 能改什么 | 不能改什么 |
|---|---|---|
| Compute backend | Docker / Podman / MicroVM / Kubernetes 切换 | 暂不支持 Firecracker / gVisor 之外的 MicroVM 实现 |
| Workload image | 自带 image、BYOC、从 registry 拉 | image 里必须有 agent —— default Ubuntu image 只有基础环境 |
| GPU | --gpu 标志启用 GPU passthrough(实验性) |
需要 host 上装 NVIDIA drivers + NVIDIA Container Toolkit;image 自己也要带 GPU 库 |
| 策略 | 网络 / Provider 可热更新 | Filesystem / Process 一旦创建不能改 |
| Credential 注入 | profile 自由定义端点和二进制 | 不能注入到未在 profile 里登记的端点 |
| Telemetry | 可以编译时关掉 | 关掉后 official 支持矩阵里不覆盖 |
GPU 直通值得单独说一句。openshell sandbox create --gpu ... 会尝试 CDI(Container Device Interface),没有就 fallback 到 Docker 的 --gpus all。文档里写得很诚实:「Expect rough edges and breaking changes」—— 是真的实验性,生产别赌。
Telemetry 的编译定制
Telemetry 不是简单的一个开关,OpenShell 提供了三层控制:
1. 运行时关闭(gateway 级别,向下传播到 sandbox):
1 | OPENSHELL_TELEMETRY_ENABLED=false openshell gateway ... |
2. 编译时剔除(生成不含任何 telemetry 端点、HTTP client、emission code 的二进制):
1 | cargo build --release -p openshell-gateway \ |
注意:--no-default-features 必须和 defaults-without-telemetry 一起用;单独传 --features defaults-without-telemetry 会失败(Cargo 不支持「减去单个默认 feature」)。
3. 按 driver 裁剪。Gateway 的 Cargo feature 还包括 compute-driver-kubernetes、compute-driver-docker、compute-driver-podman、compute-driver-vm、compute-driver-mxc(Windows)。可以只编译需要的 driver:
1 | # 只留 Docker driver,带 telemetry |
Telemetry 收集的数据范围也写得很细:只收集匿名操作类别和计数(sandbox 生命周期结果、provider profile bucket、策略决策计数、网络拒绝类别),绝不收集 sandbox 名称/ID、hostname、文件路径、二进制路径、prompt、凭证、provider 名称、模型名称、用户内容。
七、边界与风险
把 OpenShell 推到生产之前,必须知道的几个事实:
1. 0.1.0 还没发布。 README 第一行 > [!IMPORTANT] OpenShell 0.1.0 is coming soon。当前仓库 alpha,意味着:
- API / CLI 命令可能在 1.0 之前变;
- docs 链接的
latest实际上对应的是 0.0.x 文档(要看 prerelease 用dev); - 升级路径没有保证。
生产用之前,至少等到 0.1.0,并且锁版本。
2. 403 个 open issues。 这是 alpha 项目的常态,但也意味着:
- 「官方觉得的最佳实践」还在变;
- 你撞到的 edge case 可能没文档;
- 提 issue 等响应可能慢。
3. 默认 image 没有 agent。 OpenShell 不替用户装 agent。如果「我装上 OpenShell 但 agent 没装」这种新手坑可以让你少踩半天。
4. Filesystem / Process 策略一旦创建就锁。如果你的 agent 在跑的时候发现自己需要写一个临时目录但 policy 没允许 —— agent 不会去申请,会直接失败。设计「申请 + 审批」流程得自己额外搭(这恰恰是 agentgate 的领地)。
5. L7 策略写得粗等于没写。 例子里的 examples/sandbox-policy-quickstart/policy.yaml 是参考 —— 真实用的时候要覆盖到「该 agent 平时会访问的所有端点的所有 method」。否则 agent 试图访问一个忘了加白名单的端点时会直接被拒,调试体验不佳。
6. L7 vs TLS / 证书。 策略引擎只校验 method + path,不会校验 TLS 证书链 —— 如果你把根 CA pin 在了 gateway 内部而 agent 自带的 CA store 不一致,可能出现「policy 允许但 TLS 失败」的奇怪组合。这不是 OpenShell 独有的问题,但记一笔。
7. Rust SDK 暂时 source-only。 crates.io 没发版,写 Rust 集成要跟 git tag,且必须 pin 到和 gateway 同一 release。
8. OpenShell 不解决「agent prompt injection」。 SkillSpector 那种「扫 skill 里有没有恶意指令」是另一回事。OpenShell 只管运行时 —— 即便 agent 被注入「把这个文件 exfil 到 evil.com」,策略也会在 outbound 那一步把它拦下。但拦下之后你能否及时发现?这是日志/告警问题,不是 OpenShell 的锅。
9. Telemetry 的隐私边界。 虽然官方承诺不收集敏感数据,但如果你所在环境对「任何匿名遥测都不能出内网」有硬性要求,需要在编译阶段就用 --no-default-features --features defaults-without-telemetry 把 telemetry 完全剔除。运行时 OPENSHELL_TELEMETRY_ENABLED=false 只是关掉 emission,二进制里仍然保留了 telemetry endpoint 和 client 代码。
八、上手
最小可行 demo,三步:
1 | # 1. 装 OpenShell(参见 README "Install" 一节,取决于平台 —— macOS 用 brew,Linux 用 curl 一行 installer) |
然后跑官方 quickstart 走完「先拒后放」全流程:
1 | bash examples/sandbox-policy-quickstart/demo.sh |
如果想在 NVIDIA Brev 上试(云上秒起):
- 链接在 README「Learn More」:「Brev Launchable」。
第一次写自己策略的实操建议(踩过坑之后的):
- 从 Quickstart example 复制一份,把它当作自己项目的
.policy/base.yaml; - 先放最严格的,再慢慢松 —— 比起「先全开再收紧」,「先全关再放白」更容易审计;
- provider profile 拆细 —— 不要做一个「all permissions」的 provider,分成
provider-github-readonly/provider-openai-inference/provider-aws-s3-write等; - 接 agent 前先人肉 curl 测一遍 —— 用 curl 验证你写的策略是不是真的拦 / 放了你以为的那个请求;
- TLS 根 CA 一致 —— 上面边界章节提了,agent image 和 gateway 的 CA 要对齐;
- 用
openshell provider profile lint先校验 —— 在 import 之前跑 lint,能 catch 掉 endpoint 格式错误、binaries 路径不存在等常见问题。
九、一句话结论
OpenShell 不是一个新 agent 工具,是 「让现有 agent 安全接入生产系统」的一道闸 —— 用四层策略(filesystem / network / process / provider)和 L7 级别网络 + endpoint-bound credential 把 agent 关进一个有边界的世界。NVIDIA 官方背景 + Apache-2.0 + Rust 实现的「值得记一个结实底子」,但 0.1.0 还没发意味着这是给研究 / 内网 / 受控环境的工具,生产部署请等 0.1.0 至少一两个小版本再说。开始用的时候建议先 clone 一份 example policy 改出第一份自己的,再开始接 agent —— 不要跳过例子直接生产,否则你会和我一样在 TLS / 端点匹配上浪费一小时。