Featured image of post Replit 把沙箱引擎从容器换成 microVM:自研最小内核没有蓝牙没有 USB,6211 条内核 CVE 躲掉 96.1%

Replit 把沙箱引擎从容器换成 microVM:自研最小内核没有蓝牙没有 USB,6211 条内核 CVE 躲掉 96.1%

Replit 在 11 月 4 日放出了沙箱引擎重写的第一篇。这件事的量级不小。他们把跑了近十年的沙箱底座从共享内核的 Docker 容器,换成每个沙箱一台自己的 microVM,各自带一个 Linux 内核,跑在 Cloud Hypervisor 和 KVM 上,然后用几个月把数亿个项目迁过去,没有数据丢失。官方说新引擎从 2025 年 11 月动手,2026 年 2 月起内部已经在用,之后一直在加固和铺开。

先把这篇的边界说清楚,否则它会变成站内另外两篇的重复。10 月 27 日那篇拆 Anthropic srt 的稿子,标题级结论就是「密钥防线其实在出口代理」;11 月 4 日那篇拆 Google Agent Compute Engine 的稿子,开篇就明说「凭据不进沙箱」这个结论已经不新了,于是它转去讲身份拆成四种、内存压成共享物理页、闲置做成零成本。这两条路都被人走过了。

所以这篇不讲凭据,一个字的凭据都不讲。这篇只算一笔账:一个砍到极小的内核,到底替 Replit 挡掉了多少 CVE,以及那个漂亮的 96.1% 里面,有多少其实跟「内核小」没什么关系。

这堵墙为什么现在才值钱

Replit 在开篇列了几件同行事件,那份清单本身就是它做这次重写的理由。OpenAI 的 Hugging Face 事件和 Anthropic 的评估事件都说明同一件事:自主 agent 能摸到并伤害预定范围之外的真实系统。Replit 补了一句很关键的限定——那些评估里模型的防护是被削弱的,但结论依然成立,因为传统安全边界在 agent 时代本来就不够了。

行业侧的动作也对得上。Replit 给了一个数字:2025 年以来宣布的 agent 基础设施创业融资里,Daytona、E2B、Runloop、Blaxel 四家加起来是 5930 万美元。它同时点了 NIST 的 agent 标准动作和国会的关注,把隔离、身份、监控推上了议程。开源一侧,它提到 DeepSeek 的 DSec、Kubernetes Agent Sandbox、Google 的 AX 和 Agent Substrate。

这些名字排在一起说明一件事。「跑别人写的代码」这个需求存在了几十年,PC 时代的游戏厂商、云时代的共享主机、之后的容器平台都在做,客户一直是人写的代码。现在客户换成了 agent 写的代码。人写的代码上线之前有人看过,agent 写的代码没有,而它执行的动作数量级更大、节奏更快。内核这一层的账,过去可以拖,现在拖不动了。

旧的那道墙为什么不够

Replit 自己列了过去一年里九条能导致本地提权(LPE)的内核漏洞:Copy Fail、Dirty Frag(xfrm-ESP)、Dirty Frag(RxRPC)、Fragnesia、DirtyClone、一个 epoll 竞态提权、Bad Epoll、GhostLock、FUSE 文件系统驱动的一个缺陷。它顺带说了一句值得记的话,这些 bug 很多在内核里躺了很多年,是 AI 把它们的发现速度提了上去。

旧架构的麻烦在于「共享」。容器是普通的 Linux 进程,被内核用 namespace 和 seccomp 圈起来,但它们和主机、和同一台机器上的其他租户共用同一个内核。一旦这个内核里有能被触达的漏洞,攻击者从容器里出来就同时摸到了别人租户的工作负载、凭据和网络端点。

这里要抄一句 Replit 的原话,因为大量二手转述把它的意思放大了:

Not every kernel CVE is exploitable in every configuration, but sharing the kernel makes a successful escape much more consequential.

不是每条内核 CVE 在每种配置下都能利用,但共用内核会让一次成功的逃逸后果严重得多。这句话很克制,也很准确。换成 microVM 之后,攻破沙箱里那个 guest 内核,本身并不等于攻破主机内核,中间还隔着虚拟化边界。

还有一个容易被忽略的附加收益。Docker 这类工具需要一些权限,在共享内核的运行时里是不敢给的。放进 microVM 之后,这些权限最多只到 guest 内核,所以用户现在可以在沙箱里跑 Docker。墙砌高了,产品能力反而变宽了。

一个沙箱现在长什么样

现在的一个 Replit 沙箱,是一台 microVM,里面跑着几个很小的 runc 容器。除了用户的开发环境,旁边还有跑 PostgreSQL 的、做 git 备份的、以及一个安全请求代理。

Replit 明确说过考虑过给每个组件单独开一台 microVM,最后否掉了。理由是额外开销不值,而且并不会改变租户隔离模型,因为那几个容器本来就属于同一个客户。这句话背后的原则值得单独拿出来:把贵的墙砌在信任边界上。租户之间砌硬墙,同一个租户内部用便宜的栅栏就够了。

但便宜不等于随便。namespaces、seccomp、设备访问、capabilities 都是按角色配的,用户进程不会继承挨着它跑的那个监控 agent 的权限。Docker 在 guest 内拿到它需要的额外权限,这些权限永远到不了物理主机。

还有一条伏笔要记住,后文会用上:VMM 本身以无特权身份运行,没有任何 Linux capability。

控制面也换了个走法。microVM 里有个极小的 init 进程负责起停和监视容器,主机通过 gRPC 走 vsock 跟它通信。vsock 是给主机到 guest 的通道,不需要 IP 网络。这意味着控制通道不依赖 guest 的网络设置,用户在里面怎么改网络也切不断控制面。

内核是怎么砍的

guest 内核的起点是内核自己的 tinyconfig 目标,也就是「能编出来的最小内核」。发行版那套配置在这里被整个丢掉了,只往上加一台 microVM 真正需要的东西。

被砍掉的包括蓝牙、无线、USB 和显示驱动。需要的驱动全部编进内核,不留模块,guest 初始化的时候直接关掉模块加载。Replit 在这儿开了个玩笑,说你的 coding agent 肯定不需要一个任天堂 Switch 手柄。

这个思路本身不新,内核社区推了很多年,但把它和「CVE 数量」挂钩算一遍账的人不多。代码没编进内核,就不可能被那段代码的漏洞打到。Replit 于是拿这条思路去核了一遍所有已公开的内核 CVE,这就是下一节那张表。

补一句运维侧的:他们每天自动重建内核,而且这套更新流程是用建在 Replit 上的自主 agent 自己跑的。这一点在下一节里份量比看起来重。

96.1% 是怎么算出来的,以及它没说的那一半

Replit 核的是 2026 年 6 月 1 日到 11 月 4 日之间发布的 6211 条 Linux CNA CVE,数据来自 CVEProject 和 kernel.org。结论是躲掉了 5969 条,96.1%。它给的四项拆解是:4375 条(70.4%)在披露之前修复就已经进了他们的内核;676 条(10.9%)被配置排除掉了,也就是那段代码根本没编进去;剩下 918 条在披露之后及时修掉(这一项是我用 5969 减 4375 再减 676 得出的,原文没直接给);还有 242 条不在这 5969 之内,原文没说它们后来怎么了。

其中至少 15 条,是 Linux CNA 或 Red Hat 明确写成「会促成、或可能促成 LPE」的。请注意这个 15:它指的是 LPE 类 CVE 的条数,不是内核的个数。Replit 只有一套最小 guest 内核配置,从 tinyconfig 长出来,不存在「十五个内核」这回事。

这张表最有信息量的地方藏在一个反直觉的问题里:为什么五个月能有 6211 条内核 CVE?答案是内核 CVE 团队自己的规矩。他们在文档里说,内核里几乎任何 bug 都可能可被利用,而且修的时候往往还说不清,所以他们「overly cautious」,只要认定是个 bug 修复就给一个 CVE 号。所以这个数字大,是因为它把普通 bug 修复全算了进去,不代表五个月里出了六千个紧急漏洞。

第二个反直觉的问题是,为什么 70.4% 能在披露之前就修好。同一份文档给了答案。CVE 号是自动分配的,而且只在修复已经进稳定树之后才分配。也就是说,对绝大多数内核 CVE 来说,修复先于编号存在。一个紧跟 stable 分支的团队,默认就会先拿到这些修复。

两条合起来,结论就变了:这张表里靠「砍内核」拿到的只有 10.9%,靠「不停跟上上游」拿到的是 70.4%,两个杠杆出的力差了一个数量级。

所以那种「最小内核躲掉 96.1%」的说法,把因果说反了一半。真正每天在干活的是那条永不停止的重建流水线,那颗小内核只是其中一环。一个反例很直接:一颗砍得极小但从不更新的内核,照样是负债,只是它需要打补丁的面积小了一点。

还有一处值得单独说。Replit 举了一个例子证明「跟上上游」是真在跑。CVE-2026-98432,就是 Anthropic 演示过能让远程攻击者拿到 root 的那个 Linux TCP 缺陷,Replit 在 10 月 12 日就把修好的内核合了进去,比这条 CVE 公开还早,比 Anthropic 描述它的利用方式早了两周多。这条比 96.1% 更有说服力,因为它指向的是一个具体的时间差,而不是一个百分比。

至于那 242 条没交代的 CVE,我不猜。但「Replit 没说它们后来怎么了」这件事本身就是信息:96.1% 是一个经过挑选的分母,不是什么都能往里塞。

想自己算一遍这笔账,门槛在哪

这套方法可以自己跑一遍,代价主要是耐心,需要凑齐三样东西。

一是 CVE 清单。从 CVEProject 或 kernel.org 按日期区间取 Linux CNA 的编号,得到那个区间内的全部条目。

二是你内核的两个版本信息。构建配置,也就是 .config,它决定哪些代码进了内核;以及内核源码树自己的 git 历史,它决定每条修复是什么时候进的你的树。

三是分类器。对每条 CVE,问两个独立的问题。第一,这个修复涉及的代码路径,在我的配置里存在吗?不存在就属于「配置排除」,这是静态可判的,拿补丁的 diff 路径去比对你的 .config 就能得到。第二,这条修复的提交时间,早于 CVE 的公开时间吗?这属于时间比较,需要对齐两个日期。

难点在第二类。内核 CVE 的编号与实际修复提交不是一一对应,一批修复常常被拆成多个编号,一个编号也可能覆盖多个提交。更要命的是「披露」有多个日期:CVE 分配到号、NVD 正式发布、厂商发通告,三者能差好几天。你用哪一个,结论就会变。所以 Replit 的 96.1% 是某个特定口径下的数,换一套口径你会算出另一个数,这不代表谁错。

如果只想拿一个能长期盯的指标,我建议先只算第二类。把你跟的 stable 分支的相关提交时间,和你订阅的 CVE 源的发布时间对齐,得到「我在公开披露之前拿到了多少修复」这一个数。它比 96.1% 有用,因为它是你自己的,而且能每周重跑。顺带三个自检问题,隔一段时间问一遍就够了:你的内核上次重建是什么时候;模块加载关了吗;这半年我有没有真的把补丁打进去,还是只是把内核砍小了。

microVM 不是终点:PwnEscape 那两周

microVM 给 guest 一个自己的内核,但没有给它一台自己的机器。guest 的包还是要走主机的网络栈,读盘还是要走主机的存储路径,进程还是主机上的 VMM 在跑。这些全都是 guest 能够得着的主机代码。

就在 Replit 这篇发出前后,安全公司 pwn.ai 公布了 PwnEscape,把这个「够得着」的意思演示得很清楚。起点是 Vercel 在 8 月 18 日挂出的 100 万美元沙箱悬赏,挑战同时包括「逃出网络限制」和「逃出虚拟机摸到主机」。pwn.ai 花了三天,在 Linux 网络栈里找到一个原语:guest 控制的校验和元数据,能在内核已经校验过、并且已经据此采取行动之后,改写 IPv4 头。他们自己的说法是,这个漏洞甚至不在 hypervisor 里。上游接受的修复是提交 6785011f8b16,标题就一句话,在完成校验和之前先校验 checksum_start。

Vercel 后来把它称为该项目最重要的一份报告,整个悬赏的最终统计里只有一条 Critical,就是它。顺带说一句,Vercel 的沙箱本身已经超出行业常规水准,也没有任何他们能看出来的配置错误,漏洞源自 Linux 内核,不是 Vercel 自己的问题。

PwnEscape 的第二篇把同一条链打到了 Firecracker 上。用主机内存访问找到 Firecracker 进程,在它从 KVM 返回的那个点上接管一个虚拟 CPU 线程,选定的代码就以 Firecracker 的权限执行。他们的测试环境是 Firecracker v1.16.1,直接以 root 启动,没有挂 jailer,所以接管之后拿到的就是主机上的 root。

而 Firecracker 自己的设计文档说,本来就不该那么跑。文档把第一层隔离放在 KVM 和虚拟化边界上,纵深防御要求 VMM 在进程层面被约束住,靠 seccomp、cgroup、namespace 和降权,由 jailer 来设。pwn.ai 的文章从另一面说同一件事:如果部署里有这些控制,那么攻击者拿下 VMM 之后到底得到什么,需要单独评估。

现在回到 Replit 那句伏笔。VMM 以无特权身份运行,零 capability,这是第三道墙。攻破 VMM 进程不再等于拥有主机,前提是主机侧的内核也补得够快。这也顺便说明,那 242 条没交代的 CVE 为什么值得盯:guest 那边的内核面积可以砍,主机这边的补丁节奏砍不掉。

还有一点要说得精确。Replit 的原话是「攻破 guest 内核本身并不连带攻破主机内核,攻击者仍然要跨越虚拟化边界」,它没有说边界跨不过去。这是描述沙箱该有的诚实方式:每一道墙都让攻击变难,没有一道是最后一句话。

500 毫秒,和那块 AI 写的磁盘后端

冷启一台沙箱要干四件事:起内核、拉镜像、起容器、把 guest 里的 agent 拉起来。Replit 的办法是把其中大部分提前做掉。guest 内核小,启动就快;warm pool 保持一批已经启动好的 VM 待命。

用户来了之后,主机侧从池子里取一台,用 hotplug 把这位用户的盘挂上去,卷的挂载和支持服务的配置并行做。官方给的数字是整套交接大约 500 毫秒,包含挂盘,起一台空 VM 比这快得多。选 Cloud Hypervisor 的理由里,hotplug 和内存快照占了很重的分量,正是因为这个设计需要「先起 VM,等用户来了再挂盘」。

存储这块有一处我觉得整篇里最值得抄的工程判断。为了接上自家的远程块存储服务,他们写了一个 vhost-user virtio-blk 后端,一个独立进程挨着 Cloud Hypervisor 跑,替 guest 处理块 I/O,把远程磁盘包装成一块普通的虚拟块设备。动机写得很实在:以前要在一个主机内核里管理几百个网络块设备,踩过莫名其妙的死锁和节点故障。把磁盘逻辑挪进自己的进程,等于把这摊活从主机内核里拿了出来。

这块后端还是 Replit 最早完全由 AI 写成的组件之一,时间点在 Claude Sonnet 4 时代。它迭代了五到七版,最后收敛到今天的同步与异步混合模型,取舍点是吞吐和延迟的平衡。存储是写时复制的,所以 fork 项目很便宜,新盘先指向已有块,写的时候才分叉。此外还有优雅重连,发布的时候可以排空在飞的 I/O、把盘存下来、重新连上,不用重启 guest。

墙要有,眼睛也要有

隔离墙只能拖慢攻击,还得看见里面在发生什么,所以 Replit 在每个沙箱里都放了一份常驻监控。它用的组合是 eBPF 加 LSM:eBPF 程序观察进程执行、文件访问和网络活动,LSM 提供在内核边界上执行限制的钩子。两者合起来给到两样东西:运行时可见性,以及拦下特定被禁操作的能力。判断滥用和入侵这一层同样是自走的,运行时的遥测喂给自主 agent,由它持续演化已知检测项和禁止模式。Replit 的比喻是:一台永远开着、一认出有害行为就立刻阻断、而且每次发现新东西都会变强的安全摄像机。

部署上有一个细节值得记。每个沙箱配一个安全监控组件,它同时跨在 microVM 的主机侧和 guest 侧,并且受监督,崩溃、被篡改、或者发布新版本时都能自愈重启。为了让这个「常驻」名副其实,他们把持续交付管线压到了一个具体数字:改动合并之后 15 分钟以内,这个组件能在所有生产沙箱上完成更新和热重载。因为工作在内核这层,他们还能在上游修复出来之前,对零日内核漏洞先推临时缓解。

有一个实例能说明这条控制通路的价值。eMudhra 的中间 CA 密钥被攻破那次,证书信任在整个行业里突然变成急事,而沙箱 harness 自动把更新后的 CA 包注入到了所有运行中的沙箱,走的是已有的控制通路,不需要客户去逐个环境改。

网络这块的思路也一致。每个沙箱有自己的网络命名空间和路由表,所以每个沙箱都可以用同样的私有 IP;Linux 的 VRF 把这些重叠的网络隔开,连接跟踪保住路由身份,回包能找回来路。基线防火墙规则挡住云元数据和基础设施网段,也就是 169.254.169.254 这一类经典的 SSRF 目标。

这一节和前面那张 96.1% 的表是同一件事的两半。补丁流水线管的是「已知的洞别进来」,eBPF 加 LSM 管的是「未知的洞进来之后,能不能被看见、能不能被掐断」。只做前者,你会输在披露到修复之间的那个窗口;只做后者,你会一直在救火。

这套设计,别人该抄哪几条

第一条不必抄,因为大部分人不需要。每租户一颗独立内核,前提是你真的在多租户跑互不信任的代码。单租户的内部平台照抄,只会拿到启动开销和维护负担。

第二条值得所有人抄,而且它跟隔离层级无关:任何 guest 能改的规则,都不构成对 guest 的防护。Replit 的出网策略全部在 guest 之外执行,主机侧代理读 TLS 握手里的主机名、不解密应用载荷、过滤 DNS、拒绝白名单之外的去处,虚拟网卡的限速器由 VMM 强制,改 guest 里的网络配置撤不掉它。他们还把带宽限制和「允许去哪些地方」当成两件事分开管,前者管资源滥用,后者管边界。

第三条最便宜也最有效,而且这一节的整个论证都指向它:你多久重建一次内核。如果这篇只留一句能被执行的结论,就是这一句,96.1% 里能靠配置拿到的是 10.9%,其余绝大部分来自紧跟上游。

第四条是一条容易被反向踩的坑。最小内核是有代价的,模块加载被关掉之后,某些东西就是跑不起来。Replit 敢这么做,是因为它控制整个栈,从内核到镜像到 agent 都是自己的。第三方镜像、插件系统、在线评测这类场景照抄,会先撞在兼容性上,而不是先拿到安全收益。


相关阅读:站内 10 月 27 日那篇拆的是 Anthropic 开源的 srt,讲的是「不装容器怎么把 Agent 关住、密钥防线为什么必须在出口代理」;11 月 4 日那篇拆的是 Google Agent Compute Engine,讲的是零凭据之外那三件工程问题,身份拆四种、内存压成共享页、闲置做成零成本。这一篇与前两篇的分工是:那两篇分别回答「凭据放在哪」和「规模怎么扛」,本篇只回答一个问题,把攻击面砍小这件事,到底值多少。

By AI博士 万戈