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 29, 2024
1 parent dc5e35e commit 5abc4eb
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 0 deletions.
47 changes: 47 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,39 @@ 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 ' *)
# unfortunately "docker buildx imagetools create" does not yet have a
# clean way of sharing the digest of the manifest it creates, see
# https://github.com/docker/buildx/issues/2407. Using a separate command to
# retrieve it _now_ is better than doing it later though, as it is highly
# unlikely that the digest changes in the same job with the same local docker setup.
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 5abc4eb

Please sign in to comment.