
零信任的下一站是进程:OpenShell supervisor 的对照与超越
TL;DR
对照 NIST SP 800-207 逐条读 OpenShell supervisor 源码:这是一份零信任的教科书实现,且把信任主体推到了进程。
一条写了五年的零信任线
去年底写 《AuthZen 工作组:打造统一的授权模型交互标准》 的时候,重新梳理了一遍 XACML 的授权模型:PEP 拦截请求、PDP 对照策略决策、PIP 补充属性、PAP 管理策略——这套 2013 年标准化的四组件模型,到今天还是几乎所有授权系统的骨架。
更早的 2022 年,还写过一篇 SPIFFE 和 SPIRE——workload 身份的标准与实现,是读《Solving the Bottom Turtle》时的总结与思考。零信任最热的那两年,也翻译过一批当时流行的文章:
- 云上细粒度访问管理的参考架构(2021)——VPN 和堡垒主机的三不足,以及理想架构的三原则
- 《BeyondCorp Part III: The Access Proxy》解读(2022)——Google 访问代理的认证、授权与集中日志
- 零信任对 Kubernetes 意味着什么 与 在 Kubernetes 上实施零信任(2022,译)——K8s 场景两篇
读完这几篇,你对零信任的印象大概是:边界之外皆不可信、一切决策看身份、策略集中管理。这条线在 2026 年遇到了新变量——AI Agent。Agent 要能干活就得能读文件、装依赖、调 API、用凭证,而它同时是被注入指令的高危载体:能力照单全收,信任无处安放。
上周发了篇 OpenShell 教程,讲怎么用 NVIDIA 的这个开源项目把 Coding Agent 装进沙箱。但教程只回答了“怎么用”,没回答“凭什么敢”。这个凭什么,藏在 supervisor 里——OpenShell 真正做安全决策的进程。
于是我读了 OpenShell 的源码。v0.1.5,supervisor 相关七个 crate 约十七万行,其中网络执法独占九万二千行。对照 NIST SP 800-207 逐条核下来,结论写在标题里:这是一份教科书式的零信任实现——而且它把零信任的信任主体,从“用户和设备”落到了你不曾想到的那一层:进程。
下文先铺教科书(NIST 说了什么),再对照源码逐条核(supervisor 做到了什么),最后说它比经典零信任走得更远的三处,和两处诚实的代价。
零信任的教科书:NIST 800-207 说了什么
2020 年 8 月,NIST 发布了 SP 800-207《Zero Trust Architecture》,59 页,第一次把零信任从营销词汇变成了可对照的架构标准。摘要里的定义句是全文的基座:
Zero trust assumes there is no implicit trust granted to assets or user accounts based solely on their physical or network location (i.e., local area networks versus the internet) or based on asset ownership (enterprise or personally owned).
零信任假定:不因资产或账户的物理/网络位置(内网 vs 互联网)、也不因资产归属(企业/个人)而授予任何隐式信任。
另一句更狠:“zero trust security models assume that an attacker is present in the environment”——假定攻击者已经在环境里。这个假定一旦成立,“内网”就不存在了——任何信任都不能默认成立,必须逐次验证。
标准给出了七大基本信条,可以归并成四组:
- 通信一律加密——所有通信都要防护,绝不因位于内网而给予信任;
- 按会话逐次授权——对单个资源的访问按会话授予、最小权限,对 A 资源的授权不会自动延伸到 B;
- 动态策略——访问决策基于身份、应用与请求资产的可观察状态,外加行为与环境属性;
- 持续监测——持续度量所有资产的完整性与态势,尽可能收集状态数据改进策略;没有任何资产天然可信。
架构层面,标准定义了三个逻辑组件:PE(Policy Engine,策略引擎,做出最终的允许/拒绝/撤销决定)、PA(Policy Administrator,执行 PE 的决定、生成会话凭证、配置 PEP 的连接路径)、PEP(Policy Enforcement Point,策略执行点,启用、监控并终止连接)。如果你读过 AuthZen 那篇里的 XACML 模型,会发现 PE/PA 就是 PDP/PAP 的变形——决策与管理的分离从 XACML 时代延续到了零信任,只是判断依据从静态属性变成了持续的可观察状态。
还有两个容易被忽略的点。其一,信任算法有两类思路:基于准则的像检查清单——把放行条件列成一条条硬性要求,全部满足才通过;基于评分的更像信用评分——设备是否合规、行为是否正常、地点是否可信,各占一定权重,加权算出总分,过线就放行。理想态是 contextual——连请求者近期的行为历史也计入评分,否则发现不了“失陷凭据的异常访问模式”。其二,标准 §3.2.4 专门列了一种部署变体:应用沙箱——沙箱内的应用允许与 PEP 通信,其余一切应用被拒。
标准早就给沙箱留了位置。剩下的问题是:一个真跑着 Agent 的沙箱,要怎么长成这个形状。
前作们的未竟之处
把这条线上写过的方案摊开对照,会发现它们各自停在了不同的层:
| 方案 | 授权粒度 | 身份的终点 |
|---|---|---|
| K8s NetworkPolicy | L3/L4(IP 与端口) | 无身份,只有地址 |
| 2021 参考架构(接入网关 + OPA) | 服务/网关级 | 用户 + 设备证书 |
| BeyondCorp Access Proxy | URL/应用级,叠加设备 Trust Tier | 人 + 设备 Trust Tier |
| SPIFFE/SPIRE | 服务级 mTLS | workload 实例(service account) |
这张表不是在说哪个方案做错了——它们写作时,受保护的对象还是“人和他们连着的基础设施”,停在自己的层就是当时正确的答案。变化的是被保护的对象:当沙箱里跑的是 Agent,信任的主体就从“人”变成了“一个软件进程”,而上面所有的层,都管不到这个粒度。
对 Agent 来说,缺的恰好是四块:
- 身份:SPIFFE 的身份到 service account 为止,一个 Pod 里跑什么二进制它不知道——而 Agent 的行为恰恰由那个二进制决定;
- 凭证:所有方案都把短期令牌发到使用者的进程里——令牌再短,一旦被注入指令引导,它就是现成的外传凭据;
- 流量:网关级的策略管不到“哪个程序对哪个端点发了什么方法”——而 Agent 的越界恰恰发生在这个粒度;
- 生命周期:策略收紧之后,已经建立的连接和正在运行的进程不会自动被重新审视。
supervisor 就是把这四块全部补齐的那个组件。怎么补的,接下来四章逐个拆。
补齐一:不信任对端
沙箱和 supervisor 之间的通道要防两头接管。文档说“Mutual TLS”,源码更冷:TLS 单向、两侧不装客户端证书,“双向”由一层 TLS 加一层 JWT 拼出。最偏执的细节是那张证书——一次性 Ed25519 CA 现签现弃,效期 1975 到 4096 年,刻意让校验不依赖任何一台机器的时钟。JWT 侧上三把锁(代次、凭证序号、实例钉死),token 全对也不行,必须是原来那个进程。
对照 NIST:信条 2(通信加密)与信条 6(动态严格执行);gateway 现签会话凭证,正是 PA 的角色。握手与帧格式的逐行拆解见 上篇。
通道可信了,下一个问题是:谁在这条通道上发起了请求。
补齐二:不信任自报身份
Agent 完全可以起一个进程、在参数里自称 curl,所以身份不问进程,问内核——沿 /proc 账本倒查到发起连接的那个二进制文件的真身路径。最偏执的细节:cmdline 被永久排除在身份信号之外,源码注释毫不客气,argv[0] “trivially spoofable via execve”——用 execve 一行就能伪造,永远不参与授权。SPIFFE 到 service account 为止,这里到进程加文件内容(活 inode 的 SHA256,TOFU,变更即拒);procfs 观测 + 哈希缓存,就是零信任术语里的进程级 PIP。
对照 NIST:信条 4(动态策略)与信条 5(持续度量完整性)。归因链的完整拆解见上篇。
身份解决的是“谁在发请求”,下一个问题是:请求的内容怎么审。
补齐三:不信任加密流量与凭证
对普通设备来说 HTTPS 是黑盒,supervisor 在中间拆开 TLS——沙箱内 curl 看到的证书签发者是 CN=OpenShell Sandbox CA。拆开后执法核心语义一条:deny 永远赢(allow ∧ ¬deny,无顺序可绕);档位两条:enforce 真拦,audit 只记不拦——观察数据不能宣称阻断。
凭证更细:沙箱 env 里只有句柄,数字不是序号,是内容指纹(SHA-256 前 8 字节,“opaque content identities, not ordered counters”)。真实 key 只在 supervisor,转发前按端点绑定注入七个位置;带到未绑定端点,403 是固定字符串;日志一律 [CREDENTIAL] 替身。
对照 NIST:信条 2(拆开检查、重新加密)与信条 3(逐请求动态授权)。证书信任链背景可看 《从 eMudhra 事件看证书信任链》;L7 管线与凭证注入的逐行拆解见上篇。
流量和凭证都有了归宿,最后:supervisor 自己出了事怎么办。
补齐四:不信任恢复者
supervisor 消失时,沙箱最安全的形态是原地不动,等正确的人来——SIGSTOP 冻结全进程组,30 秒恢复计时。最偏执的细节:workload 自己发 SIGCONT 被返回 EPERM、seccomp 连 tgkill 都拦——被冻者没有任何办法解冻自己;解冻只认“原 supervisor 进程 + 有效 token”。30 秒没人来,先解冻再终止——连死都给体面,不给自由。父进程存活检测朴素到一根 stdin 管道。
教程里 gateway 重启后 workload 齐刷刷死于 SIGTERM(#4910),就是“确认前收到信号即退出”的 fail-closed 分支。冻结与恢复的完整解剖见上篇。
结论:对照 NIST,逐条收账
先收账。NIST 的四组信条与三个逻辑组件,supervisor 每一项都有源码锚点:
| NIST 要求 | supervisor 的实现 | 锚点 |
|---|---|---|
| 信条 2:通信一律加密,不因位置给信任 | 单向 TLS + JWT 双向认证;无网卡的沙箱 | runtime.rs:327 |
| 信条 3:按会话逐次授权 | 每个 CONNECT / 每个 HTTP 请求一次求值 | opa.rs:606 |
| 信条 4:动态策略基于可观察状态 | 内核账本身份 + 祖先链喂给 OPA | procfs.rs |
| 信条 5:持续度量资产完整性 | SHA256 TOFU,二进制变更即拒 | identity.rs:249-256 |
| 信条 6:动态严格执行 | deny 永远赢;撤销规则即切断既有隧道 | rego:239-250 |
| PE / PA / PEP 分离 | sandbox 捕获上报 → supervisor 求值注入 → gateway 签发凭证 | 三明治结构 |
在这个骨架之上,它还有三处超出了经典零信任的边界:信任主体到进程(BeyondCorp 验证“设备上的人”,这里验证到二进制文件加活 inode 哈希);把网络本身藏掉(不是藏资源藏拓扑,是让不可信侧根本没有网络);策略变更过形式化验证(prover 用 SMT 求值,建模不了就返回 unsupported——连自己要执行的新规则都不轻信)。
诚实的代价也要说:L7 检查依赖 TLS 终结,端到端加密在这条路上被牺牲了——但 BeyondCorp 的访问代理做的是同一件事,这是零信任的一贯代价而非偏离;audit 档的数据不能用于宣称阻断;九万两千行的执法代码本身就是复杂度成本——v0.1.5 还带着两个我提交的 issue(#4909、#4910)。
2021 年翻译那篇参考架构的时候,零信任还是“如何让员工安全地远程访问公司系统”的故事。2026 年,同一套原则已经用来管住一个会自己读代码、跑测试、调 API 的软件进程——被保护的对象从人变成了 Agent,而 NIST 那份清单,居然还是逐条都能对上。
零信任的下一站是进程。再下一站在哪里,取决于 Agent 会走到哪里——但无论它走到哪里,supervisor 的姿态已经写好了:默认不信,逐次验证,证明不了就不放行。


