在经历了 SolarWinds 供应链污染、XZ-utils 核心维护者后门注入等一系列触目惊心的全球网络安全事件后,开源软件的交付信任体系正在经历一场深刻的信任重构。
很多开源作者写好了一个 CLI 工具,在 GitHub Actions 里交叉编译出针对 Linux、macOS 和 Windows 的二进制包,打包成tar.gz挂在 Release 页面供用户下载。在这个看似自然的流转环节中,隐藏着一个巨大的软件供应链信任黑洞:
终端用户凭什么相信,从 GitHub Release 下载下来的二进制文件,就是由公开的源代码在纯净环境中构建出来的?
如果维护者的 GitHub 账号被黑、个人访问令牌(PAT)泄露,攻击者完全可以神不知鬼不觉地把 Release 附件里的二进制替换为一个捆绑了恶意挖矿或远控木马的恶意程序,而源码树上的代码依然显示得干干净净。
要彻底阻断这种篡改风险,必须为构建产物注入可公开密码学溯源的数字签名(Digital Signature)。
借助 Linux 基金会旗下 Sigstore 项目推出的Cosign工具与 GitHub Actions 的OIDC(OpenID Connect)无密钥签名机制(Keyless Signing),我们无需维护任何繁琐的传统 GPG 私钥,就能在 CI 流水线中实现真正透明、防篡改的软件供应链安全防线。
为什么传统的 GPG 签名走向没落
在过去,开源分发的标准做法是维护者用自己的 GPG 私钥对二进制进行本地签名,生成.asc文件。但在实际工程落地中,GPG 存在三个无法解决的硬伤:
- 私钥保管风险巨大:很多开发者把自己的 GPG 私钥导成明文 Base64 塞进 CI 的 Secrets 环境变量里。一旦第三方依赖或 CI 容器存在漏洞,私钥泄漏就意味着整个身份体系彻底崩溃。
- 信任锚点模糊(Web of Trust):用户下载了二进制和
.asc,但由于没有全球权威的集中验证体系,用户根本不知道去哪里核实这个 GPG 公钥是不是维护者本人的,导致 99% 的普通用户直接跳过核验步骤。 - 缺少防抵赖的时间戳透明存证:攻击者拿到私钥后,可以任意伪造两年前的旧版本签名,难以证明签名行为发生的具体环境与上下文。
Cosign 无密钥签名的破局机理
Cosign 提出的“无密钥签名(Keyless Signing)”并不是真的不需要密钥,而是消灭了需要人类长期保管的静态私钥:
- OIDC 短期身份认领:在 GitHub Actions 运行过程中,Runner 通过
id-token: write权限向 GitHub 的 OIDC 提供商申请一个有效期仅有数分钟的短期身份 JWT 令牌。该令牌中强绑定了当前代码仓库、Git 提交哈希、分支名称以及工作流文件路径。 - 临时公私钥对生成与签署:Cosign 在内存中生成临时的公私钥对,利用私钥对二进制构建产物(或校验和文件
checksums.txt)进行签名。 - 短期证书签发(Fulcio CA):Cosign 将公钥与 OIDC 令牌提交给 Sigstore 的权威证书颁发机构 Fulcio。Fulcio 验证无误后,签发一张绑定了该 GitHub 身份的极短效 X.509 数字证书(有效期通常只有 10 分钟)。
- 公开透明日志存证(Rekor Transparency Log):签名记录、证书与元数据被永久写入公开的、只允许追加且防篡改的默克尔树账本 Rekor 中。
- 私钥立焚:签名完成的瞬间,内存中的临时私钥被彻底销毁,物理层面杜绝了私钥泄露的可能性。
落地配置:GitHub Actions 自动化签名流水线
我们以一个典型的 Go 多架构二进制发布流水线为例,展示如何在构建完成后自动完成 Cosign 签名并将证据链回传到 Release。
在.github/workflows/release-signed.yml中编写:
name: Secure Build & Sign Release on: push: tags: - 'v*' # 核心权限声明:必须开启 id-token: write 以便获取 GitHub OIDC 身份令牌 permissions: contents: write id-token: write jobs: build-and-sign: runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Go 1.27.1 uses: actions/setup-go@v5 with: go-version: '1.27.1' # 1. 安装 Cosign CLI 工具(锁定官方 Action 签名) - name: Install Cosign uses: sigstore/cosign-installer@v3.5.0 # 2. 执行跨平台构建并生成 SHA256 校验和清单 - name: Build Binaries run: | mkdir -p dist GOOS=darwin GOARCH=arm64 go build -trimpath -o dist/mycli-darwin-arm64 GOOS=linux GOARCH=amd64 go build -trimpath -o dist/mycli-linux-amd64 GOOS=windows GOARCH=amd64 go build -trimpath -o dist/mycli-windows-amd64.exe cd dist sha256sum * > checksums.txt cat checksums.txt # 3. 核心步骤:使用 Cosign 对校验和清单进行无密钥签名 - name: Sign Artifacts with Cosign run: | # 针对包含所有二进制哈希的 checksums.txt 进行签名 cosign sign-blob \ --yes \ --bundle dist/checksums.txt.bundle \ dist/checksums.txt # 4. 发布带密码学签名的 GitHub Release - name: Upload Release Assets uses: softprops/action-gh-release@v2 with: files: | dist/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}终端用户的极速安全核验
当用户从 GitHub Release 下载了客户端二进制后,如何用单行命令验证其绝对纯正?
用户只需要安装cosign,并在终端运行一条校验命令:
cosign verify-blob \ --bundle checksums.txt.bundle \ --certificate-identity "https://github.com/my-org/my-project/.github/workflows/release-signed.yml@refs/tags/v1.0.0" \ --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ checksums.txt如果校验通过,Cosign 会在终端输出一条绿色的确凿信息:
Verified OK The following checks were performed each of them must pass: - The identity was verified: https://github.com/my-org/my-project/... - The certificate was verified against the Fulcio roots. - The transaction was verified against the Rekor log.随后,用户只需要本地比对刚下载二进制的哈希:
sha256sum -c checksums.txt --ignore-missing这一整套链条在密码学层面提供了坚不可摧的证明:
- 防恶意篡改:任何人试图把恶意木马伪装成同名二进制挂在 Release 上,其计算出的哈希必然与被签名的
checksums.txt不符; - 防身份冒领:
checksums.txt的签名证书是由 GitHub OIDC 权威证明的,确凿源自my-org/my-project的指定工作流,其他黑客即使拥有自己的 GitHub 账号也无法伪造出目标仓库的身份证书; - 可审计存证:全世界任何人都可以通过公开的 Rekor 账本检索到某年某月某分某秒该发布行为的全部透明元数据。
将数字签名沉淀为交付的标准动作,是开源项目从“作坊式代码分发”迈向“企业级可信供应链”最重要的成年礼。