03

让环境持续存在,把工作约束在边界内。

持久 holder 拥有每个工作区的 Linux 命名空间。一次性 runner 进入该边界执行命令和运行时操作,更窄的限制继续约束子进程。

Section 03 · Namespace runtime & isolation

稳定的边界,可支配的工人。

Linux 要求从单线程进程进行名称空间更改调用。 Ephemeral Sandbox 保持其守护进程多线程,并在必须创建或进入工作区边界时将相同的二进制文件重新执行到小型、单一用途的进程体中。

工艺模型

一个二进制,三个运行时角色。

holder 是终生的,而不是工人。 runner是工人,不是终身的。将这些作业分开可以让 命令 来来去去,而工作区视图仍然是 已挂载。

重新执行拓扑

守护进程委托命名空间工作;它不加入工作区。

serve

多线程守护进程

会话登记表 +闸门

执行 登记表 + 观察者

命名空间 FD 所有权

ns-holder
persistent

每个工作区一个。拥有命名空间生命周期和 PID 命名空间 init 子级。

ns-runner
一枪

每个操作一个。读取一个请求,进入选定的命名空间,写入一个结果,然后退出。

serve

Daemon

多线程控制过程。它创建会话、捕获命名空间文件描述符、启动 runner 并跟踪 命令 完成情况。

ns-holder

持久型holder

每个工作区一个长寿命进程。它创建并固定用户、挂载、PID 和可选的网络命名空间,然后暂停。

ns-runner

一发runner

适用于操作的全新单线程进程。它连接所需的命名空间,执行其有效负载,返回一个结果,然后退出。

工作区创建

代币之间真正的握手。

准备就绪是上演的,因此守护进程永远不会暴露半构建的会话。命名空间 FD 为 已捕获,之后覆盖为 已挂载 ns-up,但在孤立的 holder 宣布之前ready.

  1. 01

    生成 holder

    启动双亲死亡信号,取消共享命名空间堆栈,映射一个 uid/gid,并分叉 PID 1。

    ns-holder
  2. 02

    信号命名空间准备就绪

    holder仅在其命名空间句柄存在后才写入ns-up。

    ns-up
  3. 03

    Capture + compose

    该守护进程打开命名空间 FD,选择性地安装 veth 和挂载,通过 runner 覆盖会话。

    /proc/<pid>/ns/*
  4. 04

    完成联网

    隔离会话通过控制管道接收其接口、地址、前缀和网关。

    net-ready …
  5. 05

    承认工作

    准备好后,操作层注册会话,命令 可能会与 准入闸门交叉。

    ready

共享网络

holder 跳过 NEWNETand shares the daemon's network namespace. No veth setup is required.

隔离网络

holder 增加了 NEWNET;守护进程安装主机 veth 的一半,然后发送命名空间内的配置。

runner协议

四种有效载荷,一种运输。

每个 runner 通过一个管道接收 EOF 帧的 JSON 请求,并通过另一个管道返回一个结果。命名空间文件描述符编号在请求中传输;所选的有效负载决定了 runner 进入其中的哪一个。

--shell

命令执行

设置顺序

user → mount → PID → network*

分叉 shell 子进程,应用子进程安全策略,并等待整个进程组。

--mount-overlay

初始挂载

设置顺序

user → mount

在 holder挂载命名空间内创建合并的工作区并使其归该命名空间所有。

--file-op

会话文件操作

设置顺序

user → mount

针对 已挂载工作区执行实时、fd 相对的操作,而不加入其 PID 命名空间。

--remount-overlay

挂载带电开关

设置顺序

user → mount

运行层栈成功压缩后使用的分阶段替换序列。

生成锁故意很简短。 它涵盖了管道创建和进程生成,因此可继承的 FD 在并发启动之间不会泄漏。子进程启动后,它不会序列化操作。

命令执行

在跨越边界之前准备可观察性。

命令 不仅仅是一把叉子。该守护进程创建其转录本和终端管道,保留容量,启动 runner,并在释放会话闸门之前附加完成状态。然后,runner 进入工作区并在执行之前立即应用最终子限制。

  1. 01

    准备

    PTY、成绩单、环境

  2. 02

    预订

    全局槽+会话账本

  3. 03

    发射

    runner + 尽力cgroup

  4. 04

    限制

    setns,上限,seccomp,执行

  5. 05

    Observe

    进程组、结果、清理

终端合同

stdout 和 stderr 共享 PTY 流; stdin 是单独反压的。

竣工合同

观察者拥有进程组状态并一次性解析调用者。

清理合同

删除 准入 令牌会删除账本条目,并可能触发固定的 终结。

Admission + execution

序列化生命周期边缘,而不是 命令运行时。

每个会话闸门保护 Z​​XQ16QXZ、完成、文件操作、重新挂载和 销毁 决策。一旦 命令 连接到执行 登记表,该防护就会被释放,并且 命令 与其他已承认的 命令 并发运行。

1个命令 准入窗口

01

锁闸门

02

账本刀片

03

Prep + launch

04

附上结果

05

发布闸门

256

默认直播命令上限

可通过 命令 执行 登记表 在会话上进行配置和强制执行。

64

命名空间-操作帽

单独的引擎句柄、挂载、重新挂载和live-会话文件runner。

1 /会话

生命周期闸门

序列化正确性边缘;它是在命令等待之前释放的。

跑步时

观察者拥有完成、进程组状态以及唤醒调用者的承诺。尽力而为的 cgroup 放置支持记帐和后来的重新挂载静止。

完成后

删除 准入 令牌将从会话账本中删除 命令。如果账本耗尽,隐式会话可以根据其固定的 终结 策略发布和 销毁。

隔离边界

分层遏制,具有明确的边缘。

命名空间分离定义了工作区边界。然后 shell 子添加一个受限环境、能力减少、no_new_privs,以及执行前的 seccomp 拒绝表。

深度防御·子执行程序的外部边界
  1. 01

    沙箱容器

    外部镜像、设备、cgroup、主机暴露策略。

  2. 02

    工作区命名空间

    用户、挂载、PID、可选网络分离。

  3. 03

    Overlay + masks

    私人可写视图,隐藏运行时存储。

  4. 04

    命令儿童

    最小环境,减少功能,no_new_privs,seccomp。

User namespace

always

具有单个 uid/gid 映射的命名空间范围的功能。

Mount namespace

always

私有传播、覆盖挂载和屏蔽内部路径。

PID namespace

always

具有会话本地 PID 1 的私有进程树。

Network namespace

optional

可选netns和veth;共享模式保留在守护进程 netns 中。

边界,而不是一揽子主张

  • 不会创建每个工作区UTS、IPC、cgroup 或时间命名空间。
  • shell-child seccomp 策略是拒绝表,而不是系统调用允许列表。
  • seccomp 和能力降低适用于 shell 子级,而不是 holder、runner 或 PID 1。
  • 隔离网络将工作区对等体分开;此层不提供一般目的地过滤。
因施工而短暂

进程树是清理协议。

holder 在命名空间设置之前准备其父死亡信号。它的 PID 命名空间 init 子进程武装另一个进程。如果守护进程终止,内核将遵循该链,而无需等待应用程序清理代码。

内核强制杀伤链

daemon

控制进程退出或崩溃

ns-holder

父母死亡信号响起

PID 1 +会话树

命名空间初始化退出;剩余的进程不能比它活得更久

普通销毁

SIGTERM, configurable grace, then SIGKILL.

挂载清理

The holder's mount namespace disappears with it.

下次启动

获取持久的死会话记录和运行目录。