什么是 Docker 镜像的安全漏洞扫描(Image Scanning)?在持续集成(CI/CD)流程中如何引入镜像扫描?
在 Docker 和云原生领域,Docker 镜像安全漏洞扫描(Docker Image Scanning) 是保障容器安全的第一道防线。
下面我将为您详细拆解:什么是镜像扫描、它的工作原理,以及如何在 CI/CD 流程中无缝引入它。
一、 什么是 Docker 镜像安全漏洞扫描?
Docker 镜像安全漏洞扫描是指通过自动化工具,对 Docker 镜像的各个层(Layers)进行静态分析,检测其中是否存在已知的安全漏洞(如 CVE - Common Vulnerabilities and Exposures)、配置缺陷或敏感信息泄露(如私钥、Token)。
1. 为什么需要镜像扫描?
Docker 镜像通常基于基础镜像(如 ubuntu, alpine, node)构建,并安装了大量的第三方系统包(RPM, Deb)和应用程序依赖(npm, pip, maven)。
- 漏洞引入: 基础镜像本身可能含有漏洞,或者你安装的第三方库随着时间推移被发现了新漏洞。
- 投毒攻击: 开发者无意中拉取了恶意篡改的公共镜像。
- 敏感信息泄露: 开发者在构建镜像时,不小心将 API 密钥或密码写死在 Dockerfile 中。
2. 镜像扫描器是如何工作的?
- 解析镜像结构: 扫描器解压 Docker 镜像的每一层。
- 提取依赖列表: 收集镜像中安装的操作系统软件包(如
openssl,glibc)和应用层依赖(如log4j,express)。 - 比对漏洞数据库: 将提取的软件版本与官方漏洞数据库(如 NVD、GitHub Advisory Database、各发行版安全公告)进行比对。
- 生成报告: 输出漏洞列表,并按严重程度(Critical, High, Medium, Low)分类,通常还会提供修复建议(如“升级到某版本”)。
3. 常见的镜像扫描工具
- Trivy (极力推荐): Aqua Security 开源,速度极快,支持多种操作系统和应用依赖扫描,易于集成。
- Clair: Quay 团队开发,适合大型注册表集成。
- Grype: Anchore 开发,轻量级,专为 CI/CD 设计。
- Snyk: 商业化工具(有免费额度),与 Docker 官方深度合作(
docker scan底层就是 Snyk)。
二、 在 CI/CD 流程中如何引入镜像扫描?
在 CI/CD(持续集成/持续部署)中引入镜像扫描,核心原则是 “安全左移”(Shift Left)——在代码合并和镜像部署到生产环境之前,就发现并阻断漏洞。
1. 最佳实践流程设计
一个标准的、安全的 CI/CD 管道通常包含以下步骤:
plaintext
[代码提交]
│
▼
[构建 Docker 镜像]
│
▼
[运行镜像安全扫描] ───► (发现 Critical 漏洞?) ───► [是] ──► 阻断构建,发送报警
│ │
[否] ▼
│ [通知开发者修复]
▼
[推送镜像至私有仓库 (Harbor/ECR)]
│
▼
[部署至 Kubernetes/生产环境]
2. 关键配置策略
- 阈值阻断(Fail the Build): 设置规则,例如:如果扫描出 High 或 Critical 级别的漏洞,直接让 CI Pipeline 失败,阻止镜像推送到仓库。
- 白名单机制(CVE Exception): 允许对某些已知、但在当前环境下无法利用或无法修复的漏洞进行忽略(通过配置文件),避免因误报导致业务中断。
三、 实战:在主流 CI/CD 工具中集成扫描(以 Trivy 为例)
示例 1:在 GitHub Actions 中集成
GitHub Actions 是目前最流行的 CI 工具之一,使用 Trivy 官方 Action 非常简单:
yaml
name: Build and Scan Image
on:
push:
branches: [ "main" ]
jobs:
build-and-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
# 1. 构建 Docker 镜像
- name: Build Docker Image
run: |
docker build -t my-app:${{ github.sha }} .
# 2. 运行 Trivy 扫描
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'table'
exit-code: '1' # 如果发现指定级别的漏洞,退出码设为 1(导致 CI 失败)
ignore-unfixed: true # 忽略目前官方还没有修复补丁的漏洞
severity: 'CRITICAL,HIGH' # 只拦截严重和高危漏洞
示例 2:在 GitLab CI 中集成
在 .gitlab-ci.yml 中,可以使用 Docker-in-Docker (DinD) 或直接运行 Trivy 容器:
yaml
stages:
- build
- test
build_image:
stage: build
script:
- docker build -t my-app:$CI_COMMIT_SHA .
- docker save my-app:$CI_COMMIT_SHA -o my-app.tar
artifacts:
paths:
- my-app.tar
scan_image:
stage: test
image:
name: aquasec/trivy:latest
entrypoint: [""]
dependencies:
- build_image
script:
# 加载构建好的镜像
- docker load -i my-app.tar
# 运行扫描:如果发现 CRITICAL 漏洞,则任务失败 (exit code 1)
- trivy image --exit-code 1 --severity CRITICAL my-app:$CI_COMMIT_SHA
示例 3:在 Jenkins Pipeline 中集成
在 Jenkinsfile 中,可以使用 Shell 步骤直接调用安装在 Agent 上的 trivy:
plaintext
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'docker build -t my-app:${BUILD_NUMBER} .'
}
}
stage('Security Scan') {
steps {
// 运行 Trivy 扫描,如果发现 High 或 Critical 漏洞,则中止 Pipeline
sh 'trivy image --exit-code 1 --severity HIGH,CRITICAL my-app:${BUILD_NUMBER}'
}
}
stage('Push') {
steps {
// 只有扫描通过才会执行 Push
sh 'docker push my-app:${BUILD_NUMBER}'
}
}
}
}
四、 总结与进阶建议
- 不要只在 CI 中扫描: 漏洞是动态出现的。今天安全的镜像,下个月可能会爆出新漏洞。因此,镜像仓库(如 Harbor)也应开启“定时自动扫描”。
- 精简基础镜像: 尽量使用
alpine或 Google 的distroless镜像。基础镜像越小,包含的工具越少,漏洞暴露面就越小,扫描速度也越快。 - 多阶段构建(Multi-stage Builds): 在构建阶段使用完整的 SDK,在运行阶段只复制编译好的二进制文件到最小化镜像中,这能过滤掉 90% 的编译期漏洞。