作者的目标是通过编写一个小程序来探索容器隔离,该程序仅使用最小的一组 Linux 内核机制在隔离环境中运行一个 shell。该程序 contained.c 使用带有命名空间标志的 clone 来创建独立的 mount、PID、IPC、network 和 UTS 命名空间,并可选地创建用于 UID/GID 映射的用户命名空间。在创建子进程后,代码会删除大多数能力,安装 seccomp 过滤器以仅允许少量系统调用,并通过 cgroups(内存、CPU、pids、块 I/O)以及较旧的 setrlimit 接口限制资源。它还会准备一个新的 mount 命名空间,绑定一个临时目录,并将根 pivoted 到该目录,然后再执行请求的命令。
该机制依赖于几种互补的 Linux 特性:命名空间隔离内核对象,如文件系统和进程表;能力将 root 特权细分,使程序能够丢弃危险的特权;seccomp 过滤器限制子进程可以调用的系统调用;cgroups 和 setrlimit 限制资源使用,如内存和 CPU;mount 命名空间结合 pivot_root 改变子进程对根文件系统的视图。作者指出,该程序是故意最小化的,并不旨在生成一个安全沙盒,而是演示哪些权限对不可信代码来说是绝对不安全的。
作者警告说,这个练习不是一个安全边界;虽然用户命名空间对于映射 ID 很有用,但在主流内核中默认是关闭的,并且历史上一直是特权提升漏洞的来源。最终程序大约有 570 行,略高于最初的 500 行目标,说明容器基本上只是围绕现有内核特性的薄包装,而不是复杂的新抽象。这提醒我们,真正的容器运行时在这些基本原语之上额外叠加了安全机制。
