05 - 签名集成
第 5 章:签名集成
本章介绍如何使用 cosign 和 Sigstore 生态进行软件签名,并将签名记录写入 Rekor 透明日志。涵盖传统密钥签名和无密钥签名(Keyless Signing)两种方式。
5.1 签名方式概览
Sigstore 生态支持多种签名方式:
| 签名方式 | 密钥管理 | 适用场景 | 安全级别 |
|---|---|---|---|
| 传统密钥签名 | 长期密钥 + 密码 | 本地开发、小团队 | ⭐⭐⭐ |
| KMS 集成签名 | 云 KMS 管理密钥 | 企业环境 | ⭐⭐⭐⭐ |
| 无密钥签名 | 短期证书,无长期密钥 | CI/CD、开源项目 | ⭐⭐⭐⭐⭐ |
5.2 cosign 基础签名
5.2.1 生成密钥对
# 交互式生成密钥对
cosign generate-key-pair
# 预期输出:
# Enter password for private key: <输入密码>
# Enter again: <确认密码>
# Private key written to cosign.key
# Public key written to cosign.pub
# 非交互式生成(CI/CD 环境)
COSIGN_PASSWORD=mysecurepassword cosign generate-key-pair
5.2.2 签名容器镜像
# 签名一个 OCI 容器镜像
cosign sign \
--key cosign.key \
--yes \
ghcr.io/myorg/myimage:v1.0.0
# 签名并附带注释
cosign sign \
--key cosign.key \
--yes \
--annotations "build_id=$BUILD_ID" \
--annotations "commit_sha=$COMMIT_SHA" \
ghcr.io/myorg/myimage:v1.0.0
# 签名多架构镜像(签名 manifest list)
cosign sign \
--key cosign.key \
--yes \
--recursive \
ghcr.io/myorg/myimage:v1.0.0
5.2.3 签名 Blob 文件
# 签名文件
cosign sign-blob \
--key cosign.key \
--yes \
myartifact.tar.gz
# 签名并输出签名和证书到文件
cosign sign-blob \
--key cosign.key \
--yes \
--output-signature myartifact.sig \
--output-certificate myartifact.cert \
myartifact.tar.gz
5.2.4 验证签名
# 验证容器镜像签名
cosign verify \
--key cosign.pub \
ghcr.io/myorg/myimage:v1.0.0
# 验证 Blob 签名
cosign verify-blob \
--key cosign.pub \
--signature myartifact.sig \
myartifact.tar.gz
# 验证并检查注释
cosign verify \
--key cosign.pub \
--annotations "build_id=$BUILD_ID" \
ghcr.io/myorg/myimage:v1.0.0
5.3 Fulcio 证书颁发
5.3.1 Fulcio 是什么?
Fulcio 是 Sigstore 的证书颁发机构(CA),基于用户的身份(OIDC 身份)颁发短期代码签名证书。
┌─────────────────────────────────────────────────────────────────┐
│ Fulcio 工作流程 │
│ │
│ 用户 OIDC Provider Fulcio │
│ │ │ │ │
│ │ 1. 登录请求 │ │ │
│ │────────────────────►│ │ │
│ │ │ │ │
│ │ 2. ID Token │ │ │
│ │◄────────────────────│ │ │
│ │ │ │ │
│ │ 3. 生成临时密钥对 │ │ │
│ │ 4. CSR + ID Token │ │ │
│ │────────────────────────────────────────────►│ │
│ │ │ │ │
│ │ │ 5. 验证 Token │ │
│ │ │◄─────────────────────│ │
│ │ │ │ │
│ │ │ 6. 确认身份 │ │
│ │ │─────────────────────►│ │
│ │ │ │ │
│ │ 7. 签名证书 │ │ │
│ │◄────────────────────────────────────────────│ │
│ │ │ │ │
│ │ 8. 用临时密钥签名构件 │ │
│ │ 9. 上传签名到 Rekor │ │
└─────────────────────────────────────────────────────────────────┘
5.3.2 Fulcio 证书的内容
Fulcio 颁发的证书包含以下关键信息:
Certificate:
Data:
Version: 3 (0x2)
Serial Number: <unique-serial>
Signature Algorithm: ECDSA-SHA384
Issuer: CN=sigstore-intermediate, O=sigstore.dev
Validity
Not Before: <issue-time>
Not After : <issue-time + 10min> ← 10 分钟有效期
Subject:
<空 - 身份信息在扩展字段中>
X509v3 extensions:
X509v3 Subject Alternative Name:
email:[email protected] ← OIDC 身份
1.3.6.1.4.1.57264.1.1:
https://accounts.google.com ← OIDC 颁发者
1.3.6.1.4.1.57264.1.8:
https://github.com/login/oauth ← 构建环境(GitHub Actions)
1.3.6.1.4.1.57264.1.9:
https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main
← 工作流引用
5.3.3 Fulcio 支持的 OIDC 提供者
| 提供者 | 身份字段 | 说明 |
|---|---|---|
| Google 账号邮箱 | ||
| GitHub | Email / Workflow | GitHub Actions 身份 |
| Microsoft | Azure AD / Entra ID | |
| GitLab | Email / Pipeline | GitLab CI |
| Dex | 自定义 | 企业 OIDC 代理 |
5.4 OIDC 身份验证
5.4.1 什么是 OIDC?
OIDC(OpenID Connect)是一个基于 OAuth 2.0 的身份验证层,允许客户端验证用户身份并获取基本信息。
5.4.2 OIDC 与 Sigstore 的集成
Sigstore 使用 OIDC 获取用户的短期身份令牌(ID Token),然后向 Fulcio 申请代码签名证书。
主要 OIDC 提供者配置:
# Google
cosign sign --yes \
--oidc-issuer=https://accounts.google.com \
ghcr.io/myorg/myimage:v1.0.0
# GitHub Actions(自动检测)
# 无需手动指定 issuer,cosign 会自动检测 CI 环境
# GitLab CI
cosign sign --yes \
--oidc-issuer=https://gitlab.com \
ghcr.io/myorg/myimage:v1.0.0
# 自定义 OIDC 提供者(如企业 SSO)
cosign sign --yes \
--oidc-issuer=https://sso.mycompany.com \
--oidc-client-id=cosign \
--oidc-client-secret=$OIDC_SECRET \
ghcr.io/myorg/myimage:v1.0.0
5.4.3 手动获取 ID Token
在某些环境中,你可能需要手动获取 ID Token:
# 使用 gcloud 获取 Google ID Token
gcloud auth print-identity-token --audiences=sigstore
# 使用 GitHub API 获取 Token(GitHub Actions 中自动设置)
curl -s -H "Authorization: bearer $ACTIONS_ID_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sigstore"
# 使用 Dex 获取 Token
curl -s "https://dex.mycompany.com/auth?client_id=cosign&response_type=id_token&scope=openid+email"
5.5 无密钥签名(Keyless Signing)
5.5.1 无密钥签名原理
无密钥签名是 Sigstore 最重要的创新之一。它消除了长期密钥管理的负担:
传统签名:
开发者 ──► 长期私钥(需安全存储数年)
──► 用户 ──► 公钥(需验证信任链)
无密钥签名:
开发者 ──► OIDC 身份验证(一次性)
──► Fulcio 短期证书(10 分钟有效期)
──► 临时密钥签名
──► 签名记录到 Rekor
──► 临时密钥销毁
5.5.2 无密钥签名的步骤
# 步骤 1: 用户通过 OIDC 身份验证
# 步骤 2: Fulcio 颁发短期证书
# 步骤 3: cosign 使用临时密钥签名
# 步骤 4: 签名上传到 Rekor
# 一键完成所有步骤
cosign sign --yes ghcr.io/myorg/myimage:v1.0.0
# 输出示例:
# tlog entry created with index: 12345678
# Pushing signature to: ghcr.io/myorg/myimage:sha256-abc123.sig
5.5.3 验证无密钥签名
验证时需要提供预期的身份信息:
# 验证容器镜像
cosign verify \
--certificate-identity=[email protected] \
--certificate-oidc-issuer=https://accounts.google.com \
ghcr.io/myorg/myimage:v1.0.0
# 验证 GitHub Actions 签名的镜像
cosign verify \
--certificate-identity=https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/myorg/myimage:v1.0.0
5.5.4 无密钥签名的安全优势
| 特性 | 传统密钥 | 无密钥签名 |
|---|---|---|
| 密钥存储 | 需要长期安全存储 | 临时密钥,立即销毁 |
| 密钥泄露影响 | 所有历史签名受影响 | 仅影响单次签名 |
| 证书有效期 | 数年 | 10 分钟 |
| 身份绑定 | 手动绑定 | 自动绑定 OIDC 身份 |
| 信任建立 | 需要密钥分发 | 基于 OIDC 提供者信任 |
5.6 企业级签名集成
5.6.1 使用云 KMS 签名
对于企业环境,建议使用云 KMS 管理签名密钥:
# AWS KMS
cosign sign \
--key awskms:///alias/my-signing-key \
--yes \
ghcr.io/myorg/myimage:v1.0.0
# GCP KMS
cosign sign \
--key gcpkms://projects/my-project/locations/global/keyRings/my-ring/cryptoKeys/my-key/versions/1 \
--yes \
ghcr.io/myorg/myimage:v1.0.0
# Azure Key Vault
cosign sign \
--key azurekms://my-vault.my-service.azure.net/my-key \
--yes \
ghcr.io/myorg/myimage:v1.0.0
# HashiCorp Vault Transit
cosign sign \
--key hashivault://transit/keys/my-key \
--yes \
ghcr.io/myorg/myimage:v1.0.0
5.6.2 KMS 配置表
| 云平台 | 环境变量 | 说明 |
|---|---|---|
| AWS | AWS_REGION, AWS_ACCESS_KEY_ID | 需要 KMS 权限 |
| GCP | GOOGLE_APPLICATION_CREDENTIALS | 需要 Cloud KMS 角色 |
| Azure | AZURE_TENANT_ID, AZURE_CLIENT_ID | 需要 Key Vault 访问权限 |
| Vault | VAULT_ADDR, VAULT_TOKEN | 需要 Transit 引擎权限 |
5.6.3 多密钥签名
对于高安全需求,可以使用多个密钥进行签名:
# 使用不同密钥签名
cosign sign --key key1.key --yes ghcr.io/myorg/myimage:v1.0.0
cosign sign --key key2.key --yes ghcr.io/myorg/myimage:v1.0.0
# 验证时要求多个签名
cosign verify \
--key key1.pub \
ghcr.io/myorg/myimage:v1.0.0
cosign verify \
--key key2.pub \
ghcr.io/myorg/myimage:v1.0.0
5.7 签名容器镜像的高级操作
5.7.1 签名 OCI Bundle
# 签名并指定存储位置
COSIGN_REPOSITORY=myregistry.io/signatures \
cosign sign --key cosign.key --yes \
ghcr.io/myorg/myimage:v1.0.0
# 验证时指定签名位置
COSIGN_REPOSITORY=myregistry.io/signatures \
cosign verify --key cosign.pub \
ghcr.io/myorg/myimage:v1.0.0
5.7.2 签名时附加 SBOM
# 生成 SBOM
syft ghcr.io/myorg/myimage:v1.0.0 -o spdx-json > sbom.spdx.json
# 签名 SBOM
cosign sign-blob \
--key cosign.key \
--yes \
--output-signature sbom.sig \
--output-certificate sbom.cert \
sbom.spdx.json
# 签名镜像并附加 SBOM 附件
cosign attach sbom \
--sbom sbom.spdx.json \
--type spdx \
ghcr.io/myorg/myimage:v1.0.0
# 签名附件
cosign sign \
--key cosign.key \
--yes \
--attachment sbom \
ghcr.io/myorg/myimage:v1.0.0
5.7.3 签名时附加漏洞报告
# 生成漏洞报告
grype ghcr.io/myorg/myimage:v1.0.0 -o json > vuln-report.json
# 附加漏洞报告
cosign attest \
--key cosign.key \
--yes \
--predicate vuln-report.json \
--type vuln \
ghcr.io/myorg/myimage:v1.0.0
5.8 签名策略配置
5.8.1 cosign 签名策略
# 签名策略示例 (policy.yaml)
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: "ghcr.io/myorg/**"
- glob: "gcr.io/myproject/**"
authorities:
- key:
data: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
- keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subject: https://github.com/myorg/myrepo/.github/workflows/build.yml
5.8.2 签名验证策略
# 使用策略验证
cosign verify \
--policy policy.yaml \
ghcr.io/myorg/myimage:v1.0.0
# 使用 Rego 策略验证
cosign verify \
--policy policy.rego \
ghcr.io/myorg/myimage:v1.0.0
5.9 签名流程最佳实践
5.9.1 推荐签名流程
┌─────────────────────────────────────────────────────────────────┐
│ 推荐的签名流程 │
│ │
│ 1. 开发阶段 │
│ └─ 使用 cosign generate-key-pair 生成测试密钥 │
│ │
│ 2. CI/CD 阶段 │
│ ├─ 使用无密钥签名(GitHub Actions / GitLab CI) │
│ ├─ 自动上传签名到 Rekor │
│ └─ 附加 SBOM 和漏洞报告 │
│ │
│ 3. 发布阶段 │
│ ├─ 使用 KMS 签名生产镜像 │
│ ├─ 要求多签名(审批流程) │
│ └─ 验证所有签名和 Rekor 记录 │
│ │
│ 4. 部署阶段 │
│ ├─ 验证镜像签名 │
│ ├─ 检查 Rekor 包含证明 │
│ └─ 验证证书身份和颁发者 │
└─────────────────────────────────────────────────────────────────┘
5.9.2 签名策略对比
| 策略 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 仅密钥签名 | 小团队 | 简单 | 密钥管理负担 |
| 密钥 + Rekor | 中型团队 | 可审计 | 仍需密钥管理 |
| 无密钥签名 | 开源项目 | 无密钥管理 | 依赖 OIDC 提供者 |
| KMS + 无密钥 | 企业 | 最高安全性 | 配置复杂 |
| 多重签名 | 高安全需求 | 防止单点失败 | 流程复杂 |
5.10 注意事项
无密钥签名的证书有效期:Fulcio 颁发的证书只有 10 分钟有效期。但这不影响签名的长期有效性,因为签名本身和证书都记录在 Rekor 中。
OIDC 依赖:无密钥签名依赖于 OIDC 提供者。如果 OIDC 提供者出现问题,签名流程会受影响。
时钟同步:签名和验证过程中需要准确的时间。确保服务器时钟同步(NTP)。
速率限制:公共 Fulcio 和 Rekor 实例有速率限制。企业环境应考虑私有部署。
5.11 本章小结
| 方式 | 适用场景 | 密钥管理 | Rekor 集成 |
|---|---|---|---|
| 传统密钥签名 | 本地开发 | 长期密钥 | 手动 / 自动 |
| KMS 签名 | 企业环境 | 云 KMS | 自动 |
| 无密钥签名 | CI/CD | 无 | 自动 |
| 多重签名 | 高安全需求 | 多密钥 | 自动 |
扩展阅读
下一章:06 - 验证机制 — 深入了解签名验证、日志验证、包含证明和批量验证。