核心思路是让容器内进程从源头就拿不到高权限,而非等提权时拦截;需在构建阶段用非root用户锁定身份、运行阶段限制能力与挂载、加固层启用命名空间与安全模块,并持续验证生效情况。

核心思路是让容器内进程从源头就拿不到高权限,而不是等它试图提权时再拦截。越权提权往往依赖 root 身份、冗余能力或文件系统漏洞,所以防范重点在构建和运行两个阶段的主动降权。
构建阶段:用非 root 用户锁定执行身份
这是最基础也最关键的一步。Dockerfile 中必须显式创建并切换到非特权用户,不能依赖默认 root:
- 用 RUN adduser -D appuser 或 useradd -r -u 1001 appuser 创建 UID 固定的普通用户(避免动态 UID 引起映射混乱)
- 所有应用文件用 COPY --chown=appuser:appuser 复制,确保属主一致
- 紧接其后写 USER appuser,且该指令必须出现在 COPY 之后、CMD 之前
- 不要在 USER 指令后执行需要 root 权限的操作(比如修改 /etc/hosts 或绑定 80 端口),否则构建会失败——这正是你想要的效果
运行阶段:限制能力与挂载行为
即使镜像里用了非 root 用户,运行时仍可能被绕过。需配合运行参数进一步收紧:
- 启动时用 --user 1001:1001 强制指定 UID/GID,覆盖镜像中 USER 指令(适合多租户或统一策略场景)
- 用 --cap-drop=ALL --cap-add=NET_BIND_SERVICE 彻底清空能力集,再按需加回(如只开放端口绑定)
- 敏感目录挂载加 :ro(只读)或 :z(SELinux 标签隔离),禁止挂载 /proc/sys、/sys/fs/cgroup 等危险路径
- 绝对不用 --privileged,也不挂载 /dev 或 /hostfs
加固层:启用命名空间与安全模块
单靠 USER 指令还不够,需结合内核级隔离机制:
- 宿主机开启 user namespace 映射(配置 /etc/subuid 和 /etc/subgid),让容器内 UID 0 实际映射到宿主机高范围 UID(如 100000),彻底脱离真实 root 权限
- 加载 seccomp profile(如官方 default.json),过滤掉 clone、mount、ptrace 等易被用于逃逸的系统调用
- 配合 AppArmor 或 SELinux 策略,限制容器对文件路径、网络协议、IPC 资源的访问范围
持续验证:扫描与监控不可少
配置不是一劳永逸,要定期确认是否生效:
- 构建后检查镜像:运行 docker run --rm 镜像名 id,输出应为 uid=1001(appuser) gid=1001(appuser)
- 运行中检查:进入容器执行 cat /proc/1/status | grep CapEff,确认 capabilities 十六进制值极小(如 0000000000000000)
- 静态扫描:用 trivy config 或 docker scan 检测 Dockerfile 是否含 USER root 或 privileged: true
- 运行时告警:监控容器内是否出现 setuid 二进制文件或异常的 chown/chmod 调用


















