容器共享宿主机内核,权限控制落点在内核层;虚拟机拥有独立内核,权限隔离于Guest OS内。关键差异在于:容器内root即宿主机root(除非启用user namespace映射),而虚拟机需突破Hypervisor才能触达宿主机内核。

要真正看清容器和虚拟机的权限边界,关键不是比谁更“安全”,而是看它们各自在操作系统里“管到哪一层、又不管哪一层”。一张好的隔离架构图,必须标出内核、用户空间、Hypervisor、Docker Engine 和命名空间这些真实存在的软件层——图上少了哪一层,就容易误解权限控制的实际落点。
权限边界的起点:内核是否共享
这是所有差异的源头。虚拟机中,Guest OS 拥有自己完整的内核,所有系统调用(如 open、chmod、setuid)都由该内核处理,与宿主机内核完全无关;而 Docker 容器直接调用宿主机内核,没有中间内核层。这意味着:
- 容器内 UID 0(root)在内核眼里就是宿主机上的 UID 0,除非启用 user namespace 映射
- 容器无法绕过宿主机内核的安全策略(如 SELinux、AppArmor),但也不能规避其限制
- 一个逃逸成功的容器进程,可直达宿主机内核上下文,而虚拟机需先突破 Hypervisor 才能触达宿主机内核
命名空间决定“看到什么”,不决定“能改什么”
Namespace(如 PID、NET、Mount、User)让容器以为自己独占一套资源视图,但它只是“遮眼布”,不是“防火墙”。例如:
- mount namespace 隔离了挂载点,但若挂载的是宿主机 /data 目录,容器内对 /app/data 的写入仍受宿主机该路径的 UID/GID 权限约束
- user namespace 可将容器内 UID 0 映射为宿主机 UID 165536,从而避免直接拥有 root 权限——这是唯一能实质性收紧权限边界的命名空间
- network namespace 创建独立网络栈,但宿主机 iptables 或 nftables 仍可拦截或修改进出流量
权限落地的关键:cgroups 与文件系统权限协同
cgroups 控制资源使用上限(如 CPU 配额、内存限额),但它不管理文件读写权限;真正的访问控制仍依赖 Linux 标准的 UID/GID + rwx 位。常见错配场景:
- 容器以 UID 1001 运行,挂载宿主机 /config,但该目录属主是 UID 1000 且权限为 700 → 容器内进程被拒绝访问
- root 启动容器并挂载 /var/log,容器内应用以非 root 用户写日志 → 文件在宿主机上可能变为 root:root,导致宿主机日志轮转工具无法清理
- 未设置 --read-only=true,容器内恶意进程可直接修改挂载目录下的敏感配置文件(即使它不在自己的 mount namespace 中创建新挂载)
架构图里必须标出的权限断点
一张能用于权限分析的 Docker 架构图,至少应清晰标注以下四个断点位置:
- 宿主机内核入口:所有系统调用最终抵达处,SELinux/AppArmor 策略在此生效
- User Namespace 映射表:明确写出容器内 UID 0 ↔ 宿主机 UID X 的映射关系(默认关闭,需显式启用)
- Volume 挂载交界线:标出 bind mount 或 volume 的宿主机路径,并注明其当前 owner:group 和权限模式
- Docker Daemon 权限代理点:CLI 发起的 docker run 命令由 root 权限的 daemon 执行,它有权绕过部分用户级限制(如绑定特权端口、加载内核模块)


















