最直接有效的加固方式是让应用以非root用户运行:在Dockerfile中创建UID≥1001的专用用户,提前chown必要路径,启动后验证进程UID及权限限制。

让 Docker 镜像以非 root 用户运行,不是加一层“防护罩”,而是从构建起点就剥离不必要的权限。核心在于三步闭环:创建专用用户、精准授权路径、运行时验证效果。只要每步不跳过关键细节,就能落地真正具备内容加固特性的镜像。
在 Dockerfile 中显式创建非 root 用户
容器不读取宿主机的 /etc/passwd,必须在构建阶段定义用户。推荐使用固定 UID ≥ 1001(避开常见系统用户范围),且不分配 shell 和 home 目录:
- 用
addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001 -G appgroup创建组与用户 - 避免写
USER appuser:appgroup后再 chown —— 权限变更必须在USER指令之前完成 - 若工具依赖纯数字 UID(如 gosu),可省略用户名:
adduser -S -u 1001 -G appgroup
只对必要路径做所有权和权限调整
COPY 或 ADD 文件后,立即限定访问范围。错误做法是递归 chown 整个 / 或 /usr;正确做法是聚焦运行时真实需要读写的路径:
RUN chown -R 1001:1001 /app /var/log/myapp /tmp- 使用
COPY --chown=1001:1001直接控制文件归属,比后续 chown 更干净 - 多阶段构建中,最终镜像只保留二进制、配置、启动脚本,剔除构建缓存、源码、.git 等无关内容
构建后必须验证实际运行状态
不能只看 Dockerfile 写了 USER 就认为生效。要进容器确认进程身份和权限边界是否真实收紧:
- 启动容器后执行:
docker exec -it ps aux | grep your-app,检查 USER 列是否为1001或appuser - 尝试以 root 身份进入:
docker exec -u 0 -it sh,应被拒绝(除非启用了 user namespace remap) - 切换到目标用户后测试越权操作:
docker exec -u 1001 touch /etc/test,应返回Permission denied
配合运行时策略进一步收窄攻击面
非 root 是基础,不是终点。生产环境建议叠加以下机制:
- 启动时强制指定用户:
docker run --user 1001:1001,覆盖镜像内USER,适合多租户调度 - 禁用默认 capabilities:
--cap-drop=ALL,再按需添加,例如绑定 80 端口只需--cap-add=NET_BIND_SERVICE - 挂载宿主机目录前预处理权限:
chown -R 1001:1001 ./logs,或使用:Z标签适配 SELinux











