容器镜像安全加固,先从一个可运行镜像开始
容器镜像安全加固,先从一个可运行镜像开始
$ trivy image --severity HIGH,CRITICAL web-app:v1.4.2
web-app:v1.4.2 (debian 11.6)
============================
Total: 42 (HIGH: 35, CRITICAL: 7)
┌────────────────┬────────────────┬──────────┬───────────────────┬---------------+───────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │
│├────────────────┼────────────────┼──────────┼───────────────────┼---------------+───────────────────────────────────────────┤
│ libssl1.1 │ CVE-2024-2511 │ CRITICAL │ 1.1.1n-0+deb11u4 │ 1.1.1n-0+deb11u5│ OpenSSL session cache memory leak │
│ bash │ CVE-2024-38428 │ HIGH │ 5.1-2+b3 │ │ Out-of-bounds read in parser │
└────────────────┴────────────────┴──────────┴───────────────────┴---------------+───────────────────────────────────────────┤
上述 Trivy 扫描终端输出,展现了安全合规审计中常见的改进通知。镜像中打包了非必要的操作系统组件,包含未使用的 bash、curl、apt 等工具,同时引入了 High/Critical 级别的安全漏洞。
安全加固不应只把基础镜像从 ubuntu:latest 换成 ubuntu:22.04。是否采用精简镜像、非 root 账号和最小化 Capabilities,要结合应用依赖、运行权限和扫描结果逐项确认。
1. 从 Trivy 扫描报告入手:剥离无用软件包与多阶段构建精简。
多数标准基础镜像(如 python:3.11 或 node:18)默认基于标准的 Debian 或 Ubuntu 构建,包含完整的包管理器、GCC 编译工具链以及数十个系统动态链接库。攻击者若利用 Web 漏洞(例如远程代码执行 RCE)注入指令,即可使用容器内的 curl 或 wget 从外部下载恶意可执行文件。
加固的初始步骤是采用多阶段构建(Multi-stage Build),将“编译构建环境”与“运行时环境”进行完全隔离。在构建阶段使用功能完备的镜像,在最终交付阶段切换为 distroless 或极简 alpine 基础镜像。
改造前后的镜像对比指标:
- 改造前:基于
python:3.11基础镜像,体积为 1.02GB,检测出 68 个高危漏洞。 - 改造后:基于
gcr.io/distroless/python3-debian12镜像,体积降至 52MB,高危漏洞数降为 0。
多阶段构建除了清理非必要软件包外,关键在于移除了容器内的 Shell 执行环境(如 /bin/sh 与 /bin/bash),削弱了反向 Shell(Reverse Shell)攻击的执行条件。
2. 配置 Non-root 用户运行:文件权限与 Capabilities 最小化剥离。
默认配置下,Docker 容器内部进程以 root(UID 0)身份运行。即使 Docker 提供了 Namespace 隔离机制,一旦出现 Linux 内核漏洞或 Docker Daemon 权限缺陷,容器内的 root 用户可能发起越权攻击并影响宿主机安全。
安全加固的关键在于 Dockerfile 中显式创建并指定非特权用户(Non-root User),同时收紧工作目录的文件读写权限。
以下为针对 Python Web 应用的生产加固 Dockerfile 示例:
# ==========================================
# 阶段 1: 依赖构建阶段 (Build Stage)
# ==========================================
FROM python:3.11-slim-bookworm AS builder
WORKDIR /build
# 安装编译依赖,防止某些 C 扩展编译失败
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
libc6-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
# 将 Python 依赖安装至自定义 site-packages 目录
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# ==========================================
# 阶段 2: 最终运行阶段 (Final Stage)
# ==========================================
FROM python:3.11-slim-bookworm AS final
# 1. 创建固定的非 root 用户组和用户 (UID 10001)
RUN groupadd -g 10001 appgroup && \
useradd -u 10001 -g appgroup -s /sbin/nologin -M appuser
WORKDIR /app
# 2. 从 builder 阶段复制编译依赖与源代码
COPY --from=builder /install /usr/local
COPY --chown=appuser:appgroup ./app /app/app
# 3. 严格限制目录读写权限,禁止运行时修改代码目录
RUN chown -R root:root /app && \
chmod -R 755 /app && \
chown -R appuser:appgroup /app/app
# 4. 切换为非 root 用户运行
USER 10001:10001
# 5. 暴露业务端口与健康检查指令
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD ["python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8080/health').read()"]
ENTRYPOINT ["python", "-m", "app.main"]
在该 Dockerfile 方案中,通过 chown -R root:root /app 确保应用进程在运行时无法篡改底层源码文件;通过 USER 10001 将运行身份与 root 权限解除绑定。
3. 编写 Dockerfile 最佳安全模板与自动化校验脚本。
依赖人工审核 Dockerfile 容易遗漏安全细节。工程实践中应当在 CI/CD 流水线中引入自动化审计工具(如 Hadolint),或编写 Go 语言脚本对构建产物开展安全基线校验。
以下为使用 Go 实现的镜像安全基线检查程序,用于验证镜像运行用户与安全配置:
package imageverifier
import (
"context"
"fmt"
"github.com/docker/docker/api/types/image"
"github.com/docker/docker/client"
)
// VerifyImageSecurity Inspect 镜像配置,确保非 root 用户且无高危配置
func VerifyImageSecurity(ctx context.Context, imageName string) error {
if imageName == "" {
return fmt.Errorf("invalid parameter: imageName cannot be empty")
}
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.43"))
if err != nil {
return fmt.Errorf("failed to create docker client: %w", err)
}
defer cli.Close()
inspect, _, err := cli.ImageInspectWithRaw(ctx, imageName)
if err != nil {
return fmt.Errorf("failed to inspect image [%s]: %w", imageName, err)
}
// 1. 校验 USER 字段配置
configUser := inspect.Config.User
if configUser == "" || configUser == "root" || configUser == "0" {
return fmt.Errorf("security violation: image [%s] runs as ROOT (User field is '%s')", imageName, configUser)
}
// 2. 校验健康检查配置
if inspect.Config.Healthcheck != nil && len(inspect.Config.Healthcheck.Test) > 0 && inspect.Config.Healthcheck.Test[0] == "NONE" {
return fmt.Errorf("security violation: image [%s] disabled HEALTHCHECK", imageName)
}
fmt.Printf("[PASS] Image [%s] passed security baseline check. User: %s\n", imageName, configUser)
return nil
}
4. 运行时防护与 Seccomp 配置文件在 Docker 容器中的挂载实战。
在镜像去除了 root 权限后,若在运行容器时赋予过高的 Linux Capabilities,攻击者仍有可能利用未受限的 syscall 系统调用影响宿主机内核。
在 Docker CLI 启动指令或 Kubernetes Deployment 配置中,需显式移除默认 Capabilities,仅按需保留必要的权限(如 NET_BIND_SERVICE):
# Docker 运行时安全加固启动命令示例
docker run -d \
--name web-app-secure \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=64m \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--security-opt no-new-privileges:true \
--security-opt seccomp=/etc/docker/seccomp-default.json \
-p 80:8080 \
web-app:v2.0.0
关键加固参数的作用解析如下:
--read-only:将容器根文件系统挂载为只读模式,阻止恶意脚本的写入。--tmpfs /tmp:挂载内存中的临时文件目录,并加上noexec(禁止执行二进制文件)与nosuid标志。--cap-drop=ALL:剥离所有 Linux Capabilities(如CAP_SYS_ADMIN、CAP_NET_RAW等)。--security-opt no-new-privileges:true:阻止容器内部子进程通过setuid提升权限。
多阶段构建、非特权 UID、只读根目录和 Capabilities 剥离可以减少暴露面;是否满足上线要求仍应以镜像扫描、运行时策略和实际业务验证的结果为准。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)