第 19/19 天 — 系列收官篇。今天我们将打通 DevOps 全链路的最后一公里:从镜像漏洞扫描、签名验签到准入控制,构建完整的供应链安全闭环。
引言:为什么供应链安全至关重要

在前 18 天的实战中,我们搭建了从代码提交到灰度发布的完整 DevOps 管道。然而,如果一条流水线只关注”能不能跑通”,却忽略了”跑的东西安不安全”,那就如同一条高速运转的流水线没有质检环节——速度越快,风险越大。
近年来,软件供应链攻击事件频发:攻击者通过污染开源依赖、篡改镜像仓库等方式,将恶意代码植入生产环境。SolarWinds 事件、Log4j 漏洞都给业界敲响了警钟。Kubernetes 生态中,容器镜像是最核心的交付物,保障镜像安全就是保障整个交付管道的安全。
今天我们将围绕三个关键工具构建供应链安全闭环:
- Trivy:全能型漏洞扫描器,扫描镜像、文件系统、IaC 配置
- cosign:基于 Sigstore 项目的容器镜像签名工具
- Kyverno:Kubernetes 原生的策略引擎,用于准入控制
三者协同形成”扫描 → 签名 → 验签准入”的完整闭环。
一、核心概念:供应链安全的三大防线
1.1 漏洞扫描(Trivy)
Trivy 是 Aqua Security 开源的安全扫描工具,支持扫描容器镜像、文件系统、Git 仓库、Kubernetes 资源等。它的核心能力包括:
- OS 包漏洞:扫描 Alpine、Debian、Ubuntu 等基础镜像中的已知漏洞
- 语言依赖漏洞:扫描 package-lock.json、requirements.txt、go.mod 等
- IaC 配置扫描:检测 Terraform、Kubernetes YAML 中的配置风险
- 密钥泄露检测:扫描代码中的硬编码凭证
1.2 镜像签名(cosign)
cosign 是 Sigstore 项目的一部分,用于对容器镜像进行密码学签名。它支持:
- 密钥对签名:使用 ED25519 或 RSA 密钥对签名
- Keyless 签名:基于 OIDC 的无密钥签名(Fulcio + Rekor)
- 透明日志:签名记录存入 Rekor 透明日志,可审计不可篡改
1.3 准入控制(Kyverno)
Kyverno 是专为 Kubernetes 设计的策略引擎,使用 YAML 定义策略,无需学习新的策略语言。它可以在 Admission Controller 阶段拦截不合规的部署请求。
二、Trivy 漏洞扫描部署与集成
2.1 安装 Trivy
在 GitLab Runner 所在的 CI 环境中安装 Trivy:
# 下载并安装 Trivy
wget https://github.com/aquasecurity/trivy/releases/download/v0.50.0/trivy_0.50.0_Linux-64bit.tar.gz
tar zxvf trivy_0.50.0_Linux-64bit.tar.gz
mv trivy /usr/local/bin/
# 验证安装
trivy –version
2.2 GitLab CI 集成 Trivy 扫描
将 Trivy 扫描集成到第 6 天搭建的 GitLab CI 流水线中,在 Kaniko 构建镜像之后立即扫描:
# .gitlab-ci.yml 片段
stages:
– build
– scan
– sign
– deploy
trivy-scan:
stage: scan
image: aquasec/trivy:latest
variables:
TRIVY_EXIT_CODE: "1"
TRIVY_SEVERITY: "CRITICAL,HIGH"
script:
# 扫描构建产物镜像(Harbor 中的镜像)
– trivy image –severity CRITICAL,HIGH –exit-code 1
–format json –output trivy-report.json
$HARBOR_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA
# 生成 HTML 报告
– trivy image –format template
–template "@contrib/html.tpl"
–output trivy-report.html
$HARBOR_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA
artifacts:
paths:
– trivy-report.json
– trivy-report.html
expire_in: 30 days
rules:
– if: $CI_PIPELINE_SOURCE == "merge_request_event"
上述配置中,TRIVY_EXIT_CODE: "1" 表示当发现 CRITICAL 或 HIGH 级别漏洞时,CI 任务失败,阻止后续部署。这确保了带严重漏洞的镜像不会被推送到生产环境。
2.3 基础镜像的预扫描
除了扫描业务镜像,我们还应该在流水线入口扫描 Dockerfile 引用的基础镜像,做到尽早发现问题:
# 扫描基础镜像 python:3.12-slim
trivy image –severity CRITICAL,HIGH python:3.12-slim
# 忽略特定 CVE(经过评估确认可接受的风险)
cat > .trivyignore << 'EOF'
CVE-2024-XXXXX
CVE-2024-YYYYY
EOF
# 使用忽略文件扫描
trivy image –ignorefile .trivyignore –severity CRITICAL,HIGH myapp:latest
三、cosign 镜像签名
3.1 生成签名密钥
在安全的密钥管理系统(如 GitLab CI Variables 或 HashiCorp Vault)中存储密钥。首先生成密钥对:
# 生成 cosign 密钥对(会提示设置密码)
cosign generate-key-pair
# 输出文件:
# cosign.pub – 公钥(用于验签,可公开)
# cosign.key – 私钥(用于签名,必须保密)
将私钥内容存入 GitLab CI 变量 COSIGN_PRIVATE_KEY,密码存入 COSIGN_PASSWORD。公钥 cosign.pub 提交到代码仓库,供 Kyverno 验签使用。
3.2 GitLab CI 中签名镜像
在 Trivy 扫描通过后,对镜像进行签名:
# .gitlab-ci.yml 片段
cosign-sign:
stage: sign
image: gcr.io/projectsigstore/cosign:v2.2.3
variables:
COSIGN_EXPERIMENTAL: "true"
script:
# 登录 Harbor
– cosign login -u $HARBOR_USER -p $HARBOR_PASS $HARBOR_REGISTRY
# 对镜像进行签名
– cosign sign –key env://COSIGN_PRIVATE_KEY
$HARBOR_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA
# 验证签名是否成功
– cosign verify –key cosign.pub
$HARBOR_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA
rules:
– if: $CI_COMMIT_BRANCH == "main"
环境变量 COSIGN_PRIVATE_KEY 和 COSIGN_PASSWORD 通过 GitLab CI/CD Settings → CI/CD Variables 配置,确保密钥不会泄露到代码中。签名操作将签名元数据推送到镜像的 OCI Manifest 中,可通过 cosign verify 验证。
3.3 Keyless 签名模式(进阶)
对于更高安全要求的场景,可以使用 Keyless 模式,无需管理私钥:
# Keyless 签名(基于 OIDC 令牌,需集成 GitLab OIDC)
export COSIGN_EXPERIMENTAL=1
# 使用 OIDC 令牌签名
cosign sign –identity-token $CI_JOB_JWT_V2
$HARBOR_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA
# 验证签名(按 Issuer 和 Subject 过滤)
cosign verify
–certificate-identity "https://gitlab.com/myorg/myapp//.gitlab-ci.yml@refs/heads/main"
–certificate-oidc-issuer "https://gitlab.com"
$HARBOR_REGISTRY/myapp:$CI_COMMIT_SHORT_SHA
Keyless 模式利用 Sigstore 的 Fulcio 证书颁发和 Rekor 透明日志,签名者身份与 OIDC 令牌绑定,审计性更强。
四、Kyverno 验签准入控制
4.1 安装 Kyverno
在 K8s 集群中安装 Kyverno,使用 Helm 部署:
# 添加 Kyverno Helm 仓库
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
# 安装 Kyverno(命名空间 kyverno)
helm install kyverno kyverno/kyverno
–namespace kyverno
–create-namespace
–set admissionController.replicas=3
–set backgroundController.replicas=2
# 验证安装
kubectl get pods -n kyverno
4.2 定义验签策略
创建 ClusterPolicy,要求所有来自 Harbor 的镜像必须经过 cosign 签名才能部署:
# require-signed-images.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-cosign-signature
annotations:
policies.kyverno.io/title: Require Cosign Signature
policies.kyverno.io/category: Supply Chain Security
policies.kyverno.io/severity: high
spec:
validationFailureAction: Enforce
rules:
– name: verify-cosign-signature
match:
any:
– resources:
kinds:
– Pod
verifyImages:
– imageReferences:
– "harbor.stellardata.top/*"
attestors:
– entries:
– keys:
publicKeys: |
—–BEGIN PUBLIC KEY—–
<cosign.pub 内容,去掉注释行>
—–END PUBLIC KEY—–
# 签名验证失败时拒绝部署
failureAction: Fail
# 验证通过后将镜像替换为签名摘要版本
mutateDigest: true
应用策略:
kubectl apply -f require-signed-images.yaml
# 查看策略状态
kubectl get clusterpolicy require-cosign-signature
4.3 测试策略效果
用未签名镜像测试,应被拒绝:
# 推送一个未签名镜像到 Harbor
docker pull nginx:alpine
docker tag nginx:alpine harbor.stellardata.top/library/nginx-unsigned:alpine
docker push harbor.stellardata.top/library/nginx-unsigned:alpine
# 尝试部署未签名镜像(应被拒绝)
cat << 'EOF' | kubectl apply -f –
apiVersion: v1
kind: Pod
metadata:
name: test-unsigned
namespace: default
spec:
containers:
– name: nginx
image: harbor.stellardata.top/library/nginx-unsigned:alpine
EOF
# 输出错误:
# Error from server: admission webhook "validate.kyverno.svc" denied the request:
# resource Pod/default/test-unsigned was blocked due to the following policies:
# require-cosign-signature:
# verify-cosign-signature: image verification failed
使用已签名镜像测试,应成功部署:
# 对镜像签名后重新部署
cosign sign –key cosign.key harbor.stellardata.top/library/nginx-unsigned:alpine
# 再次尝试部署(应成功)
cat << 'EOF' | kubectl apply -f –
apiVersion: v1
kind: Pod
metadata:
name: test-signed
namespace: default
spec:
containers:
– name: nginx
image: harbor.stellardata.top/library/nginx-unsigned:alpine
EOF
# 验证 Pod 运行
kubectl get pod test-signed
五、常见问题与排查
Q1:Trivy 扫描超时或数据库下载失败?
Trivy 首次运行需下载漏洞数据库(约 200MB),在内网环境可能超时。解决方案:
# 预置数据库到内网镜像仓库
trivy image –download-db-only –db-repository harbor.stellardata.top/security/trivy-db
# 指定内网数据库镜像
export TRIVY_DB_REPOSITORY=harbor.stellardata.top/security/trivy-db
trivy image myapp:latest
Q2:cosign 签名在 CI 中报权限错误?
确保 CI 变量 COSIGN_PASSWORD 正确设置,并且私钥内容未经过多行转义损坏。建议使用 base64 编码存储私钥,在 CI 中解码:
# 编码存储
base64 -w 0 cosign.key # 结果存入 CI 变量 COSIGN_PRIVATE_KEY_B64
# CI 脚本中解码
echo "$COSIGN_PRIVATE_KEY_B64" | base64 -d > /tmp/cosign.key
cosign sign –key /tmp/cosign.key $IMAGE
Q3:Kyverno 策略导致 Argo CD 同步失败?
Argo CD 部署的资源也需要通过 Kyverno 准入控制。如果镜像未签名,Argo CD 的同步会被拒绝。确保在 GitOps 仓库中只引用已签名的镜像(使用 immutable digest 而非 tag):
# GitOps 部署清单中使用摘要
image: harbor.stellardata.top/myapp@sha256:abc123…
六、全链路安全闭环回顾
至此,我们的供应链安全闭环已完整打通:
- 代码提交 → GitLab 触发 CI 流水线
- Kaniko 构建 → 镜像推送到 Harbor
- Trivy 扫描 → 发现 CRITICAL/HIGH 漏洞则流水线中止
- cosign 签名 → 扫描通过后对镜像签名
- GitOps 部署 → Argo CD 从 Git 仓库同步部署清单
- Kyverno 验签 → 准入控制验证镜像签名,未签名镜像拒绝部署
- Argo Rollouts 灰度 → 金丝雀渐进式发布到生产环境
- Prometheus 监控 → 实时观测应用健康状态
- Alertmanager 告警 → 异常自动通知运维团队
整个闭环确保了:没有经过安全扫描的镜像无法进入流水线后段;没有签名的镜像无法部署到 K8s 集群;已部署的应用处于持续监控之下。 这就是”安全左移”和”默认不信任”理念的落地实践。
七、总结
今天我们完成了 DevOps 全链路实战系列的收官之作,构建了从漏洞扫描到准入控制的供应链安全闭环:
- Trivy 在 CI 阶段拦截带严重漏洞的镜像,实现安全左移
- cosign 对通过扫描的镜像进行密码学签名,确保镜像不可篡改
- Kyverno 在 K8s 准入控制层验证签名,未签名镜像无法部署
三者协同配合 GitLab CI、Harbor、Argo CD 形成了纵深防御体系,将安全嵌入到 DevOps 全链路的每一个环节,而非事后补救。
系列完结感言
从第 1 天的架构总览到今天的供应链安全,19 天的内容覆盖了 DevOps 工程师从零搭建生产级 CI/CD 管道所需的全部核心技能。希望这个系列能帮助大家在实际工作中少走弯路,真正落地 DevOps 实践。
下期预告
本系列已全部完结(共 19 天)。后续如果需要进一步深入某个主题,可以关注”(重制版)”系列,我们将以新技术版本和新环境背景重新梳理经典选题。感谢大家的陪伴!
系列大纲
- 全链路架构总览:从代码提交到灰度发布的完整管道设计
- K8s 集群与网络基础:kubeadm 离线部署与 Cilium 网络插件
- Harbor 私有镜像仓库部署与验证:Helm 安装与镜像推送
- GitLab CE 自托管部署:代码仓库与项目管理平台
- GitLab Runner 配置:K8s Executor 与 RBAC 权限设置
- GitLab CI 流水线搭建:.gitlab-ci.yml 与 Kaniko 无守护进程构建
- Harbor 镜像版本管理与 Tag 策略配置
- Argo CD 部署与 GitOps 配置仓库创建
- CI 与 CD 联动:从代码提交到部署的全自动闭环
- SonarQube 代码质量检查:SonarQube 部署与 GitLab CI 集成
- Prometheus 部署:kube-prometheus-stack 与 ServiceMonitor 指标采集
- Grafana 看板搭建:K8s 标准面板与自定义业务看板
- 告警规则与 Alertmanager:钉钉/邮件通知渠道配置
- Argo Rollouts 安装与基础概念:CRD 与 Rollout 资源定义
- 金丝雀发布实战:setWeight 流量分割与 pause 步骤
- AnalysisTemplate 指标分析与自动回滚机制
- 全链路端到端演示:代码提交→构建→代码检查→灰度→监控→告警完整流程
- 生产化加固要点:Harbor/GitLab/Argo CD/SonarQube 高可用方案
- 供应链安全闭环:Trivy 扫描 + cosign 签名 + Kyverno 验签(今天)

















暂无评论内容