actions / attest-build-provenance

Action for generating build provenance attestations for workflow artifacts
MIT License
288 stars 216 forks source link

Add instructions for verifying the attestations using `cosign` #162

Open iainlane opened 1 month ago

iainlane commented 1 month ago

Thanks for this action and all the work on the whole infrastructure setup. 🙂

I'm just starting to attempt to generate SBOM and provenance attestations (this part using this action), sign my images and push them to the registry. I've been working with a test image of one of our projects: grafana/wait-for-github:iainlane-attestation-test (here is the latest build log).

gh attestation verify works:

laney@melton> GH_DEBUG=1 gh attestation verify -R grafana/wait-for-github oci://grafana/wait-for-github:iainlane-attestation-test
Loaded digest sha256:360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662 for oci://grafana/wait-for-github:iainlane-attestation-test
Fetching attestations for artifact digest sha256:360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662

* Request at 2024-07-26 08:59:09.609515 +0100 BST m=+0.939990460
* Request to https://api.github.com/repos/grafana/wait-for-github/attestations/sha256:360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662?per_page=30
* Request took 254.894208ms
Loaded 1 attestation from GitHub API
Verifying attestation 1/1 against the configured Sigstore trust roots
Attempting verification against issuer "sigstore.dev"
SUCCESS - attestation signature verified with "sigstore.dev"

✓ Verification succeeded!

sha256:360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662 was attested by:
REPO                     PREDICATE_TYPE                  WORKFLOW                                       
grafana/wait-for-github  https://slsa.dev/provenance/v1  .github/workflows/build.yml@refs/pull/130/merge

What I'm wondering is how to do the same using cosign. In the build log I can see:

Attestation uploaded to registry
index.docker.io/grafana/wait-for-github@sha256:1fdab504a0700ee12b2537d75d329bee920a88a1f96ee5b20fa9cb749bfc213b

And indeed I can see this reference with e.g. docker buildx imagetools inspect. But when I try to verify or download the attestation I end up with a 404:

laney@melton> cosign --verbose download attestation grafana/wait-for-github:iainlane-attestation-test
# ...
2024/07/26 09:02:35 --> GET https://index.docker.io/v2/grafana/wait-for-github/manifests/sha256-360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662.att
2024/07/26 09:02:35 GET /v2/grafana/wait-for-github/manifests/sha256-360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662.att HTTP/1.1
Host: index.docker.io
User-Agent: cosign/v2.3.0 (darwin; arm64) go-containerregistry/v0.20.1
Accept: application/vnd.docker.distribution.manifest.v1+json,application/vnd.docker.distribution.manifest.v1+prettyjws,application/vnd.docker.distribution.manifest.v2+json,application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.oci.image.index.v1+json
Authorization: <redacted>
Accept-Encoding: gzip

2024/07/26 09:02:35 <-- 404 https://index.docker.io/v2/grafana/wait-for-github/manifests/sha256-360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662.att (95.813542ms)
2024/07/26 09:02:35 HTTP/1.1 404 Not Found
Content-Length: 169
Content-Type: application/json
Date: Fri, 26 Jul 2024 08:02:35 GMT
Docker-Distribution-Api-Version: registry/2.0
Docker-Ratelimit-Source: redacted
Strict-Transport-Security: max-age=31536000

{"errors":[{"code":"MANIFEST_UNKNOWN","message":"manifest unknown","detail":"unknown tag=sha256-360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662.att"}]}

Error: found no attestations
main.go:74: error during command execution: found no attestations

With my naive understanding, it looks like it's not being found from the tag's manifest. Am I doing something wrong, or is this not expected to work currently?

To make this an issue rather than a question --- if this is possible to do, it'd be a nice example to have in the README 🙂

bdehamer commented 1 month ago

It isn't yet possible to use cosign to verify the attestations generated by this action.

However, this is something that we're actively working on. The incompatibility largely stems from the fact that we're using the Sigstore Bundle format for packaging the attestation, but cosign doesn't yet have support for bundles.

There's a PR open already for adding bundle support to the verify-blob and verify-blob-attestation sub commands. Keep an eye out for additional PRs in the near future.

At soon as this work is complete we will definitely update our docs to show examples of using cosgin to verify our build provenance attestations.

iainlane commented 1 month ago

Cheers for the hint @bdehamer. I saw the linked PR got merged and it prompted me to have a go at this, mainly for curiosity. I didn't actually manage to make it work with cosign, but https://github.com/sigstore/sigstore-go did work in the end 👍

Here's how, for anyone who is interested I'm sure this isn't the ideal way, even with current tooling, but I was happy to have muddled through. 🙂 ```console $ BUNDLE_MANIFEST_DIGEST=$(oras discover --format json docker.io/grafana/wait-for-github:iainlane-attestation-test | jq --raw-output '.manifests[] | select (.annotations["dev.sigstore.bundle.predicateType"] == "https://slsa.dev/provenance/v1").digest') $ echo ${BUNDLE_MANIFEST_DIGEST} sha256:1fdab504a0700ee12b2537d75d329bee920a88a1f96ee5b20fa9cb749bfc213b $ eval $(jq -r ' "SIGSTORE_BUNDLE_DIGEST=\(.layers[0].digest | @sh)\n" + "IMAGE_INDEX_DIGEST=\(.subject.digest | @sh)" ' <(oras manifest fetch "docker.io/grafana/wait-for-github:iainlane-attestation-test@${BUNDLE_MANIFEST_DIGEST}")) $ echo ${SIGSTORE_BUNDLE_DIGEST} sha256:f239c387b258c8bf98c9796af9d5617dba25cf898c42893ef64628c116a84df9 $ echo ${IMAGE_INDEX_DIGEST} sha256:360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662 $ oras blob fetch --output ~/temp/bundle docker.io/grafana/wait-for-github:iainlane-attestation-test@${SIGSTORE_BUNDLE_DIGEST} ✓ Downloaded application/octet-stream 10/10 kB 100.00% 0s └─ sha256:f239c387b258c8bf98c9796af9d5617dba25cf898c42893ef64628c116a84df9 $ go run cmd/sigstore-go/main.go -artifact-digest "${IMAGE_INDEX_DIGEST#sha256:*}" -expectedIssuer https://token.actions.githubusercontent.com -expectedSAN https://github.com/grafana/wait-for-github/.github/workflows/build.yml@refs/pull/130/merge ~/temp/bundle Verification successful! ``` ```json { "mediaType": "application/vnd.dev.sigstore.verificationresult+json;version=0.1", "statement": { "_type": "https://in-toto.io/Statement/v1", "predicateType": "https://slsa.dev/provenance/v1", "subject": [ { "name": "index.docker.io/grafana/wait-for-github", "digest": { "sha256": "360587aa9781d76b825f65f56f40738ceaf79b5ef5996b4b121c7544c9f7d662" } } ], "predicate": { "buildDefinition": { "buildType": "https://actions.github.io/buildtypes/workflow/v1", "externalParameters": { "workflow": { "path": ".github/workflows/build.yml", "ref": "refs/pull/130/merge", "repository": "https://github.com/grafana/wait-for-github" } }, "internalParameters": { "github": { "event_name": "pull_request", "repository_id": "577382444", "repository_owner_id": "7195757", "runner_environment": "github-hosted" } }, "resolvedDependencies": [ { "digest": { "gitCommit": "2cbda36b5cec403a6ae33ee2aec33e52d28a9905" }, "uri": "git+https://github.com/grafana/wait-for-github@refs/pull/130/merge" } ] }, "runDetails": { "builder": { "id": "https://github.com/grafana/wait-for-github/.github/workflows/build.yml@refs/pull/130/merge" }, "metadata": { "invocationId": "https://github.com/grafana/wait-for-github/actions/runs/10106494744/attempts/7" } } } }, "signature": { "certificate": { "certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev", "subjectAlternativeName": "https://github.com/grafana/wait-for-github/.github/workflows/build.yml@refs/pull/130/merge", "issuer": "https://token.actions.githubusercontent.com", "githubWorkflowTrigger": "pull_request", "githubWorkflowSHA": "2cbda36b5cec403a6ae33ee2aec33e52d28a9905", "githubWorkflowName": "Build", "githubWorkflowRepository": "grafana/wait-for-github", "githubWorkflowRef": "refs/pull/130/merge", "buildSignerURI": "https://github.com/grafana/wait-for-github/.github/workflows/build.yml@refs/pull/130/merge", "buildSignerDigest": "2cbda36b5cec403a6ae33ee2aec33e52d28a9905", "runnerEnvironment": "github-hosted", "sourceRepositoryURI": "https://github.com/grafana/wait-for-github", "sourceRepositoryDigest": "2cbda36b5cec403a6ae33ee2aec33e52d28a9905", "sourceRepositoryRef": "refs/pull/130/merge", "sourceRepositoryIdentifier": "577382444", "sourceRepositoryOwnerURI": "https://github.com/grafana", "sourceRepositoryOwnerIdentifier": "7195757", "buildConfigURI": "https://github.com/grafana/wait-for-github/.github/workflows/build.yml@refs/pull/130/merge", "buildConfigDigest": "2cbda36b5cec403a6ae33ee2aec33e52d28a9905", "buildTrigger": "pull_request", "runInvocationURI": "https://github.com/grafana/wait-for-github/actions/runs/10106494744/attempts/7", "sourceRepositoryVisibilityAtSigning": "public" } }, "verifiedTimestamps": [ { "type": "Tlog", "uri": "TODO", "timestamp": "2024-07-26T08:24:45+01:00" } ], "verifiedIdentity": { "subjectAlternativeName": { "subjectAlternativeName": "https://github.com/grafana/wait-for-github/.github/workflows/build.yml@refs/pull/130/merge" }, "issuer": { "issuer": "https://token.actions.githubusercontent.com" } } } ```