dockerfile 中 run 指令应优先使用 exec 格式、合并安装与清理操作、非 root 用户构建、禁用交互及冗余工具。例如:run ["apt-get", "update"] && run ["apt-get", "install", "-y", "--no-install-recommends", "curl"] && rm -rf /var/lib/apt/lists/*。

RUN 指令是 Dockerfile 中最常被误用、也最容易引入安全风险的指令之一。它直接决定容器内运行时环境的洁净度和攻击面大小,配得不好,轻则镜像臃肿、构建慢,重则埋下提权、漏洞、敏感信息泄露隐患。
明确使用 exec 格式,禁用 shell 解析
避免 Shell 格式(RUN apt-get update && apt-get install -y curl),因为它会隐式调用 /bin/sh -c,可能被注入、依赖不可控 Shell 行为(如不同基础镜像中 sh 实现差异)、且无法精确控制执行上下文。
改用 exec 格式,显式指定解释器和参数:
- RUN ["/bin/sh", "-c", "apt-get update && apt-get install -y curl"]
- 更推荐:直接调用二进制,绕过 shell —— RUN ["apt-get", "update"] 和 RUN ["apt-get", "install", "-y", "curl"](注意:apt-get 不支持链式调用,需拆成多条)
合并命令 + 清理一步到位
每条 RUN 都会新增一层镜像层,残留中间文件(如缓存、临时包、文档)会永久保留在历史层中,即使后续用 rm 删除也无法真正擦除 —— 这是镜像体积膨胀和安全风险的主要来源。
正确做法:在单条 RUN 中完成“安装 → 使用 → 清理”闭环:
- Debian/Ubuntu:RUN apt-get update && apt-get install -y --no-install-recommends curl wget && rm -rf /var/lib/apt/lists/*
- Alpine:RUN apk add --no-cache curl wget(
--no-cache自动跳过索引缓存,不写rm -rf /var/cache/apk/*) - 禁止单独写
RUN rm -rf /tmp/*或RUN apt-get clean—— 它们只会新建一层空操作,毫无意义
非 root 用户执行构建步骤(可选但强推)
某些构建工具(如 Node.js 的 npm、Go 的 go build)无需 root 权限。在构建阶段就切换用户,可降低因命令注入或漏洞导致的提权风险:
- RUN groupadd -g 1001 -r appgroup && useradd -r -u 1001 -g appgroup appuser
- USER appuser(放在后续 RUN 前,确保该用户有权限访问工作目录)
- 注意:apt、apk 等包管理器仍需 root,所以用户切换应在所有依赖安装完成后、应用编译/打包前进行
禁用交互式操作与冗余工具
构建过程不应依赖 stdin、tty 或用户确认,也不应安装调试类工具(vim、bash、netcat、curl 等)到最终镜像中。
- 加
-y或--yes参数避免交互阻塞(如 apt-get install -y) - 生产镜像中只保留运行必需的二进制和库;调试工具统一放在构建阶段(多阶段构建中),不复制进最终镜像
- 若必须用 curl/wget 下载,优先用
COPY或构建时挂载方式替代运行时下载,避免引入网络不确定性及证书信任问题











