第 20/60 天
引言
容器镜像里藏着多少已知漏洞?很多团队直到被攻破或过等保审计时才意识到:基础镜像陈旧、依赖带 CVE、无人扫描 已成为供应链安全的头号隐患。上一篇文章我们解决了镜像的「清理与存储」,今天要解决的是镜像的「安全质量门禁」。
Harbor 的核心能力之一就是漏洞扫描(Vulnerability Scanning):它本身并不实现扫描引擎,而是以「扫描器(Scanner)」插件的形态集成 Trivy、Clair 等工具。本文从原理到实战,覆盖:Trivy/Clair 的选择与对比、扫描器的接入、扫描策略(定时/推送即扫)、漏洞等级阈值阻断、API 触发与 CI 联动,以及常见问题排查。读完你可以把「镜像扫描」作为一条强制质量门禁嵌入到 Tekton CI 流水线中。
核心概念
Harbor 扫描架构
Harbor 通过 Scanner Adapter(扫描器适配器) 模式与第三方扫描引擎解耦:Harbor 自身只负责镜像的存储与元数据,扫描器负责拉取镜像并生成漏洞报告,最终报告回传到 Harbor 的数据库与 UI。架构大致如下:
镜像 Push ──> Harbor Core ──> 触发扫描
│
└──> Scanner Adapter (Trivy/Clair)
│ 拉取镜像 Blob、比对漏洞库
▼
漏洞报告 JSON
│
▼
Harbor 数据库 / UI / API
Trivy 与 Clair 对比
| 维度 | Trivy | Clair |
|---|---|---|
| 开发方 | Aqua Security | Red Hat (原 CoreOS) |
| 漏洞库 | 聚合 NVD + 各发行版 CVE + GitHub Security Advisories | 各发行版 CVE(Debian/Ubuntu/RedHat/Alpine 等) |
| 速度 | 快(增量层缓存、静态分析) | 中(依赖 Clair 数据库同步) |
| 准确率 | 高(支持 OS 包 + 编程语言依赖) | 中(偏 OS 包) |
| 部署形态 | 单容器 adapter,支持 Helm 部署 | 需 PostgreSQL + Clair 四件套 |
| 语言依赖扫描 | 支持(npm/pip/gomod 等) | 支持有限 |
| 维护活跃度 | 非常活跃 | 较平稳 |
| 默认推荐 | ✅ 生产首选 | 兼容性替代 |
结论:新部署一律选 Trivy。Clair 适用于历史遗留、或对 Clair 数据源有合规要求的场景。本文以 Trivy 为主线,同时给出 Clair 的接入参数。
关键概念
- 漏洞报告等级:Critical / High / Medium / Low / Unknown,对应 Harbor 扫描报告的严重度。
- CVSS 评分:Common Vulnerability Scoring System,Harbor 报告会展示 CVSS v2/v3 分数。
- 推送即扫:镜像 push 完成后立即触发扫描(推荐生产开启)。
- 定时扫描:按 cron 周期对仓库内镜像重新扫描,捕获新增 CVE。
- 扫描策略(Quota/阻断):可设置「当镜像存在 Critical 漏洞时阻止拉取/复制」的准入规则。
- CVE Allowlist(白名单):对已知可接受的漏洞 ID 进行豁免,避免误伤业务镜像。
- 不可变标签 + 扫描:配合 immutable tag,防止「先扫后改」的绕过。
实战步骤
1. 安装阶段启用扫描器(推荐方式)
使用 Helm 安装 Harbor 时,直接启用 Trivy 内置扫描器。用 values 文件覆盖默认配置:
# values-trivy.yaml 关键片段(配合 helm install harbor 使用)
cat > /tmp/harbor-values.yaml << 'EOF'
expose:
type: clusterIP
externalURL: https://harbor.example.com
# 内置 Trivy 扫描器
trivy:
enabled: true
# 通过环境变量控制漏洞库更新
env:
TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db:2
resources:
requests:
cpu: 200m
memory: 512Mi
# Clair 作为备选(如需要可启用,二者可共存但推荐只开一个)
clair:
enabled: false
# 推送即扫描的全局默认(运行时也可在 UI/API 覆盖)
# 注:默认扫描策略见步骤 3
EOF
helm repo add harbor https://helm.goharbor.io
helm repo update
helm upgrade --install harbor harbor/harbor
-n harbor --create-namespace
-f /tmp/harbor-values.yaml
使用 docker-compose 部署(离线/内网环境)时,只需在
docker-compose.yml中将 Trivy 服务的enabled: false改为true并执行docker-compose up -d,Harbor 会在启动时自动注册内置 Trivy 扫描器。
2. 安装后手动接入第三方扫描器
如果安装时未启用扫描器,或需要接入独立部署的 Trivy/Clair 集群,可以通过 Harbor API 注册扫描器端点:
# 获取 Harbor API Token(robot 或 admin)
TOKEN=$(curl -s -u "admin:ChangeMe123"
-X POST "https://harbor.example.com/api/v2.0/users/login"
-H "Content-Type: application/json"
-d '{}' -o /dev/null -w '')
# 直接用基础认证注册 Trivy 扫描器
curl -k -u "admin:ChangeMe123"
-X POST "https://harbor.example.com/api/v2.0/scanners"
-H "Content-Type: application/json"
-d '{
"name": "trivy-standalone",
"description": "Standalone Trivy scanner",
"url": "http://trivy-adapter:8080",
"auth": "",
"skip_certVerify": false
}'
# 注册 Clair(备用示例)
curl -k -u "admin:ChangeMe123"
-X POST "https://harbor.example.com/api/v2.0/scanners"
-H "Content-Type: application/json"
-d '{
"name": "clair",
"url": "http://clair:8080"
}'
# 查看当前可用扫描器
curl -k -u "admin:ChangeMe123"
"https://harbor.example.com/api/v2.0/scanners" | jq .
3. 配置扫描策略与准入规则
在 Harbor UI 中「系统管理 → 配置管理 → 安全」可以可视化设置;同样也可以通过配置 API 完成。核心策略包括:推送即扫、定时扫描 cron、漏洞等级阈值、阻止危险镜像拉取。
# 推送即扫 + 阻止高危镜像(通过 Configuration API)
curl -k -u "admin:ChangeMe123"
-X PUT "https://harbor.example.com/api/v2.0/configurations"
-H "Content-Type: application/json"
-d '{
"scanner_skip_update_pulltime": false,
"scan_all_policy": {
"type": "daily",
"parameter": { "daily_time": 2 }
},
"prevent_vulnerable_images_from_running": true,
"prevent_vulnerable_images_from_running_severity": "High"
}'
创建定时扫描任务(对全部仓库每天凌晨 2 点重新扫描):
curl -k -u "admin:ChangeMe123"
-X POST "https://harbor.example.com/api/v2.0/scans/schedule"
-H "Content-Type: application/json"
-d '{
"schedule": { "type": "Daily", "cron": "0 2 0 * * *" }
}'
prevent_vulnerable_images_from_running_severity: High表示:当镜像存在 High 及以上 漏洞时,Harbor 将阻止该镜像被拉取(pull),从源头拦截危险镜像流向运行时。
4. 用 API 触发扫描并获取报告
手动/CI 触发某个镜像仓库的扫描,并轮询等待报告就绪:
# 对 project/repo 的 tag 发起扫描
SCAN_RESP=$(curl -k -s -u "admin:ChangeMe123"
-X POST "https://harbor.example.com/api/v2.0/projects/myapp/repositories/backend/artifacts/v1.2.3/scan")
# 查询扫描状态与摘要
curl -k -s -u "admin:ChangeMe123"
"https://harbor.example.com/api/v2.0/projects/myapp/repositories/backend/artifacts/v1.2.3"
| jq '{digest, scan_overview}'
# 拉取完整漏洞列表
curl -k -s -u "admin:ChangeMe123"
"https://harbor.example.com/api/v2.0/projects/myapp/repositories/backend/artifacts/v1.2.3/vulnerabilities?severity=High&page=1&page_size=100"
| jq '[.[] | {id, package, version, severity, fix_version}]'
5. 在 Tekton 中集成扫描质量门禁
生产最佳实践:CI 推送镜像后,必须扫描且高危漏洞数归零(或低于阈值)才能继续 CD。下面的 Tekton Task 调用 Harbor API 触发扫描并轮询结果,不合格直接失败:
apiVersion: tekton.dev/v1
kind: Task
metadata:
name: harbor-scan-gate
spec:
params:
- name: harbor-url
type: string
default: "https://harbor.example.com"
- name: image
type: string # 例如 backend
- name: tag
type: string
- name: project
type: string
- name: fail-severity
type: string
default: "High" # 高于等于该等级即失败
steps:
- name: scan-and-gate
image: bitnami/kubectl:latest
script: |
#!/usr/bin/env bash
set -euxo pipefail
H="$HARBOR_URL/api/v2.0"
# 触发扫描
curl -k -s -u "$HARBOR_USER:$HARBOR_PASS"
-X POST "$H/projects/$(params.project)/repositories/$(params.image)/artifacts/$(params.tag)/scan"
# 轮询等待扫描完成
for i in $(seq 1 30); do
ov=$(curl -k -s -u "$HARBOR_USER:$HARBOR_PASS"
"$H/projects/$(params.project)/repositories/$(params.image)/artifacts/$(params.tag)"
| jq -r '.scan_overview["application/vnd.security.vulnerability.report; version=1.1"].scan_status')
[ "$ov" = "Success" ] && break
[ "$ov" = "Error" ] && { echo "scan failed"; exit 1; }
sleep 5
done
# 提取各等级数量
read CRIT HIGH MED <<< $(curl -k -s -u "$HARBOR_USER:$HARBOR_PASS"
"$H/projects/$(params.project)/repositories/$(params.image)/artifacts/$(params.tag)/vulnerabilities?page_size=1&page=1"
-H "X-Accept-Vulnerabilities: application/vnd.security.vulnerability.report; version=1.1"
| jq -r '.summary | [.Critical // 0, .High // 0, .Medium // 0] | @tsv')
echo "Critical=$CRIT High=$HIGH Medium=$MED"
# 质量门禁:Critical/High 必须为 0
if [ "$CRIT" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
echo "❌ 高危漏洞未清零,阻断发布"
exit 1
fi
echo "✅ 扫描通过,允许发布"
将该 Task 编排进流水线,放在「构建推送」之后、「Argo CD 触发部署」之前:
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: build-and-deploy
spec:
workspaces:
- name: source
tasks:
- name: build
taskRef: { name: kaniko-build }
workspaces: [{ name: source, workspace: source }]
- name: scan
taskRef: { name: harbor-scan-gate }
runAfter: [build]
params:
- { name: project, value: myapp }
- { name: image, value: backend }
- { name: tag, value: v1.2.3 }
- name: deploy
taskRef: { name: gitops-update } # 更新 Git 清单触发 Argo CD 同步
runAfter: [scan]
6. 配置漏洞白名单与 Webhook 通知
对已评估、可接受的漏洞加入 CVE Allowlist,并配置扫描完成后的 Webhook 通知:
# 给项目添加 CVE 白名单
curl -k -u "admin:ChangeMe123"
-X PUT "https://harbor.example.com/api/v2.0/projects/myapp/preheat/policies"
-d '' # (占位示意,真实白名单走 projects API,见下方)
# 真实写法:为项目设置 CVE allowlist
curl -k -u "admin:ChangeMe123"
-X PUT "https://harbor.example.com/api/v2.0/projects/myapp"
-H "Content-Type: application/json"
-d '{
"cve_allowlist": {
"items": [
{ "cve_id": "CVE-2024-12345" },
{ "cve_id": "CVE-2024-67890" }
]
}
}'
# Webhook:扫描完成时通知团队群(如钉钉/Slack)
curl -k -u "admin:ChangeMe123"
-X POST "https://harbor.example.com/api/v2.0/projects/myapp/webhook/policies"
-H "Content-Type: application/json"
-d '{
"name": "scan-notify",
"enabled": true,
"event_types": ["SCANNING_COMPLETED", "SCANNING_FAILED"],
"targets": [{
"type": "http",
"address": "https://hooks.slack.com/services/T000/B000/XXXX",
"auth_header": "Bearer xoxb-xxx"
}]
}'
常见问题
Q1:Trivy 扫描器一直显示「不可用/Unhealthy」怎么办?
最常见是 Trivy 数据库初始化慢或无法下载漏洞库(国内网络问题)。排查顺序:kubectl logs -n harbor deploy/harbor-trivy -f 看是否卡在 DB 下载;给 Trivy 配置 TRIVY_DB_REPOSITORY 指向可访问的镜像源,或设置 HTTP 代理;数据库就绪前扫描任务会排队,耐心等待首次 DB 同步完成。
Q2:为什么扫出来的漏洞比 Clarity 少/多?
不同扫描器漏洞库与匹配算法不同,结果天然有差异属正常。Trivy 覆盖更广(OS 包 + 语言依赖),且数据库更新频繁。不要跨扫描器比较数量,关键是固定的扫描器 + 固定的策略阈值,保证趋势可比。
Q3:开启「阻止高危镜像拉取」后,业务镜像拉不下来怎么办?
这是设计使然——该规则就是为了阻断危险镜像流向运行时。处理方式:① 修复镜像漏洞后重新构建并 push 覆盖;② 对该 CVE 加入项目白名单(CVE Allowlist);③ 临时将 prevent_vulnerable_images_from_running_severity 提升到 Critical。务必走修复路线而非一味豁免。
Q4:扫描很慢,大量镜像积压如何加速?
Trivy 有层缓存,相似基础镜像复用快。优化手段:启用 skipUpdate(内网预置 DB)、提高 Trivy 副本数与资源配额、按项目错峰设置扫描计划、对历史老镜像用「非关键」优先级。必要时可限制仅扫描 immutable 标签镜像。
Q5:CI 里调用 Harbor API 需要给什么权限?
推荐用 Robot Account(而非 admin),仅授予目标项目的 push + scan + pull 权限(角色可设为「项目维护者」级别)。Robot 凭证以 robot$myapp+ci 格式作为用户名,令牌作为密码,避免 admin 明文外泄。
总结
- 扫描器即插即用:Harbor 通过 Scanner Adapter 集成 Trivy/Clair,新项目直接选 Trivy(性能、准确率、维护性全面领先)。
- 三道防线缺一不可:推送即扫(即时发现)+ 定时扫描(捕获新增 CVE)+ 准入阻断(阻止高危镜像被拉取)。
- 把扫描变成 CI 质量门禁:Tekton 推送镜像后调用 Harbor API 触发扫描并轮询结果,高危不清零不放行,形成「构建→扫描→门禁→CD」的安全闭环。
- 权限最小化:CI 凭证一律用 Robot Account,只授予目标项目必要角色,杜绝 admin 泄露。
- 供应链安全是持续过程:配合 immutable tag、CVE 白名单、Webhook 通知,让安全扫描融入日常而非事后补救。
明天我们将进入 Argo CD 深入篇,先讲 Argo CD 的安装与配置(kubectl 安装、ingress、初始密码),打通 GitOps 交付的最后一公里。















暂无评论内容