-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Buildkite OIDC to Fulcio #890
Conversation
There’s a few options for onboarding. One is to conform to one of the existing types. Given you’re a CI platform, it’s likely the “GitHub” type is the closest. This would require just configuration change, but unless you support the same claims as GitHub Actions, we would need code changes. I’m curious what your thoughts are on the attached issue. |
We’d also want to run through the list of requirements for providers, #397. This is a WIP list, so some might not be applicable to CI. Let’s continue the conversation on the issue, I’ll copy this over. |
Yeah we don't support exactly the same claims as github, but it feels like there would be common ground. We do include a unique immutable id (job_id, a uuid) which identifies a command and where it was run, and how it fits amongst a build (made of many jobs), pipeline and organization. Other provenance can be derived from that unique identifier, even if other symbolic attributes change over time. Perhaps that's the only hard requirement — there must be a unique immutable identifier for the runtime environment, and that identifier can be mapped to full provenance by the identity provider. I think this would be the job_id on GitHub Actions, for example. I'm sure there are other commonly useful attributes, but they get pretty usage-specific pretty quickly. I'll chime in on the other issue. |
Once this lands, we should talk about adding ambient authentication to cosign for this so the |
09ad89f
to
1bdd878
Compare
Codecov Report
@@ Coverage Diff @@
## main #890 +/- ##
==========================================
- Coverage 55.63% 53.97% -1.67%
==========================================
Files 38 38
Lines 2333 2366 +33
==========================================
- Hits 1298 1277 -21
- Misses 939 996 +57
+ Partials 96 93 -3
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
Sweet, I’ll take a look tomorrow or Friday! Could you take a look at the questions in #397 and answer them? In particular, confirming the well known endpoint, configurable audiences, secure key management of signing keys, uptime/SLO? |
Also, what’s the best way to test this out? |
Thanks! I'll address the specific questions you copied in here:
Our issuer is https://agent.buildkite.com, which is the private API endpoint used by the Buildkite Agent (which runs jobs, and exchanges private credentials for OIDC identity tokens), and there are matching well-known endpoints here:
The default audience is In the original issue I included an example for the direct API which shows how to vary the audience: curl -s -X POST -H "Authorization: Token $${BUILDKITE_AGENT_ACCESS_TOKEN}" \
"$${BUILDKITE_AGENT_ENDPOINT:-https://agent.buildkite.com/v3}/jobs/$${BUILDKITE_JOB_ID}/oidc/tokens" \
--data '{"audience":"sigstore"}' but we expect folks to use a new subcommand of the buildkite agent (available within jobs) to ask for these tokens, which accepts an audience argument, like this: buildkite-agent oidc request-token --audience sigstore
Our keys are stored with other production secrets and treated similarly — encrypted at rest, encrypted in transit, only available within the application environment used for signing, etc. The current cert has an expiry in 2 years, but I expect we'll rotate yearly. We're still refining these details for release.
We have a 99.95% uptime SLO. |
In terms of testing, I started a local ephemeral instance of fulcio with the buildkite provider configured using the instructions here: https://github.com/sigstore/fulcio/blob/main/docs/setup.md
Then I ran a local buildkite agent on my mac: https://buildkite.com/docs/agent/v3/macos and had it run a pipeline like this:
again referencing the local fulcio usage example from: https://github.com/sigstore/fulcio/blob/main/docs/setup.md#calling-the-fulcio-api and .. it works: The pushed signatures are visible here: https://hub.docker.com/r/sj26/test/tags This is a really rough get-it-working example, but it's enough to give me confidence that it's the right direction. |
} | ||
|
||
func JobPrincipalFromIDToken(ctx context.Context, token *oidc.IDToken) (identity.Principal, error) { | ||
var claims struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have an example of a token? Curious to see what claims are available and what examples values look like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah! Here's an example payload from our test suite:
{
"iss": "http://agent.buildkite.localhost",
"sub": "organization:acme-inc:pipeline:super-duper-app:ref:refs/heads/main:commit:9f3182061f1e2cca4702c368cbc039b7dc9d4485:step:",
"aud": "http://buildkite.localhost/acme-inc",
"iat": 1669014898,
"nbf": 1669014898,
"exp": 1669015198,
"organization_slug": "acme-inc",
"pipeline_slug": "super-duper-app",
"build_number": 1,
"build_branch": "main",
"build_commit": "9f3182061f1e2cca4702c368cbc039b7dc9d4485",
"step_key": "build",
"job_id": "0184990a-477b-4fa8-9968-496074483cee",
"agent_id": "0184990a-4782-42b5-afc1-16715b10b8ff"
}
pkg/identity/buildkite/principal.go
Outdated
var claims struct { | ||
OrganizationSlug string `json:"organization_slug"` | ||
PipelineSlug string `json:"pipeline_slug"` | ||
BuildNumber int `json:"build_number"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
build_number
is not one of the claims present in https://agent.buildkite.com/.well-known/openid-configuration - this might just be an error in the OIDC configuration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I added it to the OIDC token but forgot the discovery endpoint, will update.
pkg/identity/buildkite/principal.go
Outdated
return &jobPrincipal{ | ||
subject: token.Subject, | ||
issuer: token.Issuer, | ||
url: fmt.Sprintf("https://buildkite.com/%s/%s/builds/%d#%s", claims.OrganizationSlug, claims.PipelineSlug, claims.BuildNumber, claims.JobID), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm interested in your thoughts on adding build_branch
as part of the URL too. What we're looking for is a representation of a trust boundary. As a verifier, what do I want to know in order to determine if I trust this identity for a build? Going through the claims:
- organization_slug - A verifier needs to know which org the repo is under (Do you have anything that represents repositories, or is that in organization_slug?)
- pipeline - I'm not familiar with the term, but I'll assume this is something like GH's workflow name, so you'd want to know what build pipeline ran (correct me if I'm wrong)
- build_branch - This seems important to me, to know if your workflow ran from a trusted branch. However, you could also argue this is provenance (@asraa, thoughts?)
- build_number/job_id - Does this need to be a part of the identity? Is it expected a verifier would know the build_number? Same question with job_id - These seem more like provenance than identity
- agent_id - I noticed this in the configuration - What does this represent?
cc @asraa who has thoughts about this for other CI platforms too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it feels important to identify the job and agent — the actual environment in which an artifact was produced — for provenance. The context of a build in CI/CD is always a product of more than a few simple inputs, so the only way to truly represent the signing principle is as the full job. But for verification you'd probably want to interrogate organization_slug
, pipeline_slug
, and maybe build_branch
. These are all available in the subject or subject alt, but are partial and so awkward. Having generic attributes for these would be great. Git Commit
and Git Ref
feel like good common attributes. I'm not sure what a common name for a GitHub Workflow or Buildkite Pipeline would be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build_branch
is at least committed to the subject name. I think that even though it's "awkward" because it's present inside the whole string, I think it still suffices (we still do the same style parsing for GitHub refs based on the subject for that too)
pkg/identity/buildkite/principal.go
Outdated
return &jobPrincipal{ | ||
subject: token.Subject, | ||
issuer: token.Issuer, | ||
url: fmt.Sprintf("https://buildkite.com/%s/%s/builds/%d#%s", claims.OrganizationSlug, claims.PipelineSlug, claims.BuildNumber, claims.JobID), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One comment I have is that I think that the build_number is extraneous: regardless of build number you could identify the build occurring, and the builder could always sign off on the build_number.
I otherwise think that using organization_slug/pipeline/job_id would be good, but that perhaps the build_branch should as well. this would make it align nicely with job_workflow_ref
from GitHUb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sj26 Could you clarify the difference between pipeline and job id? I wonder if job_id is too fine grained, unless it's static for multiple runs of the workflow.
+1 on using build_branch as well. One open question is if we should also include build_commit somewhere in the certificate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
job_id would be too fine grained for what you're thinking of here I think.
There are cases where you might not care about branch.
I think the ref in job_workflow_ref
is about the source workflow configuration file, not the ref of the source code being built. Our equivalent is probably a pipeline, like https://buildkite.com/buildkite/docs
for instance. Our steps are dynamic, so there is no concrete configuration to point at.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is BuildNumber correct here? This seems like provenance and not identity. Same question with JobID.
I think BuildBranch is part of the trust boundary, because you may have different protection levels for different branches. Used in conjunction with a commit hash (build_commit
?), you have an immutable reference to a job configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pipeline, branch and commit are not enough for an immutable reference to job configuration because our configurations are dynamic and can be modified in ways not contained within the pipeline or repository, which is why I've referenced a particular job (which is itself part of a build, hence the build number — that's an actual URL where that job is accessible, and the final configuration and results can be interrogated).
If there were common branch and commit attributes/OIDs we could probably those as well, for use cases where folks want to rely on branch and commit values? Or instead, if you think a reference to the concrete build and job are not desired in this case.
But shouldn't provenance be part of the signature as well? I've started there based on what seems to be available, and figured more attributes could be added as a common schema for CI providers emerges.
This looks like a good first pass to me. What's missing to get it in? |
Hey, sorry for the delay getting it reviewed. The remaining parts to review are: |
The state right now seems like
I think this is fine, although wordy, and justifiably because it's taking the sub claim anyway. In the GH world, the subjects also something fairly similar, identifying the reusable workflow like org/repo/workflow/path@ref. |
FWIW, having the commit as part of the subject makes authoring policies for these policies more awkward than other systems, where there's generally a relatively fixed identity for the CI job, but additional OIDs for things like the commit. To accept a signature from this identity, folks will have to effectively use something like |
Totally agree @mattmoor, but in OIDC relying parties like AWS IAM there is only the ability to make assertions on the subject, not other claims or oid-equivalents, so all the details need to be crammed in there so that string matching is possible. |
I pushed up docs for the buildkite agent oidc command line the other day here: The instructions I added earlier demonstrate how to use that command to produce an OIDC token and feed it into cosign with a local fulcio: The only missing bits are that you need to sign up for a (free) buildkite account to create a pipeline: I'm happy to jump on a call to walk anybody through the details: |
Yeah, I'm aware. FWIW, that presents similar challenges on the IAM trust relationship side to what I mention on the signature verification side. However, I guess that's irrelevant since this is neither an email or URL, which are the two forms of ~identity that fulcio/cosign generally use in certificates today, so I guess the question I have is what we intend that sense of identity to be for BuildKite tokens? Is it this (from your unit tests)?
I'm wondering what As context, we have customers that are keen to use BuildKite with |
Hm, yeah, I imagine a consume of a container image signed by a github action probably wouldn't be using their subject format, but would want to do something like:
so the equivalent would theoretically be something like:
The current OIDs don't allow encoding this sort of information. I guess that's where the conversation about standardising the OIDs populated by CI providers (#754) becomes interesting. Could there be a generic repository/workflow/pipeline identity attribute? Same for git ref and sha? |
My point is a little more subtle... The "subject" used for actions (that shows up in For Github (Kubernetes does something similar), we synthesize a URL from the issuer = "https://token.actions.githubusercontent.com"
subject = "https://github.com/chainguard-images/images/.github/workflows/release.yaml@refs/heads/main" Again, note: the ~sense of the "subject" here is the sigstore subject, not the token subject. |
The subject from the oidc token is used, the one full of colons like github's, but there's a subject alt name which is the url to the specific job which would be unique every time a command is run:
Should this instead be the pipeline, the container of all builds and jobs which have or could run that pipeline?
Or would you have a way to match a subject prefix to do the same from the more-specific subject? |
@sj26 I think that for the SAN that I think that if the SAN is distinct with every pipeline execution, then verifying BuildKite signatures is going to end up be different than most other systems in downstream policy tools like I'd be happy to hop on a call sometime next week when I'm back from Holiday if it'd be helpful. I'm (normally) on the US west coast, and you can reach me at |
Thanks for the input, and sorry for the delay — I had a holiday! I've updated the SAN to be the pipeline URL, so it will be consistent across builds and useful for writing policies. Branch/commit/build/job/etc can be introduced using whatever common attributes for CI become available over time. |
I'll merge this in a day or so in case there's any more comments. cc @asraa @bobcallaway @mattmoor if you have any more comments |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sj26 Can you add a few tests to grpc_server_test, which tests e2e flows, and one other file?
- Add a test for the new issuer string - https://github.com/sigstore/fulcio/blob/main/pkg/config/config_test.go#L472
- Add a configuration test - https://github.com/sigstore/fulcio/blob/main/pkg/server/grpc_server_test.go#L174
- Add a test for issuing a certificate, like https://github.com/sigstore/fulcio/blob/main/pkg/server/grpc_server_test.go#L681-L812
@haydentherapper thanks for taking another look, I've added those tests. While in there, I noticed the github api test was conflating subject and url so I made them match the principal tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this looks great! Just a linter failure
@haydentherapper done, I think! |
Signed-off-by: Samuel Cochran <sj26@sj26.com>
This is a super basic principal which doesn't include any extensions yet, while waiting for what the common set of CI extensions might be. Signed-off-by: Samuel Cochran <sj26@sj26.com>
Co-authored-by: Hayden B <hblauzvern@gmail.com> Signed-off-by: Samuel Cochran <sj26@sj26.com>
The SAN should be a human-friendly subject which remains consistent across builds/jobs to be useful in setting policies. Signed-off-by: Samuel Cochran <sj26@sj26.com>
Signed-off-by: Samuel Cochran <sj26@sj26.com>
Signed-off-by: Samuel Cochran <sj26@sj26.com>
Signed-off-by: Samuel Cochran <sj26@sj26.com>
There was a failing test hidden in the depths, so I did a rebase to clean up slightly and include a fix. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your work on this! Ill get this merged in and rolled out to staging this week, then you can test against it. URL is fulcio.sigstage.dev, and you’ll need to initialize your cosign environment with a different TUF root - https://github.com/sigstore/cosign/blob/main/KEYLESS.md#usage-1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one nit on copyright dates but otherwise LGTM
Signed-off-by: Samuel Cochran <sj26@sj26.com>
@sj26 One thing I wanted to plug now that this has landed is that Your PR unblocks Happy to provide reviews/guidance, or elaborate on this further if you have questions. |
…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
…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 <james@buildkite.com>
Add Buildkite to the OIDC connect federation configuration. Fixes #889.
Summary
I'd like to be able to use Buildkite OIDC tokens to sign containers produced by my builds. There are more details in #889.
Our OIDC token support is still under development, but it's possible to try it out now by signing up for Buildkite and running a pipeline like:
I wasn't sure about the "type", so I followed the example of "github-workflow" — our tokens represent one "buildkite-job" which is a process being run on a host somewhere as part of a build pipeline.
Release Note
Documentation
I couldn't find specific docs for each OIDC provider, so I don't think any updates are required.