文章总结: NVIDIAOpenShell是面向AIAgent的开源安全运行时,采用四层内核级沙箱(网络命名空间、Landlock、seccomp、权限降级)与声明式策略引擎,实现L7HTTP方法+路径级的细粒度访问控制,解决Agent获得系统权限后的安全风险。其治理-first的设计理念适合企业安全团队进行合规审计与精细控制,是AI安全领域的纵深防御实践。 综合评分: 75 文章分类: AI安全,安全建设,安全工具
NVIDIA 对 Agent 安全问题交出的答卷:OpenShell 深度架构分析
原创
yzddMr6 yzddMr6
熵减矩阵
2026年3月26日 09:08 浙江
#
8 万行 Rust 代码,四层内核级沙箱,一套声明式策略引擎——OpenShell 是 NVIDIA 对 AI Agent 安全问题交出的答卷。这篇文章从架构深处拆解它的设计哲学、技术取舍,以及它给整个行业的启示。
1. 当 AI Agent 获得系统权限
OpenClaw 在短短几个月内席卷了开发者社区。它能帮你写代码、调试、部署,甚至自主完成整个开发任务。为了做到这些,它需要读写文件、执行 shell 命令、访问网络——换句话说,它需要你机器上几乎所有的权限。
这正是问题所在。一个拥有 shell 权限的 AI agent,同时也能读取你的 .env 文件里的数据库密码和云服务密钥,能 curl 任意 URL 把代码外泄到任何地方,能修改系统配置。而一旦 agent 遭遇 prompt injection 攻击——攻击者精心构造的恶意指令被注入到 agent 的上下文中——攻击者就继承了你给 agent 的全部权限。你的凭证、你的代码、你的基础设施,全部暴露在攻击面之下。
企业面对的困境是:禁用 AI agent 意味着放弃生产力优势,放开使用又意味着不可控的安全风险。目前没有好的中间方案。
这不是假设——Meta 禁止了 OpenClaw 在公司机器上运行,Microsoft 发布了专门的安全指南,CISA 在漏洞通报中引用了 OpenClaw。当 AI agent 从”聊天机器人”进化为”自主执行者”,安全模型必须跟着变。
现有方案各有不足:
- • Docker 容器:网络隔离只能做到 IP+端口级别,无法区分
GET /repos和POST /repos/issues - • 云沙箱(E2B、Daytona、Modal):需要云服务,有成本和延迟,策略粒度不够
- • 应用层约束(在 agent prompt 中说”不要访问敏感文件”):可被绕过,不可信
OpenShell 的核心主张很简单:安全控制必须在 agent 进程外部,由操作系统强制执行,agent 无法绕过。 这就是”浏览器标签页模型”——浏览器的沙箱不是靠网页 JavaScript 自我约束实现的,而是靠操作系统进程隔离。OpenShell 把同样的思路应用到 AI agent 上。
2. 项目全景与竞品定位
2.1 OpenShell 是什么
一句话:OpenShell 是一个开源的 AI agent 安全运行时,通过内核级沙箱 + 声明式 YAML 策略 + 推理隐私路由,让 Claude Code、Codex、OpenClaw 等 agent 在受控环境中运行。
项目由 NVIDIA 在 GTC 2026 发布,Apache 2.0 许可,作为 NVIDIA Agent Toolkit 的核心组件。目前处于 alpha 阶段(单用户模式),约 8 万行有效代码(Rust 为主 + Python SDK),10 个 Rust crate 组成 workspace。
2.2 架构鸟瞰
OpenShell 架构鸟瞰
四个核心组件各司其职:
| 组件 | 职责 | 关键特性 | | — | — | — | | Gateway | 控制平面:沙箱生命周期、策略分发、SSH 隧道 | 单端口复用 gRPC/HTTP/SSH,不执行策略 | | Sandbox | 数据平面:隔离执行、策略强制、流量拦截 | Landlock + seccomp + netns + OPA | | Policy Engine | 策略评估:L4 连接控制 + L7 请求检查 | 嵌入式 regorus(纯 Rust OPA),热更新 | | Privacy Router | 推理路由:凭证注入/剥离、模型覆写 | sandbox 内直连后端,不经 Gateway |
2.3 竞品光谱:从轻量到重量
AI agent 沙箱市场在 2025-2026 年快速分化。从轻到重,可以画出这样一条光谱:
AI Agent 沙箱竞品光谱:从轻量到重量
三个维度的对比:
安全模型
| 方案 | 隔离机制 | 网络策略粒度 | 凭证保护 | | — | — | — | — | | Membrane | Docker + eBPF | DNS 主机名白名单 | 无 | | Docker Sandboxes | Firecracker microVM | Allow/deny 列表 | 环境变量注入 | | E2B | Firecracker microVM | VM 级网络隔离 | SDK 管理 | | Modal | gVisor 系统调用拦截 | deny-by-default 出站 | 平台管理 | | OpenShell | Landlock+seccomp+netns | L7 HTTP 方法+路径级 | 占位符替换,agent 不持有真实密钥 |
OpenShell 是唯一能做到”允许 GET /repos/** 但拒绝 POST /repos/*/issues“的方案。这个粒度对企业合规至关重要——安全团队需要精确控制 agent 能做什么,而不是简单的”能不能访问 GitHub”。
开发者体验
| 方案 | 安装复杂度 | 首次启动时间 | 平台支持 | | — | — | — | — | | Membrane | 一条命令 | 秒级 | Linux | | Docker Sandboxes | Docker Desktop | ~125ms | 全平台 | | E2B | API key + SDK | ~150ms | 云端 | | OpenShell | install.sh + Docker | 分钟级(首次需部署 K3s) | Linux(alpha) |
OpenShell 的启动时间是最大的体验短板。首次 openshell sandbox create 需要拉取 K3s 镜像、部署集群、生成 mTLS 证书,整个过程可能需要 2-5 分钟。后续创建沙箱快得多(K8s Pod 调度),但首次体验远不如 docker run 那样即时。
架构哲学
| 方案 | 核心理念 | 适用场景 | | — | — | — | | E2B/Daytona | SDK-first:通过 API 管理沙箱生命周期 | AI 应用开发者,需要编程式控制 | | Modal | 平台-first:沙箱是 AI 基础设施的一部分 | ML 团队,需要 GPU + 推理 + 沙箱一体化 | | Docker Sandboxes | 简单-first:一个 Docker 容器搞定 | 个人开发者,需要最低门槛 | | OpenShell | 治理-first:策略即代码,可审计可版本控制 | 企业安全团队,需要合规审计和精细控制 |
NVIDIA 选择了”最重”的方案,这不是技术洁癖,而是目标用户决定的。OpenShell 的目标不是让个人开发者快速上手,而是让企业安全团队能够:用 YAML 定义策略 → git 管理 → code review → CI 验证 → 审计追踪。这条路径上的每一步都需要 OpenShell 提供的基础设施。
3. 纵深防御:四层安全隔离架构
OpenShell 的安全模型不依赖单一机制。它将四种独立的 Linux 内核安全原语组合成纵深防御体系——每一层都独立生效,即使某一层被绕过,其余层仍然提供保护。
四层纵深防御架构
3.1 网络命名空间 + veth pair:不可绕过的网络隔离
这是整个安全模型的基石。sandbox supervisor 在 fork agent 进程之前,创建一个独立的 Linux 网络命名空间,通过 veth pair 连接到主机命名空间:
主机命名空间 Sandbox 命名空间
───────────── ─────────────
veth-h-{uuid} veth-s-{uuid}
10.200.0.1/24 ◄── veth pair ──► 10.200.0.2/24
│ │
▼ ▼
代理监听器 Agent 进程
(OPA 策略评估) (默认路由 → 10.200.0.1)
│
▼
外部网络 (经策略过滤)
agent 进程在 pre_exec 中通过 setns(fd, CLONE_NEWNET) 进入 sandbox 命名空间——这是不可逆操作。进入后,agent 看到的网络栈中只有一个 veth 接口,默认路由指向代理地址。它根本看不到主机的网络接口。
为什么选择 veth pair 而非 iptables? iptables 方案中进程仍然共享主机网络栈,一个 CAP_NET_ADMIN 就能删除规则。veth pair 创建了物理上隔离的网络拓扑——即使 agent 获得 root 权限,它也无法直接访问主机网络。
绕过检测:supervisor 还在 sandbox 命名空间内安装 iptables OUTPUT 链规则,对任何绕过代理的直接出站流量记录 LOG 并 REJECT。后台 bypass_monitor 任务读取 /dev/kmsg 检测这些日志事件,提供快速失败的用户体验和诊断信息。
(crates/openshell-sandbox/src/sandbox/linux/netns.rs:41-170)
3.2 Landlock:文件系统访问白名单
Landlock 是 Linux 5.13+ 引入的非特权沙箱机制。OpenShell 用它实现文件系统访问控制——只有策略中明确列出的路径才能被访问,其余一律拒绝。
策略示例:
filesystem_policy:
read_only: [/usr, /lib, /proc, /dev/urandom, /etc]
read_write: [/sandbox, /tmp, /dev/null]
实现上,supervisor 为每个路径创建 PathBeneath 规则:只读路径获得 AccessFs::from_read 权限,读写路径获得 AccessFs::from_all 权限。调用 restrict_self() 后,规则对当前进程及所有后代生效,不可撤销。
执行顺序的微妙之处:Landlock 在 pre_exec 中的执行顺序是先 drop_privileges(切换到 sandbox 用户),再 sandbox::apply(应用 Landlock + seccomp)。这个顺序至关重要——drop_privileges 需要读取 /etc/passwd 和 /etc/group,而这些文件可能被 Landlock 规则限制。
兼容性策略:默认 best_effort 模式在内核不支持 Landlock 时静默降级(务实选择,降低采用门槛),生产环境应使用 hard_requirement 模式。
(crates/openshell-sandbox/src/sandbox/linux/landlock.rs:1-89)
3.3 seccomp BPF:系统调用级过滤
seccomp 是最后一道防线。它在系统调用层面阻止进程创建特定类型的网络连接:
| 网络模式 | 允许的 socket 类型 | 阻止的 socket 类型 | | — | — | — | | Proxy(默认) | AF_INET, AF_INET6, AF_UNIX | AF_PACKET, AF_NETLINK, AF_BLUETOOTH, AF_VSOCK | | Block | AF_UNIX | 以上全部 + AF_INET, AF_INET6 |
Proxy 模式允许 AF_INET/AF_INET6 看起来像安全漏洞,但实际上不是——agent 运行在网络命名空间中,它创建的 TCP socket 只能到达 veth 对端(即代理)。seccomp 在这里的作用是纵深防御:阻止 AF_PACKET(原始数据包嗅探)和 AF_NETLINK(网络配置修改)等危险操作。
应用 seccomp 前必须先设置 PR_SET_NO_NEW_PRIVS,确保子进程不能通过 setuid 二进制文件绕过过滤器。
(crates/openshell-sandbox/src/sandbox/linux/seccomp.rs:1-77)
3.4 权限降级:六步验证的 drop_privileges
sandbox 进程以 root 启动(需要创建网络命名空间),但在 exec agent 之前必须放弃所有特权。drop_privileges 实现了六步验证序列:
- 1.
initgroups()— 设置补充组 - 2.
setgid()— 切换组 ID - 3. 验证
getegid()匹配目标 GID - 4.
setuid()— 切换用户 ID - 5. 验证
geteuid()匹配目标 UID - 6. 验证
setuid(0)失败 — 确认无法重新获得 root
第 6 步是 CERT POS37-C 的硬化措施——某些内核配置下 setuid 可能不会完全放弃 saved-set-user-ID。显式验证确保这种边界情况被捕获。
(crates/openshell-sandbox/src/process.rs:352-459)
3.5 四层如何协同:一个请求的完整路径
当 sandbox 内的 Claude Code 执行 curl https://api.github.com/repos/octocat/hello-world 时:
- 1. seccomp 允许
socket(AF_INET, ...)— Proxy 模式下 TCP 被放行 - 2. 网络命名空间 将连接路由到
10.200.0.1:3128(代理地址) - 3. curl 发送
CONNECT api.github.com:443 - 4. 代理 从
/proc识别调用进程(curl, PID 42),构建 OPA 输入 - 5. OPA 引擎 评估
data.openshell.sandbox.network_action:
- • 端点匹配:
api.github.com:443在策略的github_api规则中 ✓ - • 二进制匹配:
/usr/bin/curl在规则的binaries列表中 ✓ - • 返回
"allow",匹配策略名github_api
- 6. SSRF 防护 DNS 解析
api.github.com,验证 IP 不是内部地址 ✓ - 7. 代理返回
200 Connection Established,建立 TCP 隧道 - 8. TLS 自动检测 peek 前 8 字节,检测到 TLS ClientHello
- 9. MITM 终止 使用临时 CA 签发
api.github.com证书,终止 TLS - 10. L7 检查 解析 HTTP 请求:
GET /repos/octocat/hello-world
- • OPA 评估
data.openshell.sandbox.allow_request - • 策略配置了
access: read-only(展开为 GET/HEAD/OPTIONS) - •
GET匹配 ✓,路径/repos/**匹配 ✓
-
- 代理重新加密并转发到真实的
api.github.com
- 代理重新加密并转发到真实的
- 12. Landlock 确保 curl 只能写入
/sandbox和/tmp
如果同一个 curl 尝试 POST /repos/octocat/hello-world/issues,步骤 1-9 相同,但步骤 10 会失败——POST 不在 read-only preset 中,代理返回 403 {"error":"policy_denied"}。
4. 策略引擎:从 YAML 到内核级执行
4.1 嵌入式 OPA:为什么用 regorus 而不是外部 OPA daemon
OpenShell 选择了 regorus——一个纯 Rust 的 Rego 评估器,而非官方的 Go OPA 运行时。这个选择有三个原因:
- 1. 无 CGO 依赖:官方 OPA 需要 Go 运行时,交叉编译困难。regorus 是纯 Rust,与项目其余部分统一工具链
- 2. 微秒级延迟:策略评估在每个 CONNECT 请求上执行,必须极快。regorus 通过
Mutex<regorus::Engine>序列化访问,单次评估在微秒级完成 - 3. Arc 共享编译策略:通过
arcfeature,clone 引擎只复制解释器状态,编译后的策略通过Arc共享。这让 L7 检查可以获得独立的引擎副本而不重新编译策略
引擎的 Rego 规则被编译进二进制(include_str!("../data/sandbox-policy.rego")),不依赖外部文件。策略数据(网络规则、文件系统配置等)通过 JSON 注入到 OPA 的 data 命名空间。
(crates/openshell-sandbox/src/opa.rs:59-147)
4.2 进程身份识别:谁在发起这个连接?
策略不仅控制”能访问哪个端点”,还控制”哪个二进制能访问”。代理在收到 CONNECT 请求时,需要识别发起连接的进程身份。
识别链通过 /proc 文件系统实现:
- 1. 从 TCP 连接的源端口查找
/proc/net/tcp,定位到 inode - 2. 遍历
/proc/*/fd/找到持有该 inode 的进程 - 3. 读取
/proc/{pid}/exe获取二进制路径 - 4. 沿
/proc/{pid}/status的 PPid 字段向上遍历祖先链 - 5. 读取
/proc/{pid}/cmdline获取命令行参数中的路径
OPA Rego 规则用四种策略匹配二进制身份:精确路径、祖先链匹配(如 claude 启动了 node,node 的祖先链中有 claude)、glob 模式(/usr/bin/*),以及一个重要的安全决策——cmdline_paths 被显式排除于 glob 匹配,因为 argv[0] 可以被 execve 轻易伪造。
BinaryIdentityCache 实现了 TOFU(Trust-On-First-Use)完整性检查:首次看到某个路径时计算 SHA256 哈希并缓存,后续访问时验证哈希一致。这防止了运行时替换二进制文件的攻击。
(crates/openshell-sandbox/src/procfs.rs、identity.rs、data/sandbox-policy.rego:128-153)
4.3 L7 MITM:TLS 自动检测与终止
当 OPA 允许一个 CONNECT 隧道后,代理需要决定是否进行 L7 检查。这取决于端点配置中是否有 protocol 字段。
TLS 处理采用自动检测模式:代理 peek 隧道的前几个字节,如果看到 TLS ClientHello(0x16 0x03),就执行 MITM 终止。具体流程:
- 1. sandbox 启动时生成临时 CA(
rcgen),写入/etc/openshell-tls/ - 2. agent 进程的环境变量
SSL_CERT_FILE、NODE_EXTRA_CA_CERTS等指向包含系统 CA + sandbox CA 的 bundle - 3. 代理使用 sandbox CA 为目标主机名签发临时叶子证书(
CertCache按主机名缓存) - 4. 与 agent 建立 TLS 连接(使用临时证书),与上游建立独立的 TLS 连接(使用系统 CA)
- 5. 在两个 TLS 连接之间解密、检查、重新加密
tls: skip 配置可以跳过 TLS 检测,直接建立原始隧道——用于不需要 L7 检查的场景(如数据库连接)。
(crates/openshell-sandbox/src/l7/tls.rs、l7/relay.rs、l7/rest.rs)
4.4 策略热更新与 Last-Known-Good
策略不是一次性加载的。运行中的 sandbox 可以接收策略更新,无需重启:
策略热更新时序
关键设计:只有 network_policies(动态字段)可以热更新。filesystem、landlock、process(静态字段)在 sandbox 创建时锁定,因为 Landlock 的 restrict_self() 和 setuid 都是不可逆操作。Gateway 的 UpdateSandboxPolicy RPC 会拒绝任何修改静态字段的请求。
LKG(Last-Known-Good)行为确保安全:reload_from_proto 先构建完整的新引擎,成功后才原子替换旧引擎。如果新策略有验证错误(如 L7 规则格式错误),旧引擎保持不变,sandbox 继续使用上一个有效策略。
(crates/openshell-sandbox/src/lib.rs:1305-1423)
4.5 Policy Advisor:从拒绝事件到策略建议
OpenShell 不只是拒绝不合规的请求——它还能自动生成策略建议。当 agent 的网络请求被拒绝时,sandbox 内的 DenialAggregator 收集拒绝事件,MechanisticMapper 将其转化为具体的策略修改建议,提交给 Gateway 供用户审批。
Policy Advisor 管道
这个管道的关键架构决策:所有分析都在 sandbox 侧运行。Gateway 只是一个薄的持久化+审批层,不生成建议也不调用 LLM。N 个 sandbox = N 个独立管道,天然水平扩展。
映射器生成的建议包含置信度评分(基于拒绝次数、端口识别度)和安全注释(私有 IP 警告、数据库端口警告)。用户可以在 TUI 中逐条审批([a] 批准 / [x] 拒绝),或批量审批。审批后的规则自动合并到活跃策略,sandbox 在下次轮询时热加载。
(crates/openshell-sandbox/src/denial_aggregator.rs、mechanistic_mapper.rs)
5. K3s-in-Docker:一个大胆的架构赌注
5.1 为什么选择 Kubernetes
OpenShell 把整个 K3s 集群塞进一个 Docker 容器里。这是整个项目中最具争议的架构决策。
选择 K8s 的理由是实际的:
- • 声明式沙箱管理:每个 sandbox 是一个 CRD(
agents.x-k8s.io/v1alpha1/Sandbox),K8s 的 reconciliation loop 自动处理 Pod 调度、重启、清理 - • 网络策略:Helm 管理的
NetworkPolicy限制 sandbox Pod 的 SSH 入站只能来自 Gateway Pod,防止集群内横向移动 - • 存储原语:Gateway 的 SQLite 数据库通过
PersistentVolumeClaim持久化,Pod 重启不丢数据 - • Helm 部署:所有组件通过 Helm chart 声明式部署,版本升级是 chart 更新
替代方案是直接使用 Docker API(docker run 创建容器),代价是需要自己实现:容器调度、健康检查、自动重启、网络隔离策略、存储管理。这些都是 K8s 已经解决的问题。
5.2 K3s-in-Docker:降低用户门槛
K3s 是 Rancher 的轻量 K8s 发行版,单二进制文件,内置 containerd、flannel CNI、local-path-provisioner。OpenShell 把它打包进一个 Docker 容器,用户不需要安装 K8s:
用户机器
└── Docker
└── openshell-cluster 容器
└── K3s (v1.35.2-k3s1)
├── Gateway StatefulSet
├── Sandbox Pod 1
├── Sandbox Pod 2
└── Agent Sandbox CRD Controller
cluster-entrypoint.sh 是集群启动的关键脚本(506 行 shell),它解决了 Docker-in-K3s 的一系列兼容性问题:
- • DNS 代理:Docker 的内部 DNS(127.0.0.11)对 K3s Pod 不可达,脚本通过 iptables DNAT 将容器 eth0 IP 的 53 端口转发到 Docker DNS
- • cgroup v1 兼容:K8s 1.35+ 默认拒绝 cgroup v1,脚本检测并添加
--kubelet-arg=fail-cgroupv1=false - • flannel 目录预创建:防止 kubelet 在 flannel 写入
subnet.env之前尝试创建 Pod sandbox - • NSSH1 密钥生成:
openssl rand -hex 32生成 SSH 握手密钥并注入 Helm values
5.3 代价与权衡
K3s-in-Docker 的代价是真实的:
- • 启动时间:首次部署需要拉取 ~1GB 的集群镜像,K3s 启动需要 30-60 秒,整个流程 2-5 分钟
- • 资源开销:K3s 本身消耗 ~200MB 内存,加上 etcd、CoreDNS、flannel 等组件
- • 调试复杂度:问题可能出在 Docker 层、K3s 层、Helm 层、应用层的任何一个。项目为此提供了
openshell doctor命令和debug-openshell-clusteragent skill - • 平台限制:网络命名空间和 Landlock 需要 Linux 内核,macOS/Windows 只能通过远程 Linux 主机使用
如果重新设计:对于单用户场景,直接使用 Docker API + 自定义网络可能更合适——启动快、资源少、调试简单。但 OpenShell 的目标是多租户企业部署,K8s 的调度、网络策略、RBAC 在那个场景下是必需的。当前的 alpha 阶段用 K3s-in-Docker 作为”单用户模式的 K8s 体验”,是一个合理的过渡策略。
(deploy/docker/cluster-entrypoint.sh、crates/openshell-bootstrap/)
6. Gateway 控制平面
Gateway 是 OpenShell 的”大脑”,但它有一个严格的自我约束:只协调,不执行。即使 Gateway 被完全攻破,已运行的 sandbox 仍然受内核级策略保护。
6.1 单端口协议复用
Gateway 只暴露一个端口(默认 8080),同时服务三种协议:
Gateway 单端口协议复用
这不是偷懒——在 K3s 集群内 NodePort 资源有限,在 Cloudflare Tunnel 场景下每个额外端口都意味着额外配置。MultiplexedService 按请求级别路由(不是连接级别),使用 hyper_util 的 serve_connection_with_upgrades() 支持 HTTP CONNECT 和 WebSocket 升级。
(crates/openshell-server/src/multiplex.rs)
6.2 持久化:万物皆对象的单表设计
Gateway 用一张 objects 表存储所有业务实体——Sandbox、Provider、SshSession、InferenceRoute、Settings 全部序列化为 protobuf blob。这个设计看起来粗暴,但对 Gateway 的数据模型恰到好处:实体类型多(6 种)但数量少(几十到几百),查询模式简单(按 ID/名称查找),不需要 JOIN。
protobuf 编码让 schema 演进无痛——新增字段不需要 ALTER TABLE。三个 trait(ObjectType、ObjectId、ObjectName)提供类型安全的泛型 CRUD,编译器确保你不会把 Sandbox 当 Provider 存。
SQLite 用于本地开发(自动创建文件),Postgres 用于生产多副本部署。两个后端在连接时自动运行迁移。
(crates/openshell-server/src/persistence/)
6.3 SSH 隧道:HTTP CONNECT + NSSH1 握手
sandbox 运行在 K3s Pod 中,没有公网 IP。用户通过 SSH 访问 sandbox 的路径是:
CLI → ssh-proxy 子命令 → HTTP CONNECT /connect/ssh → Gateway → NSSH1 握手 → Sandbox SSH daemon
NSSH1 是 OpenShell 自定义的握手协议:NSSH1 <token> <timestamp> <nonce> <hmac>\n。HMAC-SHA256 签名证明连接来自授权的 Gateway,timestamp + nonce 防止重放攻击(sandbox 维护 nonce 缓存,60 秒过期清理)。
sandbox 的 SSH daemon 基于 russh,每次启动生成临时 Ed25519 主机密钥,接受任何 SSH 认证(因为 NSSH1 已经完成了认证)。shell 进程在 PTY 中启动,继承完整的 sandbox 策略(Landlock + seccomp + netns + 权限降级)。
(crates/openshell-server/src/ssh_tunnel.rs、crates/openshell-sandbox/src/ssh.rs)
7. 推理路由与隐私保护
7.1 inference.local:透明代理设计
OpenShell 的推理路由解决了一个根本性的信任问题:agent 需要调用 LLM API,但它的凭证不应该泄露,敏感上下文也不应该发送到外部模型。
设计方案是一个虚拟主机 inference.local。agent 代码(如 Claude Code)被配置为将推理请求发送到 https://inference.local/v1/chat/completions,sandbox 代理拦截这个请求并路由到配置的后端。
推理路由时序
7.2 为什么推理在 Sandbox 内路由而不通过 Gateway
传统做法是让所有推理请求经过 Gateway 代理。OpenShell 选择了不同的路径——路由器运行在 sandbox 内部,直连后端。原因有二:
- 1. 延迟:LLM 流式响应对 time-to-first-token 敏感,额外的 Gateway 跳转增加延迟
- 2. 瓶颈:Gateway 成为所有推理流量的单点,N 个 sandbox 的推理请求都经过一个进程
Gateway 只负责控制平面:通过 GetInferenceBundle gRPC 将解析后的路由配置(含后端凭证)下发给 sandbox。sandbox 内的路由器直接与后端通信。
7.3 凭证注入/剥离的四层防护
路由器的 send_backend_request 实现了四层请求改写:
- 1. 认证注入:根据 provider 类型注入正确的认证头(OpenAI 用
Authorization: Bearer,Anthropic 用x-api-key) - 2. Header 清洗:强制剥离
authorization、x-api-key、host,防止 agent 凭证泄露 - 3. 默认 Header 注入:如 Anthropic 要求的
anthropic-version: 2023-06-01(尊重 agent 已发送的同名 Header) - 4. Model 覆写:请求体中的
model字段被强制替换为路由配置的值
更进一步,sandbox 的凭证管理采用占位符替换机制:agent 环境变量中的 API key 被替换为 openshell:resolve:env:ANTHROPIC_API_KEY 占位符。真实密钥只存在于 supervisor 进程内存中,代理在转发时替换。即使 agent 被完全攻陷,攻击者也无法从进程内存中提取真实密钥。
(crates/openshell-router/src/backend.rs:85-158、crates/openshell-sandbox/src/secrets.rs)
8. CLI、运维与可观测性
8.1 一条命令从零到沙箱
OpenShell 的 CLI 设计目标是极致简化首次体验:
openshell sandbox create -- claude
这一条命令背后发生了什么:
- 1. CLI 尝试连接 Gateway → 失败(没有集群)
- 2.
should_attempt_bootstrap()判断是连接性错误 → 触发自动引导 - 3. Bootstrap 拉取 K3s-in-Docker 镜像,创建集群容器
- 4. 等待 K3s 就绪,部署 Helm chart
- 5. 生成 mTLS 证书,存储到
~/.config/openshell/ - 6.
detect_provider_from_command("claude")→ 需要 Anthropic provider - 7. 从
$ANTHROPIC_API_KEY环境变量自动创建 provider - 8. 发送
CreateSandboxgRPC,开启WatchSandbox流跟踪进度 - 9. Pod 就绪后,通过 SSH ProxyCommand 连接到 sandbox
should_attempt_bootstrap() 的判断逻辑很精细:它区分”Gateway 不存在”(连接拒绝、DNS 失败)和”Gateway 存在但配置错误”(证书验证失败)。前者触发自动引导,后者直接报错。
8.2 文件同步:tar-over-SSH
文件上传使用 tar-over-SSH 而非 rsync:CLI 将本地文件打包为 tar 流,通过 SSH stdin 管道传输,远端执行 tar xf - -C /sandbox 解压。
权衡:不需要 sandbox 内安装 rsync(减少依赖),流式传输不需要中间文件,但没有增量同步能力。对于 OpenShell 的场景(一次性推送项目文件到本地网络的 sandbox),全量传输的开销可以接受。
(crates/openshell-cli/src/ssh.rs:454-525)
8.3 TUI:k9s 风格的实时监控
openshell term 启动一个基于 ratatui 的终端仪表盘,提供 Gateway、sandbox、provider 的实时监控。2 秒轮询 gRPC + 流式日志推送实现准实时可观测性,无需 Prometheus/Grafana。
日志查看器支持 vim 风格导航(j/k/g/G)、visual selection 模式、源过滤(Gateway/Sandbox)、OSC 52 剪贴板集成。Draft 审批面板实现了”人在回路”的策略治理——AI agent 推荐网络规则,运维人员在 TUI 中审批。
8.4 OCSF:企业级审计日志
OpenShell 将所有安全事件映射到 OCSF v1.7.0 标准格式(AWS、Splunk、IBM 联合推动的企业安全事件标准)。8 个事件类覆盖网络活动、HTTP 请求、SSH 会话、进程执行、安全检测、策略变更等。
双格式输出:shorthand(人类可读,用于 TUI)和 JSONL(机器可读,可直接导入 Splunk/Sentinel)。通过 thread-local 桥接与 Rust tracing 生态集成,同一事件同时写入两种格式。
9. 总结
拆完 8 万行代码,回过头来看 OpenShell,它其实在回答一个很朴素的问题:当 AI agent 强大到需要 root 权限才能干活,我们怎么确保它只干该干的事?
NVIDIA 给出的答案有真正的技术洞察。L7 策略粒度是整个方案中最有价值的部分——在它之前,没有开源方案能区分”读 GitHub 仓库”和”用你的身份创建 issue”。
推理路由的 inference.local 虚拟主机 + 凭证占位符替换,让 agent 代码零修改就能获得隐私保护。策略即代码的完整闭环(YAML → git → OPA → OCSF 审计),几乎是开箱即用的企业合规基础设施。但 K3s-in-Docker 的架构选择让启动时间比竞品慢了一个数量级,Linux-only 的限制砍掉了大量 macOS 开发者,alpha 阶段的成熟度离生产部署还有距离。这些取舍背后的逻辑是清晰的——NVIDIA 选择了企业治理优先于开发者体验——只是这条路注定走得更慢。
但比 OpenShell 本身更值得关注的,是它指向的方向。
AI agent 的权限膨胀是不可逆的趋势。从纯文本对话到文件读写,从命令执行到全自主开发——每一代产品都在索要更多的系统权限。这不是产品经理的贪心,而是能力边界的自然延伸。问题不在于要不要给权限,而在于怎么给。
当前的 agent 安全方案呈现出明显的两极分化:一端是 Membrane 这样的轻量方案,eBPF 做 DNS 白名单,几乎零开销但策略粒度粗;另一端是 OpenShell 这样的重量方案,四层纵深防御但部署门槛高。市场真正缺少的是中间地带——一个既有 L7 级策略粒度,又能秒级启动的方案。而且今天的方案大多绑定特定运行时:OpenShell 绑定 Linux 内核原语,E2B 绑定 Firecracker,Modal 绑定自家平台。一个真正通用的 agent 安全层,应该像 TLS 之于网络通信——无论底层是什么,上层都能获得一致的安全保证。
OpenShell 的策略即代码指向了一个有意思的未来:每个项目的仓库里不只有 Dockerfile 和 CI.yml,还有一份 agent-policy.yaml,声明式地定义 AI agent 能访问哪些 API、能读写哪些文件、能使用哪个模型。安全策略像代码一样版本控制、code review、CI 验证,融入现有的 DevOps 工作流,而不是成为一个独立的安全孤岛。
当 AI 的能力足够强大,约束它的方式就不能再是”请你不要这样做”的 prompt,而必须是操作系统级别的、不可绕过的、可审计的硬性边界。
这个笼子还很粗糙,但方向已经明确。剩下的问题是:谁来把它做得更轻、更通用、更无感。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:熵减矩阵 yzddMr6 yzddMr6《NVIDIA 对 Agent 安全问题交出的答卷:OpenShell 深度架构分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论