Skip to content
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

Sign docker images using github attestations #8685

Merged
merged 1 commit into from
Jan 31, 2025

Conversation

mjpieters
Copy link
Contributor

@mjpieters mjpieters commented Oct 29, 2024

GitHub attestation signing uses the GitHub action ID token to retrieve an ephemeral code signing certificate from Fulcio, and store the signature in the Rekor transparency log.

Once an image has been successfully signed, you should be able to verify the signature with:

gh attestation verify --owner astral-uv oci://ghcr.io/astral-sh/uv:latest

Closes #8670

@mjpieters
Copy link
Contributor Author

A word of warning: I am not 100% certain I fully understand everything that the build-docker.yml pipeline does. I'd appreciate some feedback on how the digest of the final docker-republish image manifest is shared with the signing step, for example.

@zanieb
Copy link
Member

zanieb commented Oct 29, 2024

cc @samypr100 (as always, no obligation)

@samypr100
Copy link
Collaborator

samypr100 commented Oct 30, 2024

Note, I'm running a release job on my fork with these changes (9b162d) as I'd like to see it working E2E

https://github.com/samypr100/uv/actions/runs/11593512252

@samypr100
Copy link
Collaborator

@mjpieters

Note, I'm running a release job on my fork with these changes (9b162d) as I'd like to see it working E2E

https://github.com/samypr100/uv/actions/runs/11593512252

@mjpieters signed images are here https://github.com/samypr100/uv/pkgs/container/uv in case you'd like to verify the signatures are working as expected

@mjpieters
Copy link
Contributor Author

@mjpieters

Note, I'm running a release job on my fork with these changes (9b162d) as I'd like to see it working E2E
https://github.com/samypr100/uv/actions/runs/11593512252

@mjpieters signed images are here https://github.com/samypr100/uv/pkgs/container/uv in case you'd like to verify the signatures are working as expected

Excellent! I was working my way through understanding cargo dist in my own fork to try this out but hadn't gotten as far as actually seeing the workflow complete.

The first thing I notice is that the the .sig files are now obscuring the actual docker images on that page, which is a bummer.

The signatures are valid however!

% cosign verify ghcr.io/samypr100/uv:latest --certificate-identity-regexp='.*' --certificate-oidc-issuer-regexp='.*' --output-file /tmp/samypr100-uv-verify.json

Verification for ghcr.io/samypr100/uv:latest --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates

% jq -r '.[].optional.Subject' /tmp/samypr100-uv-verify.json
https://github.com/samypr100/uv/.github/workflows/build-docker.yml@refs/heads/docker-test
https://github.com/samypr100/uv/.github/workflows/build-docker.yml@refs/heads/docker-test
https://github.com/samypr100/uv/.github/workflows/build-docker.yml@refs/heads/docker-test

I'll have to see what can be done about those signature files in the registry.

@mjpieters
Copy link
Contributor Author

mjpieters commented Oct 30, 2024

So cosign pushes the signature as a new 'container' to the registry, tagged with sha256-$DIGEST.sig, with the signature contained in the manifest (see https://hackmd.io/@maelvls/how-cosign-uses-registries-to-store-signatures for an overview). It's these tags that are shown at the top of the page by ghcr.io, because it only ever shows the latest tags that have been pushed. The GH packages API and UI (which include the ghcr container registry) is very very sparse and doesn't offer any control over this.

I've looked at a number of other projects that use cosign to sign their containers (of which there are a very large number), and all the ones I looked at so far show these tags at the top. E.g. the Dependabot update bundler (a Github project!) has signature tags at the top. You see this on other registries too, e.g. the boxyhq jackson container on the docker hub.

I see but a few options here:

  • Accept that the signature tags are going to be pushed last and end up on top.
  • Experiment with the Github packages API to delete and then restore specific tags, to see if that puts them back at the top again.
  • Tag the containers after signing. Build and push the images, store the digests, sign the digests, then handle the version and 'latest' tags afterwards.
  • Tell cosign to push signatures to a separate registry. This would complicate verification of the images, I don't quite know how that'd work.
  • Decline this PR and forgo the security of having signed containers (which would be a huge pity, security is important, especially in supply-chain tools like uv).

@zanieb
Copy link
Member

zanieb commented Oct 30, 2024

We're already pushing the latest tag separately for this reason, can we push the signature before that final image tag?

@mjpieters
Copy link
Contributor Author

We're already pushing the latest tag separately for this reason, can we push the signature before that final image tag?

You push not only a tag, but a manifest as well. You'd have to separate the two steps; push the manifest without tags and then tag the digest of the manifest afterwards. It could be that you can retag the same digest; I'll try this out in my own fork registry.

@samypr100
Copy link
Collaborator

We're already pushing the latest tag separately for this reason, can we push the signature before that final image tag?

You push not only a tag, but a manifest as well. You'd have to separate the two steps; push the manifest without tags and then tag the digest of the manifest afterwards. It could be that you can retag the same digest; I'll try this out in my own fork registry.

I think the main issue here is how GH ranks packages and gives no control over the ordering to orgs/users of what's displayed, so we have to rely on these ordering hacks related to publishing.

@mjpieters
Copy link
Contributor Author

I think the main issue here is how GH ranks packages and gives no control over the ordering to orgs/users of what's displayed, so we have to rely on these ordering hacks related to publishing.

and from my experiments it is clear that it is order that the manifest were pushed to the registry that is used to list them. I tried deleting then restoring, and retagging (moving tags to another manifest then back again to the right manifest), and nothing shifts the signature from the top.

So the only thing that would work is to build and not push, sign (and let cosign push the signature manifest), and only then push the actual image manifest.

Comment on lines +42 to +43
# PRs from forks don't have access to secrets, disable this step in that case.
if: ${{ github.event.pull_request.head.repo.full_name == 'astral-sh/uv' }}
Copy link
Contributor Author

@mjpieters mjpieters Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added this in here so this workflow would at least build the docker image in this PR. The jobs otherwise would just fail with a Password required error.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, I think there's 3 other places in this file missing this conditional (if we're in favor of adding it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think there was any point as those 3 all are part of jobs that only run on release.

Copy link
Member

@zanieb zanieb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I might wait for @samypr100 to give it a look too.

.github/workflows/build-docker.yml Outdated Show resolved Hide resolved
.github/workflows/build-docker.yml Outdated Show resolved Hide resolved
Comment on lines +42 to +43
# PRs from forks don't have access to secrets, disable this step in that case.
if: ${{ github.event.pull_request.head.repo.full_name == 'astral-sh/uv' }}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note, I think there's 3 other places in this file missing this conditional (if we're in favor of adding it)

@samypr100
Copy link
Collaborator

samypr100 commented Jan 31, 2025

Good news is that this approach does keep the tags working as-is.

I tried it on a test run here https://github.com/samypr100/uv/actions/runs/13066181068

Which did keep the package versions tags intact https://github.com/samypr100/uv/pkgs/container/uv

Verifications also work!

$ gh attestation verify --owner samypr100 oci://ghcr.io/samypr100/uv:latest                          [23:14:17]
Loaded digest sha256:ebd769519134c730eb1af61ddd06a8f5629f5082d0c2d8f02a6d577fe4b2a9dc for oci://ghcr.io/samypr100/uv:latest
Loaded 1 attestation from GitHub API
✓ Verification succeeded!

sha256:ebd769519134c730eb1af61ddd06a8f5629f5082d0c2d8f02a6d577fe4b2a9dc was attested by:
REPO          PREDICATE_TYPE                  WORKFLOW
samypr100/uv  https://slsa.dev/provenance/v1  .github/workflows/build-docker.yml@refs/heads/cosign-docker-images

$ gh attestation verify --owner samypr100 oci://ghcr.io/samypr100/uv:0.5.26                          [23:14:23]
Loaded digest sha256:ebd769519134c730eb1af61ddd06a8f5629f5082d0c2d8f02a6d577fe4b2a9dc for oci://ghcr.io/samypr100/uv:0.5.26
Loaded 1 attestation from GitHub API
✓ Verification succeeded!

sha256:ebd769519134c730eb1af61ddd06a8f5629f5082d0c2d8f02a6d577fe4b2a9dc was attested by:
REPO          PREDICATE_TYPE                  WORKFLOW
samypr100/uv  https://slsa.dev/provenance/v1  .github/workflows/build-docker.yml@refs/heads/cosign-docker-images

$ gh attestation verify --owner samypr100 oci://ghcr.io/samypr100/uv:0.5                             [23:14:27]
Loaded digest sha256:ebd769519134c730eb1af61ddd06a8f5629f5082d0c2d8f02a6d577fe4b2a9dc for oci://ghcr.io/samypr100/uv:0.5
Loaded 1 attestation from GitHub API
✓ Verification succeeded!

sha256:ebd769519134c730eb1af61ddd06a8f5629f5082d0c2d8f02a6d577fe4b2a9dc was attested by:
REPO          PREDICATE_TYPE                  WORKFLOW
samypr100/uv  https://slsa.dev/provenance/v1  .github/workflows/build-docker.yml@refs/heads/cosign-docker-images

$ gh attestation verify --owner samypr100 oci://ghcr.io/samypr100/uv:python3.13-bookworm             [23:14:41]
Loaded digest sha256:5537896c0c8bd2351350d32dea35c590997c418e954fb5b31d6fcbfd96126562 for oci://ghcr.io/samypr100/uv:python3.13-bookworm
Loaded 1 attestation from GitHub API
✓ Verification succeeded!

sha256:5537896c0c8bd2351350d32dea35c590997c418e954fb5b31d6fcbfd96126562 was attested by:
REPO          PREDICATE_TYPE                  WORKFLOW
samypr100/uv  https://slsa.dev/provenance/v1  .github/workflows/build-docker.yml@refs/heads/cosign-docker-images

...

cosign uses the GitHub action ID token to retrieve an ephemeral code
signing certificate from Fulcio, and store the signature in the Rekor
transparency log.
@zanieb zanieb added releases Related to building and distributing release artifacts of uv security labels Jan 31, 2025
@zanieb zanieb merged commit 47f80a6 into astral-sh:main Jan 31, 2025
96 checks passed
@zanieb
Copy link
Member

zanieb commented Jan 31, 2025

Would you mind following with a brief guide on how to verify images in https://docs.astral.sh/uv/guides/integration/docker/ ?

@mjpieters mjpieters changed the title Sign docker images using cosign Sign docker images using github attestations Jan 31, 2025
@mjpieters mjpieters deleted the cosign-docker-images branch January 31, 2025 15:54
@mjpieters
Copy link
Contributor Author

Would you mind following with a brief guide on how to verify images in https://docs.astral.sh/uv/guides/integration/docker/ ?

I'll see what I can do!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
releases Related to building and distributing release artifacts of uv security
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FR: signed docker images
3 participants