Skip to content

Commit

Permalink
Update Buildkite issuer to include some of the new certificate extens…
Browse files Browse the repository at this point in the history
…ions

The Buildkite Issuer was added in sigstore#890, prior to the efforts to
standardise certificate extensions for CI providers, and sigstore#1074 calls for
the Buildkite issuer to be updated to use the new extensions (where
applicable).

This is an early attempt to make those changes.

I've added the extensions that make the most sense in a Buildkite
context, like RunInvocationURI, RunnerEnvironment and
SourceRepositoryDiget. Many of the other extensions don't apply because
we're not a code host as well, or need further discussion.

I have not added tests yet. This is my first contribution to fulcio and
I'm keen to confirm I'm heading in the right direction before adding
tests. However, I have tested this locally with a Buildkite agent and
OIDC token, and the certificate was issued as expected.

Using `git cat-file commit HEAD` and piping it through `openssl pkcs7
-print -print_certs -text`, the extensions section looks like this:

    X509v3 extensions:
        X509v3 Key Usage: critical
            Digital Signature
        X509v3 Extended Key Usage:
            Code Signing
        X509v3 Subject Key Identifier:
            19:9E:E7:53:4D:F6:65:D4:23:9D:60:21:B8:F3:12:80:FD:11:30:7F
        X509v3 Authority Key Identifier:
            8A:3E:9E:34:19:F7:32:18:3D:2A:1B:F9:5F:60:29:24:0F:70:0B:B4
        X509v3 Subject Alternative Name: critical
            URI:https://buildkite.com/yob-opensource/oidc-signing-experiment
        1.3.6.1.4.1.57264.1.1:
            https://agent.buildkite.com
        1.3.6.1.4.1.57264.1.8:
            ..https://agent.buildkite.com
        1.3.6.1.4.1.57264.1.11:
            ..self-hosted
        1.3.6.1.4.1.57264.1.13:
            .(5242de9e5c2ca164cb3dfc500fb605f0b8b86763
        1.3.6.1.4.1.57264.1.21:
            .mhttps://buildkite.com/yob-opensource/oidc-signing-experiment/builds/35%230189cb29-62fa-41af-8641-62e1d6c5edfd

Fixes sigstore#1074

Signed-off-by: James Healy <[email protected]>
  • Loading branch information
yob committed Aug 6, 2023
1 parent 04e4ac9 commit 3025b65
Showing 1 changed file with 61 additions and 12 deletions.
73 changes: 61 additions & 12 deletions pkg/identity/buildkite/principal.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ package buildkite
import (
"context"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
"net/url"
"strconv"

"github.com/coreos/go-oidc/v3/oidc"
"github.com/sigstore/fulcio/pkg/certificate"
Expand All @@ -35,16 +36,38 @@ type jobPrincipal struct {
// https://agent.buildkite.com/.well-known/openid-configuration
issuer string

// The URL of the pipeline, the container of many builds. This will be
// set as a human-friendly SubjectAlternativeName URI in the certificate.
// Buildkite's domain
url string

// Unique identifier for a Buildkite customer
organizationSlug string

// Unique identifier (within the scope of an OrganizationSlug) for a container of many builds.
pipelineSlug string

// Incrementing number within each pipeline
buildNumber int64

// The commit sha being tested by a build
buildCommit string

// UUID that identifies a single unique execution within a build
jobId string

// Did the job run in a cloud hosted environment or self hosted by the customer. All
// Buildkite jobs execute on self hosted agents, so this will always be `self-hosted`
runnerEnvironment string
}

func JobPrincipalFromIDToken(_ context.Context, token *oidc.IDToken) (identity.Principal, error) {
var claims struct {
OrganizationSlug string `json:"organization_slug"`
PipelineSlug string `json:"pipeline_slug"`
OrganizationSlug string `json:"organization_slug"`
PipelineSlug string `json:"pipeline_slug"`
BuildNumber json.Number `json:"build_number"`
BuildCommit string `json:"build_commit"`
JobId string `json:"job_id"`
}

if err := token.Claims(&claims); err != nil {
return nil, err
}
Expand All @@ -57,10 +80,29 @@ func JobPrincipalFromIDToken(_ context.Context, token *oidc.IDToken) (identity.P
return nil, errors.New("missing pipeline_slug claim in ID token")
}

buildNumber, err := claims.BuildNumber.Int64()
if err != nil {
return nil, errors.New("error parsing build_number claim in ID token")
}

if claims.BuildCommit == "" {
return nil, errors.New("missing build_commit claim in ID token")
}

if claims.JobId == "" {
return nil, errors.New("missing job_id claim in ID token")
}

return &jobPrincipal{
subject: token.Subject,
issuer: token.Issuer,
url: fmt.Sprintf("https://buildkite.com/%s/%s", claims.OrganizationSlug, claims.PipelineSlug),
subject: token.Subject,
issuer: token.Issuer,
url: "https://buildkite.com",
organizationSlug: claims.OrganizationSlug,
pipelineSlug: claims.PipelineSlug,
buildNumber: buildNumber,
buildCommit: claims.BuildCommit,
jobId: claims.JobId,
runnerEnvironment: "self-hosted",
}, nil
}

Expand All @@ -69,16 +111,23 @@ func (p jobPrincipal) Name(_ context.Context) string {
}

func (p jobPrincipal) Embed(_ context.Context, cert *x509.Certificate) error {
// Set SubjectAlternativeName to the pipeline URL on the certificate
parsed, err := url.Parse(p.url)
baseURL, err := url.Parse(p.url)
if err != nil {
return err
}
cert.URIs = []*url.URL{parsed}

pipelineUrl := baseURL.JoinPath(p.organizationSlug, p.pipelineSlug)
jobUrl := baseURL.JoinPath(p.organizationSlug, p.pipelineSlug, "builds", strconv.FormatInt(p.buildNumber, 10)+"#"+p.jobId)

// Set SubjectAlternativeName to the pipeline URL on the certificate
cert.URIs = []*url.URL{pipelineUrl}

// Embed additional information into custom extensions
cert.ExtraExtensions, err = certificate.Extensions{
Issuer: p.issuer,
Issuer: p.issuer,
RunInvocationURI: jobUrl.String(),
RunnerEnvironment: p.runnerEnvironment,
SourceRepositoryDigest: p.buildCommit,
}.Render()
if err != nil {
return err
Expand Down

0 comments on commit 3025b65

Please sign in to comment.