Skip to content

Commit

Permalink
Sign docker images using cosign
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mjpieters committed Oct 30, 2024
1 parent dc5e35e commit d1a3809
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 0 deletions.
54 changes: 54 additions & 0 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ jobs:
needs:
- docker-publish
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
permissions:
packages: write
id-token: write # needed for signing the images with GitHub OIDC Token
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -180,6 +183,8 @@ jobs:
- python:3.9-slim-bookworm,python3.9-bookworm-slim
- python:3.8-slim-bookworm,python3.8-bookworm-slim
steps:
- uses: sigstore/[email protected]

- uses: docker/setup-buildx-action@v3

- uses: docker/login-action@v3
Expand Down Expand Up @@ -242,6 +247,7 @@ jobs:
${{ env.TAG_PATTERNS }}
- name: Build and push
id: build-and-push
uses: docker/build-push-action@v6
with:
context: .
Expand All @@ -254,6 +260,17 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}

- name: Sign the images with GitHub OIDC Token
env:
DIGEST: ${{ steps.build-and-push.outputs.digest }}
TAGS: ${{ steps.meta.outputs.tags }}
run: |
images=""
for tag in ${TAGS}; do
images+="${tag}@${DIGEST} "
done
cosign sign --yes ${images}
# This is effectively a duplicate of `docker-publish` to make https://github.com/astral-sh/uv/pkgs/container/uv
# show the uv base image first since GitHub always shows the last updated image digests
# This works by annotating the original digests (previously non-annotated) which triggers an update to ghcr.io
Expand All @@ -265,6 +282,9 @@ jobs:
needs:
- docker-publish-extra
if: ${{ inputs.plan != '' && !fromJson(inputs.plan).announcement_tag_is_implicit }}
permissions:
packages: write
id-token: write # needed for signing the images with GitHub OIDC Token
steps:
- name: Download digests
uses: actions/download-artifact@v4
Expand All @@ -273,6 +293,8 @@ jobs:
pattern: digests-*
merge-multiple: true

- uses: sigstore/[email protected]

- uses: docker/setup-buildx-action@v3

- name: Extract metadata (tags, labels) for Docker
Expand All @@ -295,14 +317,46 @@ jobs:

# Adapted from https://docs.docker.com/build/ci/github-actions/multi-platform/
- name: Create manifest list and push
id: manifest-push
working-directory: /tmp/digests
# The readarray part is used to make sure the quoting and special characters are preserved on expansion (e.g. spaces)
# The jq command expands the docker/metadata json "tags" array entry to `-t tag1 -t tag2 ...` for each tag in the array
# The printf will expand the base image with the `<UV_BASE_IMG>@sha256:<sha256> ...` for each sha256 in the directory
# The final command becomes `docker buildx imagetools create -t tag1 -t tag2 ... <UV_BASE_IMG>@sha256:<sha256_1> <UV_BASE_IMG>@sha256:<sha256_2> ...`
# The digest of the new manifest is then shared as the 'digest' output.
run: |
readarray -t lines <<< "$DOCKER_METADATA_OUTPUT_ANNOTATIONS"; annotations=(); for line in "${lines[@]}"; do annotations+=(--annotation "$line"); done
docker buildx imagetools create \
"${annotations[@]}" \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.UV_BASE_IMG }}@sha256:%s ' *)
# To sign the manifest, we need it's digest. Unfortunately "docker
# buildx imagetools create" does not (yet) have a clean way of sharing
# the digest of the manifest it creates (see docker/buildx#2407), so
# we use a separate command to retrieve it. This _has_ to be done in
# the same step to minimize the risk of the tag having been moved to
# another digest, which would pose a security risk. Within the same
# step, we can count on the local docker cache of the tag not having
# changed.
# imagetools inspect [TAG] --format '{{json .Manifest}}' gives us
# the machine readable JSON description of the manifest, and the
# jq command extracts the digest from this. The digest is then
# sent to the Github step output file for sharing with other steps.
digest="$(
docker buildx imagetools inspect \
"${UV_BASE_IMG}:${DOCKER_METADATA_OUTPUT_VERSION}" \
--format '{{json .Manifest}}' \
| jq -r '.digest'
)"
echo "digest=${digest}" >> "$GITHUB_OUTPUT"
- name: Sign the manifest with GitHub OIDC Token
env:
DIGEST: ${{ steps.manifest-push.outputs.digest }}
TAGS: ${{ steps.meta.outputs.tags }}
run: |
images=""
for tag in ${TAGS}; do
images+="${tag}@${DIGEST} "
done
cosign sign --yes ${images}
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ jobs:
with:
plan: ${{ needs.plan.outputs.val }}
secrets: inherit
# docker jobs get escalated permissions, so they can sign the images
permissions:
"contents": "read"
"packages": "write"
"id-token": "write"

# Build and package all the platform-agnostic(ish) things
build-global-artifacts:
Expand Down

0 comments on commit d1a3809

Please sign in to comment.