Skip to content
This repository has been archived by the owner on Dec 10, 2024. It is now read-only.

Commit

Permalink
feat: sign image before pushing to registry (#127)
Browse files Browse the repository at this point in the history
Closes #111
  • Loading branch information
alexashley authored Mar 21, 2024
1 parent 512498e commit 27549b7
Showing 1 changed file with 88 additions and 83 deletions.
171 changes: 88 additions & 83 deletions .github/workflows/build-and-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
outputs:
digest:
description: "Docker image digest"
value: ${{ jobs.push.outputs.digest }}
value: ${{ jobs.sign.outputs.digest }}

jobs:
detect-workflow:
Expand Down Expand Up @@ -65,16 +65,26 @@ jobs:
name: image
path: /tmp/image.tar

push:
sign:
permissions:
packages: write # required to push the image to GHCR
id-token: write # required to sign image
contents: read # for reading local TUF root.json file
packages: write # required to upload signature to GHCR
runs-on: ubuntu-latest
needs:
- build
- detect-workflow
outputs:
digest: ${{ steps.digest.outputs.digest }}
defaults:
run:
working-directory: ./app
steps:
- name: Checkout App Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
path: app

- name: Checkout Workflows Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
Expand Down Expand Up @@ -102,6 +112,14 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Cosign Setup
uses: ./gh-trusted-builds-workflows/.github/actions/cosign
with:
tufRoot: ${{ steps.config.outputs.tufRoot }}
tufMirror: ${{ steps.config.outputs.tufMirror }}
version: ${{ steps.config.outputs.cosignVersion }}
workingDirectory: ./gh-trusted-builds-workflows

- name: Install Crane
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -122,72 +140,26 @@ jobs:
- name: Compute Digest
id: digest
run: |
localDigest=$(crane digest --tarball /tmp/image.tar)
echo "locally-computed digest: ${localDigest}"
echo "digest=${localDigest}" >> $GITHUB_OUTPUT
- name: Push Image
id: push
env:
IMAGE_METADATA: ${{ needs.build.outputs.metadata }}
run: |
commit="${{ github.sha }}"
pushedImage=$(printf "${IMAGE_METADATA}" | jq --arg commit "${commit::7}" -r '.tags | .[] | select(.|endswith($commit))')
echo "Pushing ${pushedImage}"
crane push /tmp/image.tar "${pushedImage}"
echo "image=${pushedImage}" >> $GITHUB_OUTPUT
echo "digest=$(crane digest --tarball /tmp/image.tar)" >> $GITHUB_OUTPUT
- name: Compare Digests
run: |
pushedImage=${{ steps.push.outputs.image }}
registryDigest=$(crane digest "${pushedImage}")
localDigest=${{ steps.digest.outputs.digest }}
if [[ "${localDigest}" == "${registryDigest}" ]]; then
echo "Remote digest matches digest from local registry"
else
echo "Digest in registry doesn't match expected digest"
echo "Locally-computed digest: ${localDigest}"
echo "Digest from registry: ${registryDigest}"
echo "This may indicate the image was modified by the registry"
exit 1
fi
- name: Add Tags
env:
IMAGE_METADATA: ${{ needs.build.outputs.metadata }}
- name: Sign
run: |
pushedImage=${{ steps.push.outputs.image }}
# add the remaining tags, skipping the tag that was pushed initially
printf "${IMAGE_METADATA}" | jq -rc '.tags | .[]' | while read taggedImage; do
if [[ "${taggedImage}" == "${pushedImage}" ]]; then
continue
fi
tag=$(printf "${taggedImage}" | awk -F "${image}:" '{print $2}')
echo "adding tag ${tag}"
crane tag "${pushedImage}" "${tag}"
done
cosign sign \
--annotations liatr.io/github-actions-run-link='${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' \
--annotations liatr.io/signed-off-by=platform-team \
--rekor-url ${{ steps.config.outputs.rekorUrl }} \
--fulcio-url ${{ steps.config.outputs.fulcioUrl }} \
--yes ghcr.io/${{ github.repository }}@${{ steps.digest.outputs.digest }}
sign:
push:
permissions:
id-token: write # required to sign image
contents: read # for reading local TUF root.json file
packages: write # required to upload signature to GHCR
packages: write # required to push the image to GHCR
runs-on: ubuntu-latest
needs:
- push
- build
- sign
- detect-workflow
defaults:
run:
working-directory: ./app
steps:
- name: Checkout App Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
path: app

- name: Checkout Workflows Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
Expand All @@ -202,13 +174,11 @@ jobs:
with:
environment: ${{ inputs.environment }}

- name: Cosign Setup
uses: ./gh-trusted-builds-workflows/.github/actions/cosign
- name: Download Image Archive
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4
with:
tufRoot: ${{ steps.config.outputs.tufRoot }}
tufMirror: ${{ steps.config.outputs.tufMirror }}
version: ${{ steps.config.outputs.cosignVersion }}
workingDirectory: ./gh-trusted-builds-workflows
name: image
path: /tmp

- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
Expand All @@ -217,15 +187,50 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Sign
- name: Install Crane
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
cosign sign \
--annotations liatr.io/github-actions-run-link='${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' \
--annotations liatr.io/signed-off-by=platform-team \
--rekor-url ${{ steps.config.outputs.rekorUrl }} \
--fulcio-url ${{ steps.config.outputs.fulcioUrl }} \
--yes ghcr.io/${{ github.repository }}@${{ needs.push.outputs.digest }}
cd $(mktemp -d)
gh release download \
--repo google/go-containerregistry \
--pattern "go-containerregistry_Linux_x86_64.tar.gz" "${{ steps.config.outputs.craneVersion }}"
tar xvf go-containerregistry_Linux_x86_64.tar.gz
mkdir -p $HOME/.bin/crane
mv ./crane $HOME/.bin/crane
echo "$HOME/.bin/crane" >> $GITHUB_PATH
$HOME/.bin/crane/crane version
- name: Push Image
id: push
env:
IMAGE_METADATA: ${{ needs.build.outputs.metadata }}
run: |
commit="${{ github.sha }}"
pushedImage=$(printf "${IMAGE_METADATA}" | jq --arg commit "${commit::7}" -r '.tags | .[] | select(.|endswith($commit))')
echo "Pushing ${pushedImage}"
crane push /tmp/image.tar "${pushedImage}"
echo "image=${pushedImage}" >> $GITHUB_OUTPUT
- name: Add Tags
env:
IMAGE_METADATA: ${{ needs.build.outputs.metadata }}
run: |
pushedImage=${{ steps.push.outputs.image }}
# add the remaining tags, skipping the tag that was pushed initially
printf "${IMAGE_METADATA}" | jq -rc '.tags | .[]' | while read taggedImage; do
if [[ "${taggedImage}" == "${pushedImage}" ]]; then
continue
fi
tag=$(printf "${taggedImage}" | awk -F "${image}:" '{print $2}')
echo "adding tag ${tag}"
crane tag "${pushedImage}" "${tag}"
done
provenance:
permissions:
Expand All @@ -235,7 +240,7 @@ jobs:
packages: write # required to upload attestations to GHCR
runs-on: ubuntu-latest
needs:
- push
- sign
- detect-workflow
defaults:
run:
Expand Down Expand Up @@ -303,7 +308,7 @@ jobs:
--type slsaprovenance \
--fulcio-url ${{ steps.config.outputs.fulcioUrl }} \
--yes \
ghcr.io/${{ github.repository }}@${{ needs.push.outputs.digest }}
ghcr.io/${{ github.repository }}@${{ needs.sign.outputs.digest }}
sbom:
Expand All @@ -313,7 +318,7 @@ jobs:
packages: write # used to push attestations to GHCR
runs-on: ubuntu-latest
needs:
- push
- sign
- detect-workflow
- provenance # prevent race conditions when updating attestation tag
defaults:
Expand Down Expand Up @@ -363,14 +368,14 @@ jobs:
SYFT_FILE_METADATA_CATALOGER_ENABLED: true
SYFT_FILE_METADATA_DIGESTS: sha256
run: |
syft -o spdx-json --file sbom.spdx.json ghcr.io/${{ github.repository }}@${{ needs.push.outputs.digest }}
syft -o spdx-json --file sbom.spdx.json ghcr.io/${{ github.repository }}@${{ needs.sign.outputs.digest }}
cosign attest --predicate="sbom.spdx.json" \
--rekor-url ${{ steps.config.outputs.rekorUrl }} \
--type spdxjson \
--fulcio-url ${{ steps.config.outputs.fulcioUrl }} \
--yes \
ghcr.io/${{ github.repository }}@${{ needs.push.outputs.digest }}
ghcr.io/${{ github.repository }}@${{ needs.sign.outputs.digest }}
rm sbom.spdx.json
Expand All @@ -382,7 +387,7 @@ jobs:
pull-requests: read # used to populate attestation fields
runs-on: ubuntu-latest
needs:
- push
- sign
- detect-workflow
- sbom # prevent race conditions when updating attestation tag
defaults:
Expand Down Expand Up @@ -435,19 +440,19 @@ jobs:
run: |
attestation github-pull-request \
--artifact-uri ghcr.io/${{ github.repository }} \
--artifact-digest ${{ needs.push.outputs.digest }} \
--artifact-digest ${{ needs.sign.outputs.digest }} \
--rekor-url ${{ steps.config.outputs.rekorUrl }} \
--fulcio-url ${{ steps.config.outputs.fulcioUrl }}
workflow-metadata:
permissions: {}
runs-on: ubuntu-latest
needs:
- push
- sign
steps:
- name: Create Results Metadata
run: |
jq -n --arg digest ${{ needs.push.outputs.digest }} '{digest: $digest}' > /tmp/workflow-metadata.json
jq -n --arg digest ${{ needs.sign.outputs.digest }} '{digest: $digest}' > /tmp/workflow-metadata.json
- name: Upload Workflow Metadata
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4
Expand Down

0 comments on commit 27549b7

Please sign in to comment.