WEBKT

GitHub Actions 实战:五分钟配置 Cosign Keyless 无密钥镜像签名

2 0 0 0

在软件供应链攻击频发的今天,为容器镜像进行签名已成为生产环境的标配。传统的签名方式通常需要开发者手动管理私钥(如存储在 GitHub Secrets 中),这不仅存在泄露风险,还带来了密钥轮转的运维负担。

Sigstore 项目下的 Cosign 提供的 Keyless(无密钥)模式彻底改变了这一现状。它利用 GitHub Actions 提供的 OIDC 身份令牌,结合 Sigstore 的 Fulcio(CA 服务)和 Rekor(透明日志),实现了“无需管理密钥即可完成签名”的优雅流程。

本文将手把手带你快速在 GitHub Actions 中集成这一方案。

一、 核心原理简述

在 Keyless 模式下:

  1. GitHub Actions 为运行的工作流生成一个短效的 OIDC ID Token
  2. Cosign 提取该 Token 并发送给 Sigstore 的 Fulcio
  3. Fulcio 验证身份后,签发一个有效期仅几分钟的短效证书。
  4. Cosign 使用该证书签署镜像,并将记录上传到 Rekor 公开透明账本。
  5. 验证者只需通过 Rekor 即可核实该镜像确实是由特定的 GitHub Repository 生成的。

二、 前置要求

  1. 一个托管在 GitHub 的项目。
  2. 使用 GitHub Container Registry (ghcr.io) 或 Docker Hub 存储镜像。
  3. 工作流文件位于 .github/workflows/ 目录下。

三、 实战配置步骤

以下是一个完整的 GitHub Actions YAML 示例。

1. 设置权限 (核心步骤)

要实现 Keyless 签名,GitHub 工作流必须具备获取 id-token 的权限,否则 Cosign 无法向 Fulcio 申请证书。

permissions:
  contents: read
  packages: write
  id-token: write # 必须:用于获取 OIDC 令牌

2. 编写完整工作流

name: Docker Image CI & Sign

on:
  push:
    tags: [ 'v*.*.*' ]

jobs:
  build-and-sign:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      id-token: write 

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      # 安装 Cosign 工具
      - name: Install Cosign
        uses: sigstore/cosign-installer@v3.5.0

      # 登录到 GitHub Container Registry
      - name: Log into registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      # 构建并推送镜像
      - name: Build and push Docker image
        id: build-and-push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}/my-app:latest

      # 使用 Cosign 进行 Keyless 签名
      - name: Sign the published Docker image
        env:
          DIGEST: ${{ steps.build-and-push.outputs.digest }}
          TAGS: ghcr.io/${{ github.repository }}/my-app:latest
        run: |
          cosign sign --yes "${TAGS}@${DIGEST}"

四、 关键点解析

  • cosign-installer: 官方提供的 Action,用于在 Runner 中快速安装 cosign 环境。
  • cosign sign --yes: 在非交互模式下,--yes 参数会自动处理 Keyless 流程。它会自动探测当前环境是否在 GitHub Actions 中,并自动请求 OIDC 令牌。
  • 不可篡改性: 建议使用 DIGEST(镜像摘要)而不是 TAG 来进行签名,这能防止在签名过程中镜像被篡改或覆盖。

五、 如何验证签名?

签名完成后,任何人都可以验证该镜像的真实性,且不需要你的公钥。

使用以下命令验证:

cosign verify ghcr.io/your-username/your-repo/my-app:latest \
  --certificate-identity-regexp https://github.com/your-username/your-repo/.github/workflows/ \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

验证逻辑:

  • --certificate-identity: 检查证书中的身份是否匹配指定的 GitHub 工作流路径。
  • --certificate-oidc-issuer: 确保证书是由 GitHub 的 OIDC 发行版签发的。

六、 常见问题排查

  1. Permission Denied: 检查你的 permissions 部分。如果漏掉了 id-token: write,Cosign 会提示无法获取 credential。
  2. Private Repo: 默认情况下,Keyless 签名的元数据会上传到 Sigstore 的公共 Rekor 服务器。如果你是私有商业项目,且不想公开任何构建元数据,请考虑部署私有的 Sigstore 实例。
  3. Docker Hub 支持: 如果你推送到 Docker Hub,Cosign 的操作完全一致,只需更换 login-action 的地址即可。

总结

通过 GitHub Actions 结合 Cosign Keyless 模式,我们可以在不接触任何密钥的情况下,为镜像建立起强大的信任链。这不仅简化了 CI/CD 的维护工作,更显著提升了项目分发的安全性。建议所有开源项目的维护者都尽早开启此功能。

DevOps探路者 Cosign云原生安全

评论点评