核心是全程不依赖root权限并剥离内核能力:构建阶段用alpine镜像编译,运行阶段切distroless/scratch镜像+显式uid 1001用户+--chown复制+user指令切换;运行时强制--read-only、--cap-drop=all、securitycontext限制,并清除suid文件。

核心是让容器从构建到启动全程不依赖 root 权限,同时剥离不必要的内核能力。这不是加一道防护,而是重构执行上下文。
用多阶段构建隔离构建与运行环境
构建阶段可保留 root 权限用于编译、安装依赖;运行阶段必须切换为最小化镜像+非 root 用户,且不带 shell 和包管理器。
- 第一阶段用
node:20-alpine或golang:1.23-alpine完成构建 - 第二阶段优先选
distroless或scratch,避免引入攻击面 - 若需调试能力(如 curl、sh),改用
alpine:3.18并立即移除 su-exec 等提权工具
显式创建低权限用户并统一 UID/GID
不要依赖默认用户或用户名,直接指定数字 UID(推荐 1001),确保与宿主机挂载卷权限兼容,也避免不同系统同名用户 UID 冲突。
- 在运行阶段镜像中执行:
RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 -G appgroup - 复制应用文件时强制归属:
COPY --chown=appuser:appgroup . /app - 切换用户必须放在所有
COPY和RUN之后、CMD之前:USER 1001:1001
运行时主动降权:禁用能力 + 只读根 + 非特权模式
Dockerfile 只控制镜像内用户,真正生效靠运行时参数。生产部署必须配合以下约束:
- 启动时加
--read-only,禁止写入根文件系统(日志等需挂载/tmp或/var/log卷) - 用
--cap-drop=ALL清空默认能力,再按需添加(如仅需绑定端口则加--cap-add=NET_BIND_SERVICE) - 绝对禁用
--privileged、--pid=host、--ipc=host等破坏隔离的选项 - Kubernetes 中通过
securityContext强制:runAsNonRoot: true、readOnlyRootFilesystem: true
清除潜在提权入口点
即使以非 root 运行,残留的 SUID/SGID 文件或危险二进制仍可能被利用。
- 在运行阶段镜像中执行:
RUN find /usr/bin /bin -perm -4000 -o -perm -2000 -exec rm -f {} \; - 删除
ping、ss、ip、mount等非业务必需的 setuid 工具 - 检查最终镜像层:
docker run --rm <image> ls -l /usr/bin/ping</image>,确认无 s 位











