打开容器的四扇门
一句话总结
容器逃逸通常并非源于内核漏洞,而是来自我们亲手开放的配置。了解四种模式,就能在审查中用 30 秒发现问题。
本课用于防御。每一项都会配对讲解“为什么危险” 和“如何阻止”。
为什么需要它
仅确认容器使用非 root 用户,并不能证明主机边界安全。如果开放 Docker 套接字、主机文件系统、PID 命名空间或过多 capability,容器内的低权限进程也能绕过边界并取得主机权限。因此,除了镜像用户,还必须同时审查运行时连接的资源和内核权限。
门 1 — 挂载 Docker 套接字
volumes:
- /var/run/docker.sock:/var/run/docker.sock
这行常见于 CI 运行器或监控代理。该套接字是 Docker 守护进程的 API,而守护进程以主机 root 身份运行。只要能访问套接字,就能新建一个挂载整个主机根目录的特权容器。也就是说,这一行实际上等同于授予主机 root 权限。
阻止方法——不要把套接字交给容器。如果需要构建容器,使用不依赖守护进程的构建器(kaniko、buildah);如果只为查询信息,则在前面放置只读代理,以白名单限制允许访问的端点。LabHub 使用 kaniko 构建镜像,原因就在这里。
门 2 — --privileged
--privileged 会授予全部 capability、开放设备访问,并实质上禁用 seccomp/AppArmor。在这种状态下,可以把主机磁盘作为 /dev/sda 打开并挂载。
阻止方法——只逐项授予必需的 capability。例如,如果目的只是绑定低位端口,仅需 NET_BIND_SERVICE。在 Kubernetes 中,则应通过 Pod Security Admission 的 baseline 或更严格级别直接拒绝特权 Pod。
门 3 — 挂载主机路径
volumes:
- /:/host # 최악
- /etc:/etc-host # 나쁨
- /var/log:/logs # 상황에 따라
根目录 / 的危险无需多言;即使只有 /etc,也能接触 shadow、sudoers 和 cron 文件。如果可写,就会直接导致主机失陷。
阻止方法——把挂载路径缩小到最小范围,并加上 :ro。用命名卷代替主机路径;在 Kubernetes 中,使用策略引擎完全禁止 hostPath 更为可靠。
门 4 — 共享命名空间
--pid=host 会暴露主机上的所有进程,--net=host 则直接使用主机网络栈。前者通过 /proc/<pid>/root 打开访问其他进程文件系统的路径,后者则可访问只在 localhost 上开放的管理端口。
阻止方法——保持默认隔离。即使监控等场景确有必要,也应限制为只读并只保留最少 capability。
一览
| 配置 | 实际含义 | 替代方案 |
|---|---|---|
挂载 docker.sock |
主机 root | kaniko/buildah、只读代理 |
--privileged |
全部 capability + 设备 | 仅授予所需 cap、PSA baseline |
-v /:/host |
主机文件系统 | 缩小范围 + :ro、命名卷 |
--pid=host |
访问其他进程 | 保持默认隔离 |
以及多层防御
即使配置正确,运行时漏洞仍然存在,因此要叠加多层防线。
- UID——
USER 10001。不以 root 运行,就能封堵大多数路径。 - capability——全部 drop,只 add 必需项。
- NO_NEW_PRIVS——切断通过 setuid 提升权限的途径。
- 只读根目录——
--read-only,只在必要位置使用 tmpfs。 - seccomp——在内核入口阻止危险系统调用。
LabHub 的实验 Pod 同时采用这五层防护。因此,无论在实验内部以 root 身份做什么,都无法触及主机。
实际现场中的情况
- CI 运行器挂载了套接字 → 任何能向仓库提交代码的人都可能控制主机。
- “先用 privileged 试试,成功后再缩减权限” → 缩减权限的提交永远不会到来。
- 日志收集器以 rw 挂载
/var/log→ 可以删除审计日志。
下一项检查要关注什么
接下来的测验将区分 Docker 套接字、主机挂载和 PID 共享分别打开了哪些路径,以及最小 capability、NoNewPrivs、seccomp 各自起什么作用。请以前一实验确认的内核权限值为依据,排除“单一防御措施已经足够”的错误答案。