Skip to content

Commit 64b0c85

Browse files
CopilotbrabsterCopilot
authored
Add build provenance attestations for published Docker images (#45)
* Initial plan * Add attestation support for published Docker images Co-authored-by: brabster <[email protected]> * Fix digest capture to occur after push operation Co-authored-by: brabster <[email protected]> * Extract digest from docker push output instead of docker inspect Co-authored-by: brabster <[email protected]> * Extract digest handling to script with comprehensive tests Co-authored-by: brabster <[email protected]> * Update checkout action to v4 for consistency Co-authored-by: brabster <[email protected]> * Revert script complexity - return to inline digest extraction Co-authored-by: brabster <[email protected]> * Update CHANGELOG to reference PR #45 Co-authored-by: brabster <[email protected]> * Update .github/workflows/docker-publish.yml Co-authored-by: Copilot <[email protected]> * Apply code review feedback: add set -euo pipefail, fix comment, remove stderr redirect Co-authored-by: brabster <[email protected]> * Fix pull request links in CHANGELOG.md Updated pull request references in CHANGELOG.md for accuracy. --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: brabster <[email protected]> Co-authored-by: Paul Brabban <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c7438c1 commit 64b0c85

File tree

3 files changed

+106
-6
lines changed

3 files changed

+106
-6
lines changed

.github/workflows/docker-publish.yml

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ jobs:
131131
runs-on: ubuntu-latest # maintained by GitHub
132132
permissions:
133133
packages: write
134+
# id-token: write is required for generating attestations using GitHub's OIDC token
135+
# This allows the workflow to prove its identity to the attestation service
136+
# Reference: https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect
137+
id-token: write
138+
# attestations: write is required to publish attestations to the repository
139+
# This permission allows the workflow to write attestation data associated with the published artifacts
140+
# Reference: https://github.com/actions/attest-build-provenance#permissions
141+
attestations: write
134142
needs:
135143
- build_and_load
136144
- osv_scan
@@ -147,14 +155,53 @@ jobs:
147155
- name: Log in to the Container registry
148156
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
149157

158+
# Push the image and capture its digest for attestation
159+
# The digest uniquely identifies the image content and is required for attestation
150160
- name: Push tagged image
161+
id: push
151162
env:
152163
DOCKER_SHA_TAG: ghcr.io/${{ github.repository }}:${{ github.sha }}
153164
DOCKER_LATEST_TAG: ghcr.io/${{ github.repository }}:latest
154165
run: |
166+
set -euo pipefail
155167
docker load -i ${{ runner.temp }}/candidate_image.tar
156-
for tag in $DOCKER_SHA_TAG $DOCKER_LATEST_TAG; do
157-
docker tag candidate_image:latest $tag
158-
docker push $tag
159-
done
168+
# Tag and push the SHA-tagged version first, capturing the digest from push output
169+
# docker push outputs the digest in the format "digest: sha256:... size: ..."
170+
# We extract the digest directly from this output without depending on docker inspect
171+
docker tag candidate_image:latest $DOCKER_SHA_TAG
172+
PUSH_OUTPUT=$(docker push $DOCKER_SHA_TAG)
173+
DIGEST=$(echo "$PUSH_OUTPUT" | grep -oP 'digest: \K(sha256:[a-f0-9]{64})')
174+
if [ -z "$DIGEST" ]; then
175+
echo "::error::Failed to extract digest from docker push output"
176+
echo "Push output was: $PUSH_OUTPUT"
177+
exit 1
178+
fi
179+
echo "digest=$DIGEST" >> $GITHUB_OUTPUT
180+
# Push the latest tag (same image, so same digest)
181+
docker tag candidate_image:latest $DOCKER_LATEST_TAG
182+
docker push $DOCKER_LATEST_TAG
183+
184+
# Generate build provenance attestations for the published Docker images
185+
# This creates cryptographically signed attestations that prove:
186+
# 1. The image was built by this specific GitHub Actions workflow
187+
# 2. The image digest matches what was produced by the workflow
188+
# 3. Build metadata including workflow, repository, and commit information
189+
#
190+
# The attestation is signed using GitHub's Sigstore infrastructure and stored
191+
# in the repository's attestations, allowing consumers to verify the provenance
192+
# of the image before use.
193+
#
194+
# Consumers can verify the attestation using:
195+
# gh attestation verify oci://ghcr.io/brabster/terraform-bootstrap-gcp:latest --owner brabster
196+
#
197+
# Reference: https://github.com/actions/attest-build-provenance
198+
- name: Attest build provenance
199+
uses: actions/attest-build-provenance@v2 # maintained by GitHub
200+
with:
201+
# Attest the image by its name and digest
202+
# Using the digest ensures the attestation is tied to the exact image content
203+
# rather than a mutable tag
204+
subject-name: ghcr.io/${{ github.repository }}
205+
subject-digest: ${{ steps.push.outputs.digest }}
206+
push-to-registry: true
160207

CHANGELOG.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,34 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7-
## [[#41](https://github.com/brabster/terraform-bootstrap-gcp/pull/42)] - Remove dbt-bigquery cache warming
7+
## [[#45](https://github.com/brabster/terraform-bootstrap-gcp/pull/45)] - Add attestation to published Docker images
8+
9+
### Added
10+
11+
- Build provenance attestations for all published Docker images using GitHub's `actions/attest-build-provenance` action.
12+
- Documentation in README explaining attestation benefits and how consumers can verify image provenance.
13+
- Instructions for verifying attestations using the GitHub CLI.
14+
15+
### Changed
16+
17+
- Updated the publish job in the docker-publish.yml workflow to include `id-token: write` and `attestations: write` permissions.
18+
- Modified the image push step to capture the image digest for use in attestation.
19+
20+
### Rationale
21+
22+
Build provenance attestations provide cryptographic proof of an artifact's origin and build process. This enables consumers to verify that images were built by the official GitHub Actions workflow and have not been tampered with. The attestation includes metadata such as the commit SHA, workflow, and build environment, creating an auditable trail for supply chain security.
23+
24+
### Security
25+
26+
- Attestations enable consumers to verify image authenticity before use, reducing the risk of supply chain attacks.
27+
- Signed attestations create an auditable trail linking published images to their source code and build process.
28+
- The attestation signature is created using GitHub's OIDC token, which proves the workflow's identity without requiring long-lived credentials.
29+
- Consumers can integrate attestation verification into their CI/CD pipelines to enforce supply chain security policies.
30+
31+
- **Supply Chain Posture Impact:** This change significantly improves the supply chain security posture by providing cryptographic proof of provenance for all published artifacts. Attestations enable consumers to verify that images were built by the expected workflow, detect tampering, and establish trust in the build process. This addresses a critical gap in software supply chain security by making the build process transparent and verifiable. The attestations are signed using GitHub's Sigstore infrastructure, which follows industry best practices for artifact signing and verification.
32+
- **Security Posture Impact:** Positive
33+
34+
## [[#42](https://github.com/brabster/terraform-bootstrap-gcp/pull/42)] - Remove dbt-bigquery cache warming
835

936
### Removed
1037

@@ -28,7 +55,7 @@ The cache warming provided minimal benefit while adding complexity to the build
2855
- **Supply Chain Posture Impact:** This change improves the project's supply chain security posture by removing unnecessary dependencies from the base image. Users now explicitly install only the Python packages they need, reducing the number of packages that must be monitored for vulnerabilities. This aligns with the principle of minimal dependencies and reduces the image's attack surface.
2956
- **Security Posture Impact:** Positive
3057

31-
## [[#35](https://github.com/brabster/terraform-bootstrap-gcp/pull/36)] - Add git CLI completion support
58+
## [[#36](https://github.com/brabster/terraform-bootstrap-gcp/pull/36)] - Add git CLI completion support
3259

3360
### Added
3461

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,32 @@ The image has two types of tags:
3737
- **`latest`**: This tag always points to the most recent daily build.
3838
- **git SHA**: A tag with the git SHA of the commit that triggered the build is created for each build. This allows for pinning to a specific version of the image should the need arise.
3939

40+
## Supply chain security
41+
42+
Each published image includes a cryptographically signed attestation that provides build provenance information. This attestation proves that the image was built by this repository's GitHub Actions workflow and allows you to verify the image before use.
43+
44+
### Benefits of attestation
45+
46+
- **Authenticity**: Verify that the image was built by the official workflow, not by an unauthorised party.
47+
- **Integrity**: Confirm that the image content has not been tampered with since it was built.
48+
- **Transparency**: Access build metadata including the exact commit, workflow, and build environment that produced the image.
49+
- **Compliance**: Meet supply chain security requirements for your organisation or regulatory framework.
50+
51+
### Verifying attestations
52+
53+
You can verify the attestation using the GitHub CLI:
54+
55+
```sh
56+
gh attestation verify oci://ghcr.io/brabster/terraform-bootstrap-gcp:latest --owner brabster
57+
```
58+
59+
This command checks that:
60+
1. The attestation signature is valid and was created by GitHub Actions.
61+
2. The image digest matches the attested content.
62+
3. The attestation was created by a workflow in this repository.
63+
64+
For automated verification in your CI/CD pipeline, see [GitHub's attestation documentation](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds).
65+
4066
## How to use
4167

4268
### GitHub container registry

0 commit comments

Comments
 (0)