视野
namespaces 让进程看到各自的系统视图
命名空间(namespaces)给一组进程单独的资源视图。同一程序可以在容器里叫 PID 1,在宿主机上又有另一个进程编号;宿主机仍能管理它。这解释了第 6 章的容器为什么有自己的进程列表、目录和网络。
| 类型 | 隔开什么 | 你会观察到什么 |
|---|---|---|
pid | 进程编号与可见进程范围 | 容器主进程在内部是 PID 1 |
mnt(mount,挂载) | 挂载点的视图 | 容器与宿主机的目录树可以不同 |
net(network,网络) | 网卡、路由、端口等网络资源 | 各自的 localhost;可用端口映射连通 |
uts(历史名称 Unix Time-sharing System) | 主机名与 NIS 域名(传统网络信息服务的域标识) | 容器可以叫 lab-a |
ipc(interprocess communication,进程间通信) | System V IPC 对象、POSIX 消息队列 | 这些通信对象有各自范围;并非所有通信都被隔断 |
user(用户) | 用户与组编号的映射、特权范围 | 内部 UID 0 可以映射为外部普通用户编号 |
独立视图不等于复制了所有数据。mnt 隔开挂载布局,但绑定挂载仍能让两边访问同一份文件。普通 rootful Docker(守护进程以 root 运行)也不会自动开启 userns-remap 用户映射。
本页实验准备:在已安装 Docker Engine 的 Ubuntu 26.04 LTS 虚拟机上运行下列命令,建议在虚拟机或容器里试。使用已有 Docker 权限;首次运行会下载镜像,--rm 在退出后清理实验容器。当前编写环境无 Docker,Ubuntu 容器实验未实测;所有终端只列命令,观察结果按文档说明。
观察 Shell 的编号、主机名和 PID 1 的程序名:这个命令没有加 --init,bash 就是容器主进程。/proc 是内核提供的进程信息接口,self 指读取它的程序自身;ns 链接标识所在命名空间,uid_map 显示用户编号映射。可在宿主机运行同样的 readlink 命令对比标识,不必记住编号。
把观察结果放回对应的视图边界。
- 容器主进程叫 PID 1
- 容器主机名是 lab-a
- 同一路径挂载了另一份目录
- 两个容器各有 localhost
“像独立机器”是多种视图拼起来的效果。改主机名不会自动改网络,隔离进程编号也不会自动限制内存。
深入一点:为什么不直接用 unshare?
unshare 可以为程序创建部分新命名空间,适合拆开实验,但它不会自动组成完整容器。Ubuntu 还可能通过 AppArmor 限制非特权用户创建 user namespace;云端会话也可能已有调用或权限限制。创建失败说明需要检查当前环境边界,无需为实验关闭策略。Linux 还有 cgroup、time 命名空间,本页聚焦上面的六种。
预算
cgroups 为一组进程设置资源预算
控制组(control groups,cgroups)把进程分组,统计并控制资源使用。namespaces 决定视野,cgroups 决定用量;容器拥有独立主机名,并不意味着自动有 CPU、内存和任务数上限。
这里按 cgroup v2 的文件接口观察:memory.max 是字节数上限,cpu.max 是每个调度周期可用的配额与周期,pids.max 是任务数上限(线程也计入)。--cpus 0.5 表示约半个 CPU 的总时间预算,不是锁定某一个核心。若文件不存在,先检查 Docker 宿主机的 cgroup 版本与控制器支持。
到上限时的行为也不同:CPU 通常被节流、运行变慢;内存压力下可能触发组内 OOM(内存不足)终止;任务数到顶后,新建任务可能失败。本实验只读取限额,不主动耗尽资源。
同一组里,进程 A 和 B 各使用 128 MiB,合计多少?
合计 MiB;若组上限为 256 MiB,就没有给第三个进程单独预留一份额度。
128 + 128 = 256。这里只做简化记账练习;实际控制组还会统计其他内存用量,不能据此预测某次分配何时失败。
两个构建任务需要各自的 localhost,还要避免其中一个占满内存。需要哪两种机制配合?
网络视图没有给内存记账或限额。
主机名属于 uts,它不分隔 localhost,也不限制内存。
net namespace 提供各自的网络视图,cgroups 为每组进程设置内存预算。并行任务的环境边界和资源边界需要分别设置。
特权
capabilities 把 root 的特权拆成小份
能力(capabilities)把传统 root 的管理特权拆成可单独保留或移除的单元。例如 CAP_CHOWN 允许任意更改文件的用户与组归属。程序可以保留需要的能力,而不获得整套管理权限;普通文件权限检查仍然有作用。
id -u 显示用户编号。CapEff 是当前有效能力集合,CapBnd 是能力边界集合,约束经程序执行可获得的能力。它们以十六进制位图显示,全部为零表示集合为空;你只需比较两次结果,不必手算每一位。字段定义见 proc_pid_status(5)。
移除全部 capabilities 后,程序仍显示 UID 0,能否据此认定它仍有任意改属主的特权?
不能。用户编号没有变,能力集合却可以变。要执行某个操作,还要满足相关能力、命名空间范围、文件权限和策略要求;“去掉能力”也不意味着普通读写都不能做。
深入一点:capabilities 和 user namespace 怎么配合?
user namespace 可以把内部 root 映射到宿主机普通用户编号,并限定能力作用的范围。capabilities 决定有哪些特权,user namespace 决定这些特权在哪个范围内有效。用户映射与 Docker rootless 模式是相关但不同的配置,详见Docker 用户映射文档。
调用
seccomp 过滤程序向内核发出的系统调用
安全计算模式(secure computing,seccomp)能按系统调用编号及部分参数过滤请求。它约束“允许请求哪种内核服务”;capabilities 则约束“请求所需的特权有没有”。即使有某项能力,调用也仍可能被 seccomp 拒绝。
- 程序用
openat请求打开文件。先猜:调用通过过滤,就一定能读到文件吗? - 这份示例策略允许
openat,过滤器放行。 - 仍需检查文件权限与路径策略,因此允许调用不保证能打开目标文件。
- 现在换成用
mount请求挂载文件系统。先猜:即使进程有管理能力,过滤器还能拒绝吗? - 这份示例策略拒绝
mount;请求在这里被拦下。 - 有能力也不能绕过调用过滤。这里只演示两类请求的差别,不代表固定检查顺序或 Docker 默认规则。
Docker 通常使用默认 seccomp 过滤策略,实际状态以这里的字段为准:Seccomp 为 0 表示未启用,1 为严格模式,2 为过滤模式。Seccomp_filters 是附加过滤器数量(内核提供该字段时);它们都不列出具体规则。seccomp 的普通过滤器不能解引用参数指针,所以不能靠它按完整文件路径决定读哪个文件。
如果你读到 Seccomp: 2,可以确认什么?
该进程处在 模式;不能据此确认 mount 是否被允许。
2 表示过滤模式。不同过滤器可以有不同规则,同一个状态值不能证明两次运行具有相同的策略。
路径
AppArmor 为程序的文件访问再加一道规则
AppArmor 是 Linux 内核的访问控制模块,用配置文件(profile,策略档案)约束程序的访问。它可以按路径和读、写、执行等操作规定文件权限,在传统用户权限之外再检查一次;也支持能力、网络等规则,本页聚焦文件。
在启用 AppArmor 的 Ubuntu Docker 宿主机上,Docker 通常给容器应用 docker-default 策略。查看当前策略标签及模式:enforce 会执行限制;complain 用于记录违规而不执行这些策略限制。unconfined 表示没有被该 AppArmor 策略约束,其他隔离仍可能存在;若接口无法读取,就不能据此判断策略状态。这里观察标签,不修改宿主机策略。
同一个文件已经挂载进容器,普通权限也允许读。为什么程序仍可能读不到?请用“路径策略”解释。
挂载让路径出现在进程视图里,传统权限提供一项许可;AppArmor 仍可能按当前 profile 拒绝该路径的读取。要区分“看得到”“普通权限允许”和“策略允许”。
- 提到挂载改变可见性
- 提到 AppArmor 是额外的路径与操作检查
- 没有把单次权限错误直接判定为 AppArmor 拒绝
深入一点:Permission denied 能定位是哪一层吗?
不能。只看报错,无法区分传统文件权限、能力缺失或 AppArmor 等策略。先查看挂载与用户身份,再核对能力、profile、策略和相关日志;seccomp 也可配置返回权限类错误。把观察结果交给 AI 协作者时,附上启动参数与宿主机环境,能更准确地一起定位边界。
回到环境
容器的边界取决于实际启用的配置
让两个 agent 并行处理项目时,可以分别提供工作目录、进程和网络视图,再设资源预算与所需权限。共享源码、服务和凭据属于需要明确连接的部分:挂载会把数据接进来,端口会把网络接出去,镜像本身并没有决定全部运行边界。
容器共享 Linux 内核,适合用较小额外开销组织和复现环境;需要独立客体内核时,回到虚拟机。在 Windows 或 macOS 的 Docker Desktop 中,Linux 容器通常共享其 Linux 虚拟机的内核。下一页看进程级沙箱与 bubblewrap如何组合这些原语,为工具搭出需要的运行环境。
把宿主机目录可写地挂载到容器里后,能否凭“有独立 mnt namespace”断定修改不会影响宿主机文件?
mnt 隔离挂载布局,没有为绑定挂载复制数据。
2 只表示过滤模式,不能说明某条路径是否可写。
绑定挂载有意连接两边的数据。是否能写,还受挂载只读设置、普通权限、能力及 AppArmor 等限制。理解每层管什么,才能判断实际边界。
- 视野:namespaces 隔开资源视图,容器仍由宿主机内核运行。
- 用量:cgroups 给一组进程记账并限额,独立视野不会自动产生限额。
- 动作:capabilities 拆特权,seccomp 过滤调用,AppArmor 约束路径与操作;一个许可不能替代其他检查。
- 证据:看启动配置与进程状态,单个 PID、UID 或 Seccomp 字段都不足以说明全部隔离。
本页机制与观察方法的来源
- Linux man-pages:namespaces(7):命名空间类型与 /proc 接口;Docker:user namespace 映射:内部与外部 UID 的关系。
- Linux 内核:Control Group v2:层级、cpu.max、memory.max、pids.max;Docker:资源限制:限额参数与行为。
- Linux man-pages:capabilities(7):特权拆分与 CAP_CHOWN;proc_pid_status(5):Cap 与 Seccomp 字段。
- Linux 内核:Seccomp BPF:过滤范围与局限;Docker:seccomp:默认策略。
- Ubuntu Server:AppArmor:默认加载、profile 与两种模式;Ubuntu:AppArmor 路径模型与 user namespace 限制;Docker:AppArmor:docker-default 与策略标签观察。
- Docker:安全机制总览;Docker Desktop:Linux 虚拟机;docker run 官方参数参考:本页命令依据。软件行为核对日期:2026-11-06。