From 6d5c1a44820cd58fdb3d7d68b3746845aa975451 Mon Sep 17 00:00:00 2001 From: James Healy Date: Mon, 3 Mar 2025 15:59:45 +1100 Subject: [PATCH] Add support for Buildkite OIDC tokens Add minimal support required to generate attestations for rubygems trusted publisher support in a Buildkite job 1. Teach sigstore-cli gem how to detect when it's running in a Buildkite job, and how to request a Buildkite OIDC token automatically 2. Teach sigstore gem how to parse an Buildkite OIDC token and generate the identity that fulcio uses for the x509 common name in certificates for Buildkite OIDC tokens With these changes, the following command works in a Buildkite job: bundle exec sigstore-cli sign --bundle bundle.sigstore.json I've tested this using the pipeline defined in a gist: https://gist.github.com/yob/512f079e0586d49d125547ad548c08b5 The pipeline is private [1], however an example rekor log for a bundle created using this branch is at [2] [1] https://buildkite.com/yob-opensource/sigstore-ruby-test/ [2] https://search.sigstore.dev/?logIndex=176164097 Signed-off-by: James Healy --- cli/lib/sigstore/cli/id_token.rb | 34 ++++++++++++++++++++++++++++++-- lib/sigstore/oidc.rb | 19 +++++++++++++++--- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/cli/lib/sigstore/cli/id_token.rb b/cli/lib/sigstore/cli/id_token.rb index 6877c44..6700a77 100644 --- a/cli/lib/sigstore/cli/id_token.rb +++ b/cli/lib/sigstore/cli/id_token.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "open3" + class Sigstore::CLI class IdToken include Sigstore::Loggable @@ -9,9 +11,9 @@ class AmbientCredentialError < Sigstore::Error def self.detect_credential [ - GitHub + GitHub, + Buildkite # detect_gcp, - # detect_buildkite, # detect_gitlab, # detect_circleci ].each do |detector| @@ -85,5 +87,33 @@ def call end end end + + class Buildkite < IdToken + def call + logger.debug { "looking for OIDC credentials" } + unless ENV["BUILDKITE"] + logger.debug { "environment doesn't look like Buildkite; giving up" } + return + end + + raise AmbientCredentialError, "buildkite-agent executable not found" unless buildkite_agent_found? + + request_token + end + + private + + def buildkite_agent_found? + _, status = Open3.capture2("which buildkite-agent") + status.success? + end + + def request_token + token, status = Open3.capture2("buildkite-agent oidc request-token --audience sigstore") + raise AmbientCredentialError, "error requesting Buildkite OIDC token" unless status.success? + + token.strip + end + end end end diff --git a/lib/sigstore/oidc.rb b/lib/sigstore/oidc.rb index 77918a9..6ee9e8f 100644 --- a/lib/sigstore/oidc.rb +++ b/lib/sigstore/oidc.rb @@ -20,7 +20,8 @@ module OIDC "https://accounts.google.com" => "email", "https://oauth2.sigstore.dev/auth" => "email", "https://oauth2.sigstage.dev/auth" => "email", - "https://token.actions.githubusercontent.com" => "job_workflow_ref" + "https://token.actions.githubusercontent.com" => "job_workflow_ref", + "https://agent.buildkite.com" => "pipeline_slug" }.freeze private_constant :KNOWN_OIDC_ISSUERS @@ -47,8 +48,20 @@ def initialize(raw_token) end @identity = @unverified_claims[identity_claim] - # https://github.com/sigstore/fulcio/blob/8311f93c01ea5b068a86d37c4bb51573289bfd69/pkg/identity/github/principal.go#L92 - @identity = "https://github.com/#{@identity}" if issuer == "https://token.actions.githubusercontent.com" + case issuer + when "https://token.actions.githubusercontent.com" + # https://github.com/sigstore/fulcio/blob/8311f93c01ea5b068a86d37c4bb51573289bfd69/pkg/identity/github/principal.go#L92 + @identity = "https://github.com/#{@identity}" + when "https://agent.buildkite.com" + # https://github.com/sigstore/fulcio/blob/ec8a1d7a96125a1a624b9e69df892f987bebc41c/config/identity/config.yaml#L241 + org_slug = @unverified_claims["organization_slug"] + if org_slug.nil? + raise Error::InvalidIdentityToken, + "identity token is missing required claim: organization_slug" + end + + @identity = "https://buildkite.com/#{org_slug}/#{@identity}" + end else @identity = @unverified_claims["sub"] end