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

Publish image attestations #1017

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft

Publish image attestations #1017

wants to merge 35 commits into from

Conversation

almet
Copy link
Member

@almet almet commented Nov 28, 2024

This pull request introduces a new workflow to automate the release of container images. Each night, the worflow builds a container image and uploads it to the container registry.

Note

Please note that, while signatures are involved in this workflow, the produced images are not signed by our release key. So, this is not to be confused with cosign sign commands, which are also used for independent container updates, but is different that the signatures involved when producing attestations.

Here is a list of the produced assets:

Verifying the validity of the attestation can be done with the following commands:

We should be able to also do it using sigstore-python and raw HTTP calls, but because the main goal here is to see how it works, I stopped there.

# First, login to the container registry.
# (We only need this because images are not publicly available yet)
# Enter "USERNAME" instead of your username
# and use your PAT as a password
regctl registry login ghcr.io

# Get the manifest from the latest tag
regctl manifest get --format raw-body ghcr.io/freedomofpress/dangerzone/dangerzone:latest > manifest.json

# The attestation for this manifest hash is available
# at the tag named "sha256-sha256(manifest.json)"
DIGEST="sha256-$(sha256sum manifest.json | awk '{ print $1 }')"
regctl artifact get ghcr.io/freedomofpress/dangerzone/dangerzone:${DIGEST} > bundle.json

# Finally verify that the attestation is the right one
cosign verify-blob-attestation --bundle bundle.json --new-bundle-format --certificate-oidc-issuer="https://token.actions.githubusercontent.com" --certificate-identity-regexp="^https://github.com/freedomofpress/dangerzone/.github/workflows/release-container-image.yml@refs/heads/test/image-publication-cosign" manifest.json

Related to #745

@almet almet force-pushed the test/image-publication-cosign branch from 87a65ce to 178364e Compare November 28, 2024 15:22
@almet
Copy link
Member Author

almet commented Nov 28, 2024

ℹ️ One of the things that isn't clear yet is why the attestations show up as sha256-$(sha256sum manifest.json) in the container registry.

It seems related to the Reference Types effort effort which might not be implemented by the GHCR.

@apyrgio apyrgio added this to the 0.9.0 milestone Dec 5, 2024
@almet
Copy link
Member Author

almet commented Dec 10, 2024

In order to get the attestation from the container registry, one need to do a few HTTP requests. We found these do be hard to put together, so here is a summary of the calls we had to do (testing on my personal user in order to have the container be public):

Step Endpoint Headers
1. Get authentication token https://ghcr.io/token None
2. Fetch manifest for tag https://ghcr.io/v2/almet/dangerzone/dangerzone/manifests/{tag} Accept: application/vnd.docker.distribution.manifest.v2+json
3. Get manifest list for attestation https://ghcr.io/v2/almet/dangerzone/dangerzone/manifests/sha256-{manifest_hash} Accept: application/vnd.oci.image.index.v1+json
4. Get Sigstore bundle manifest https://ghcr.io/v2/almet/dangerzone/dangerzone/manifests/{blob_manifest_digest} Accept: application/vnd.oci.image.manifest.v1+json
5. Retrieve attestation blob https://ghcr.io/v2/almet/dangerzone/dangerzone/blobs/{blob_hash} None

Variable definitions:

  • {tag}: Usually "latest" or a specific version
  • {manifest_hash}: SHA256 hash of the original manifest content
  • {blob_manifest_digest}: Digest from manifest list with artifactType = "application/vnd.dev.sigstore.bundle.v0.3+json"
  • {blob_hash}: Digest from layers where mediaType = "application/vnd.dev.sigstore.bundle.v0.3+json"

Authentication:

  • All requests (except token request) require header: Authorization: Bearer {token}
  • Token obtained from /token endpoint with:
    • service=ghcr.io
    • scope=repository:almet/dangerzone/dangerzone:pull

These calls have been put together in a small script I wrote to download the attestations from a container registry for a specific tag.

@almet
Copy link
Member Author

almet commented Dec 12, 2024

While trying to build our own attestations (without using the GHA), we've been puzzled by how the container registry was populated in different ways.

It turns out that attestations are different from plain signatures:

  1. The Cosign Signature spec explains how plain signatures are represented. This is what's being used via cosign sign. It's using annotations (not to be confused with attestations), attaching them to the container registry. The signed payload are represented by the "simple signing" format.
  2. The Cosign Bundle Specification explains how image attestations are to be represented as OCI artifacts, and attached to the container registry. This is what's being used via cosign attest

What's been complex to understand is that cosign, in their documentation, points us to use cosign sign in order to sign our container images. This will add plain signatures and is different from an attestation.

(the attest-build-provenance this PR has been using uses the Cosign Bundle Specification)

@almet almet linked an issue Dec 17, 2024 that may be closed by this pull request
@almet almet force-pushed the test/image-publication-cosign branch from 178364e to 93b95db Compare January 20, 2025 13:15
@almet almet changed the title Build and sign container images on new tags Publish image attestations Jan 20, 2025
@almet almet force-pushed the test/image-publication-cosign branch 3 times, most recently from b2760ce to e0e1458 Compare January 22, 2025 14:39
@almet almet force-pushed the test/image-publication-cosign branch from 59a93c6 to cbd4795 Compare January 28, 2025 15:21
almet added 6 commits January 29, 2025 15:08
Placing these inside the `dangerzone` python package enables an
inclusion with the software itself, and also makes it possible for
end-users to attest the image.
- Verify the archive against the known public signature
- Prepare a new archive format (with signature removed)
- Load the new image and retag it with the expected tag

During this process, the signatures are lost and should instead be
converted to a known format. Additionally, the name fo the repository
should ideally come from the signatures rather than from the command
line.
almet added 2 commits February 4, 2025 11:49
On air-gapped environements, it's now possible to load signatures
generated by `cosign save` commands. The signatures embedded in this
format will be converted to the one used by `cosign download signature`.
@almet almet force-pushed the test/image-publication-cosign branch from 12aafa2 to 9c2d7a7 Compare February 4, 2025 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: PR Review
Development

Successfully merging this pull request may close these issues.

Implement container image attestations
2 participants