本节摘要:纵深防御的最后一条纪律是承认外层会破:runc 有过真实逃逸漏洞(CVE-2019-5736、CVE-2024-21626,官方记录),策略有灰区,供应链有漏网。本节不再问「怎么防」,只问「逃逸那天会发生什么」。三块内容:其一,爆炸半径压缩——一次性沙箱让逃逸成果随实例销毁、宿主面收敛到只剩一个工作区挂载、出站流量收窄成一条过白名单代理的可审计通道、任务限时压缩作恶时间窗;其二,凭据隔离——沙箱内零长期凭据,需要外部能力时凭据留在网关侧或以分钟级短命 token 注入,环境变量做白名单;其三,事件响应五步——发现、隔离、取证、根因、恢复加固,每一步都标注它依托的前文构件(熔断器、每会话独立、审计链、回放对照、红队回归)。本节结尾与第 6 章的演练直接衔接。
前四章的思路都是「降低逃逸概率」,本节的思路是「压低逃逸条件下的损失上限」。两个视角并不矛盾,而是互补:概率再低,乘以足够长的时间和足够多的会话,就是必须预案的事件。工程上把这个预案拆成两个问题:逃逸者能碰到什么(爆炸半径),以及事件发生后的既定动作(响应剧本)。先给一张自查表,读者可以现在就对着自己的部署逐行打勾:
| 自查项 | 问题 | 对应手段 |
|---|---|---|
| 实例寿命 | 沙箱是每会话新建,还是长期复用? | 一次性沙箱(本节二) |
| 宿主暴露面 | 除了工作区,还挂了什么进来? | 工作区即世界(本节二) |
| 出站通道 | 需要联网的沙箱怎么出去? | 出站白名单代理(本节二) |
| 凭据位置 | 沙箱内有没有任何长期凭据? | 凭据隔离(本节三) |
| 响应剧本 | 逃逸发生后第一小时做什么? | 事件响应五步(本节四) |
这张表的建议用法:每个季度对着部署现状重填一遍——爆炸半径是随业务生长悄悄变大的,上一季度的答案不作数。
一次性沙箱。 第 2.1 节的 --rm 已经是习惯,这里把它升级为纪律:每个会话(甚至每个任务)新建实例,结束即销毁。逃逸者拿到的是一个寿命以分钟计的环境——植入的持久化后门随实例销毁,横向上没有第二个目标。反面清单也随之清晰:长寿命沙箱、多任务共用一个实例、把沙箱当缓存池用,都会把半径越滚越大。
工作区即世界。 宿主侧只挂载一个工作区目录(2.1 节边界的另一面):逃逸代码即便摸到宿主文件系统,能读写的业务面也只有这一个目录。两个高频违例要当场清掉:挂 Docker Socket(等于交出宿主 root,2.1 节一票否决项)与挂宿主根目录(等于没有沙箱)。
出站白名单代理。 断网是最干净的边界,但真实任务常常需要联网(拉依赖、查文档)。这时不给沙箱直接出站,而是统一走代理,代理上配域名白名单:数据外传通道从「任意目标」收窄成「一条可审计的缝」——谁在什么时候连了什么,代理日志就是答案。第 3.1 节说过,断网沙箱里的 python -m http.server 是哑弹;一旦放开出站,这类命令就要靠白名单代理管住。
时间窗。 任务限时(sandbox_run.py 的 TIMEOUT 已有)加上凭据短寿命(下一节),把「逃逸后能干多久」压到分钟级。四个手段合起来的逻辑:半径小的世界 + 短命的实例 + 单通道的外联 + 有限的时间,逃逸的期望收益被系统性压低。
逃逸者真正想要的往往不是沙箱里的文件,而是凭据——有了云凭据或代码仓库令牌,攻击就从沙箱内一跃到沙箱外。三条原则:
env_allowlist 在这里兑现:进沙箱的环境变量逐项白名单,白名单外一律剥离。⚠️ 环境变量是最常见的意外泄露面:历史包袱里一句
-e就把高价值凭据带进了每一个容器。做法是反向的——默认全剥离,按任务逐项申请;这与第 3.1 节「自下而上长白名单」是同一个哲学:默认拒绝,按需放开。
(文字流程图)事件响应五步与依托构件 发现 ──▶ 隔离 ──▶ 取证 ──▶ 根因 ──▶ 恢复加固 │ │ │ │ │ 熔断告警 杀实例、 验审计链、 重放对照、 补策略/补锁定/ 审计统计 冻工作区 旁路导出 入口定位 红队回归后恢复 人工报告 吊短命token 按 run 重建 (依赖/Skill/ (第 6.1 节) (5.1) (每会话独立) 时间线(3.2) 注入三分查)
| 步骤 | 做什么 | 关键纪律 |
|---|---|---|
| 一 · 发现 | 告警触发:熔断动作、审计指标异常(超时率、审批通过率跳变)、人工报告 | 宁可误报进流程,不可漏报靠运气 |
| 二 · 隔离 | 终止涉事实例、冻结其工作区快照、吊销该会话注入的短命 token | 每会话独立让「隔离」不伤及邻居(第 2.2 节平台级形态下尤其如此) |
| 三 · 取证 | 先 verify() 审计链,再旁路导出副本;按 run_id 重建时间线 |
先保护证据再动现场;哈希链断了本身就是重要线索 |
| 四 · 根因 | 定位入口:供应链查锁定文件与 diff(第 4 章)、执行侧查策略与网关日志、注入侧对照不可信输入源 | 三条线并行查,不要先验排除任何一条 |
| 五 · 恢复加固 | 补策略、补锁定、补测试;红队回归全绿后才恢复服务 | 没有针对根因的加固,恢复等于重蹈覆辙 |
五步里最容易被跳过的是第三步(急着修复,毁了现场)与第五步(修完就上线,不补回归)。把这两步写进剧本并用演练检验——第 6.1 节的自测用例会把「逃逸演练 + 响应计时」纳入回归范围,第 6.2 节的落地路线则把「事件响应演练通过」作为平台段门禁。
💡 剧本的形态建议:一页纸,五个标题,每个标题下三行以内的动作与负责人占位——超过一页的剧本在真实事件里不会被翻开。它应该像消防疏散图而不是消防规范手册。
剧本不演练就是纸。最小演练设计(一个下午可完成,全部在自有授权环境):
| 演练项 | 做法 | 计时目标(示意) |
|---|---|---|
| 触发发现 | 人为制造信号:连续发起三条会被 deny 的外联命令,看熔断是否动作、告警是否到达 | 从第三次尝试到告警送达不超过 5 分钟 |
| 隔离执行 | 按剧本杀掉涉事 run 的实例、冻结工作区、吊销测试 token | 不超过 10 分钟,全程不动邻居会话 |
| 取证走查 | 对审计链跑 verify(),按 run_id 重建时间线并导出旁路副本 | 不超过 15 分钟,副本哈希留档 |
| 根因模拟 | 给一个 planted(预置)的投毒样本,按三条线定位入口 | 不超过 30 分钟,结论可被复核 |
四项全过,剧本才算「活过一次」;任何一项超时或卡壳,回到对应章节补构件——发现卡壳补 5.1,隔离卡壳补每会话独立,取证卡壳补 3.2,根因卡壳补第 4 章锁定链。演练频率的建议(示意):团队段每季度一次,平台段每月一次;每次真实事件后追加一次复盘演练。
防线、进料、信号、止损至此全部就位,还剩最后一个问题:它们真的挡得住吗?答案不在配置里,在实验里。第 6 章用四类红队用例攻击自己的沙箱,把有效性变成回归资产,并给出从个人到平台的三段落地路线,为全书收官。