饮墨

子安饮墨馀三斗,留与卿儿作赋来

Cosign + Sigstore 容器镜像签名验证实战:从零构建软件供应链安全

痛点

容器化部署已成为标配,但一个关键问题常被忽视:你怎么确认运行的镜像确实是 CI/CD 构建出来的那个,而不是被篡改过的?

2024 年的多起供应链攻击事件再次敲响警钟 —— 攻击者通过入侵镜像仓库、CI 流水线或依赖库,注入恶意代码到最终镜像中。传统的 tag 机制(如 latestv1.2.3)是可变的,任何有仓库写权限的人都能覆盖同一个 tag 指向不同的 digest。

核心需求: 在镜像从构建到部署的全链路中,建立不可篡改的信任锚点 —— 即镜像签名与验证。

方案

Sigstore 是 Linux Foundation 孵化的开源项目,提供免费的代码/容器签名基础设施。其核心组件 Cosign 专门用于容器镜像的签名、验证和存储。

为什么选 Sigstore/Cosign 而非传统 GPG 签名?

维度 GPG 签名 Cosign (Sigstore)
密钥管理 手动分发/轮换公钥 支持 Keyless(OIDC 身份绑定)
透明度 无公开审计 Rekor 透明日志,不可篡改
集成难度 需自建验证链路 原生支持 OCI Registry、K8s Policy
CI/CD 集成 复杂 GitHub Actions/GitLab CI 原生支持

核心架构:

CI Build → Cosign Sign(私钥/Keyless)→ OCI Registry(镜像+签名)
                                              ↓
                            Kubernetes Admission → Policy Controller 验证签名 → 允许/拒绝部署

实操步骤

第 1 步:安装 Cosign

# Linux amd64
curl -sSL -o cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign && sudo mv cosign /usr/local/bin/

# 验证安装
cosign version

第 2 步:生成密钥对并签名镜像

方式一:本地密钥对(适合私有环境)

# 生成密钥对(会提示输入密码)
cosign generate-key-pair

# 签名镜像(以 digest 引用,避免 tag 歧义)
IMAGE="registry.example.com/myapp@sha256:abc123..."
cosign sign --key cosign.key $IMAGE

签名会作为 OCI artifact 存储在同一个 Registry 中,无需额外存储。

方式二:Keyless 签名(推荐用于 CI/CD)

# 无需管理密钥,通过 OIDC 身份(GitHub Actions/Google/Microsoft)签名
# 在 GitHub Actions 中:
cosign sign --yes $IMAGE

Keyless 模式下,Sigstore 的 Fulcio CA 会颁发短期证书,绑定 CI 的 OIDC 身份(如 https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main),签名记录写入 Rekor 透明日志。

第 3 步:验证镜像签名

# 使用公钥验证
cosign verify --key cosign.pub $IMAGE

# Keyless 验证(指定预期的签名者身份)
cosign verify \
  --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  $IMAGE

验证通过输出签名 payload(JSON 格式),包含签名时间、身份信息等元数据。

第 4 步:Kubernetes 集群中强制验证(Policy Controller)

安装 Sigstore Policy Controller(基于 Kubernetes Admission Webhook):

# 使用 Helm 安装
helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update

helm install policy-controller sigstore/policy-controller \
  -n cosign-system --create-namespace \
  --set webhook.failOpen=false

创建 ClusterImagePolicy 策略,强制所有镜像必须经过签名:

apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
  name: require-signed-images
spec:
  images:
    - glob: "registry.example.com/**"
  authorities:
    - key:
        data: |
          -----BEGIN PUBLIC KEY-----
          MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
          -----END PUBLIC KEY-----

对命名空间启用策略:

kubectl label namespace production policy.sigstore.dev/include=true

此后,任何未签名或签名验证失败的镜像都将被 Admission Webhook 拒绝部署。

第 5 步:CI/CD 集成示例(GitHub Actions)

name: Build, Sign and Push
on:
  push:
    branches: [main]

permissions:
  contents: read
  id-token: write    # 必须:用于 Keyless 签名的 OIDC token
  packages: write

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: sigstore/cosign-installer@v3

      - name: Build and Push
        run: |
          docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}

      - name: Sign Image (Keyless)
        env:
          DIGEST: ${{ steps.push.outputs.digest }}
        run: |
          cosign sign --yes ghcr.io/${{ github.repository }}@${DIGEST}

避坑

1. 签名用 digest 而非 tag

# ❌ 错误:tag 可能被覆盖,签名就失效了
cosign sign --key cosign.key registry.example.com/myapp:v1.0

# ✅ 正确:始终用 sha256 digest
cosign sign --key cosign.key registry.example.com/myapp@sha256:abc123...

2. Policy Controller 的 failOpen 配置

生产环境建议 failOpen=false(严格模式),但首次部署务必先在 staging 环境验证,避免因策略配置错误导致所有 Pod 无法启动。可以先用 warn 模式观察:

spec:
  mode: warn  # 只告警不阻断,观察一周后再切 enforce

3. Keyless 签名的 OIDC Issuer 变更

如果你的 CI 平台更换了 OIDC Issuer URL(如从 GitHub.com 迁移到 GHES),验证策略中的 certificate-oidc-issuer 必须同步更新,否则所有旧签名验证都会失败。建议在策略中同时保留新旧 issuer 过渡期。

总结

环节 工具 作用
签名 Cosign 对镜像 digest 进行密码学签名
证书 Fulcio Keyless 模式颁发短期签名证书
透明日志 Rekor 签名记录不可篡改,支持审计
准入控制 Policy Controller K8s 集群层面强制验证

落地建议:

  1. 起步阶段:CI 中加入 Cosign 签名步骤,成本几乎为零(多 5 秒构建时间)
  2. 观察阶段:部署 Policy Controller,用 warn 模式运行 1-2 周
  3. 强制阶段:切换为 enforce,拒绝一切未签名镜像进入生产
  4. 进阶:结合 SBOM(cosign attach sbom)和漏洞扫描结果,构建完整的供应链信任链

镜像签名不是银弹,但它是软件供应链安全中 ROI 最高的一环 —— 实施成本低、效果显著、合规审计友好。对于任何运行容器化工作负载的团队,这都应该是 Day 2 的必做项。

您还没有登录,请登录后发表评论。