Skip to content

Commit 6f4ce2d

Browse files
committed
verify attestation
1 parent 2c9972f commit 6f4ce2d

3 files changed

Lines changed: 110 additions & 6 deletions

File tree

.github/workflows/quality-checks-devcontainer.yml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,16 @@ on:
2626
required: true
2727

2828
jobs:
29+
verify_runtime_image:
30+
uses: ./.github/workflows/verify-runtime-image.yml
31+
with:
32+
runtime_docker_image: ${{ inputs.runtime_docker_image }}
33+
2934
quality_checks:
35+
needs: verify_runtime_image
3036
runs-on: ubuntu-22.04
3137
container:
32-
image: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.runtime_docker_image }}
38+
image: ${{ needs.verify_runtime_image.outputs.pinned_image }}
3339
options: --user 1001:1001 --group-add 128
3440
defaults:
3541
run:
@@ -207,8 +213,9 @@ jobs:
207213

208214
get_docker_images_to_scan:
209215
runs-on: ubuntu-22.04
216+
needs: verify_runtime_image
210217
container:
211-
image: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.runtime_docker_image }}
218+
image: ${{ needs.verify_runtime_image.outputs.pinned_image }}
212219
options: --user 1001:1001 --group-add 128
213220
defaults:
214221
run:
@@ -276,13 +283,13 @@ jobs:
276283
277284
docker_vulnerability_scan:
278285
runs-on: ubuntu-22.04
286+
needs: [verify_runtime_image, get_docker_images_to_scan]
279287
container:
280-
image: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.runtime_docker_image }}
288+
image: ${{ needs.verify_runtime_image.outputs.pinned_image }}
281289
options: --user 1001:1001 --group-add 128
282290
defaults:
283291
run:
284292
shell: bash
285-
needs: get_docker_images_to_scan
286293
if: ${{ inputs.run_docker_scan == true }}
287294
strategy:
288295
matrix:
@@ -319,8 +326,9 @@ jobs:
319326
320327
IaC-validation:
321328
runs-on: ubuntu-22.04
329+
needs: verify_runtime_image
322330
container:
323-
image: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.runtime_docker_image }}
331+
image: ${{ needs.verify_runtime_image.outputs.pinned_image }}
324332
options: --user 1001:1001 --group-add 128
325333
defaults:
326334
run:

.github/workflows/tag-release-devcontainer.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,15 @@ on:
5959
required: false
6060
description: "NPM token to publish packages"
6161
jobs:
62+
verify_runtime_image:
63+
uses: ./.github/workflows/verify-runtime-image.yml
64+
with:
65+
runtime_docker_image: ${{ inputs.runtime_docker_image }}
6266
tag_release:
67+
needs: verify_runtime_image
6368
runs-on: ubuntu-22.04
6469
container:
65-
image: ghcr.io/nhsdigital/eps-devcontainers/${{ inputs.runtime_docker_image }}
70+
image: ${{ needs.verify_runtime_image.outputs.pinned_image }}
6671
options: --user 1001:1001 --group-add 128
6772
defaults:
6873
run:
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Verify runtime image provenance
2+
"on":
3+
workflow_call:
4+
inputs:
5+
runtime_docker_image:
6+
required: true
7+
type: string
8+
description: Image name and tag (for example node_24_python_3_12:v1.0.3) or full image reference
9+
registry:
10+
required: false
11+
type: string
12+
default: ghcr.io
13+
namespace:
14+
required: false
15+
type: string
16+
default: nhsdigital/eps-devcontainers
17+
owner:
18+
required: false
19+
type: string
20+
default: NHSDigital
21+
outputs:
22+
pinned_image:
23+
description: Fully qualified digest-pinned image reference
24+
value: ${{ jobs.verify.outputs.pinned_image }}
25+
digest:
26+
description: Resolved digest for the input tag
27+
value: ${{ jobs.verify.outputs.digest }}
28+
29+
jobs:
30+
verify:
31+
runs-on: ubuntu-22.04
32+
permissions:
33+
contents: read
34+
packages: read
35+
attestations: read
36+
outputs:
37+
pinned_image: ${{ steps.resolve.outputs.pinned_image }}
38+
digest: ${{ steps.resolve.outputs.digest }}
39+
steps:
40+
- name: Login to github container registry
41+
if: startsWith(inputs.registry, 'ghcr.io')
42+
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
43+
with:
44+
registry: ghcr.io
45+
username: ${{ github.actor }}
46+
password: ${{ secrets.GITHUB_TOKEN }}
47+
48+
- name: Resolve image tag to digest
49+
id: resolve
50+
shell: bash
51+
env:
52+
RUNTIME_IMAGE_INPUT: ${{ inputs.runtime_docker_image }}
53+
REGISTRY: ${{ inputs.registry }}
54+
NAMESPACE: ${{ inputs.namespace }}
55+
run: |
56+
set -euo pipefail
57+
58+
if [[ "$RUNTIME_IMAGE_INPUT" == *"/"* ]]; then
59+
IMAGE_REF="$RUNTIME_IMAGE_INPUT"
60+
else
61+
IMAGE_REF="${REGISTRY}/${NAMESPACE}/${RUNTIME_IMAGE_INPUT}"
62+
fi
63+
64+
if [[ "$IMAGE_REF" == *@sha256:* ]]; then
65+
DIGEST="${IMAGE_REF#*@}"
66+
IMAGE_BASE="${IMAGE_REF%@*}"
67+
else
68+
DIGEST="$(docker buildx imagetools inspect "$IMAGE_REF" | awk '/^Digest:/ {print $2; exit}')"
69+
IMAGE_BASE="${IMAGE_REF%:*}"
70+
fi
71+
72+
if [[ -z "$DIGEST" ]]; then
73+
echo "Failed to resolve digest for image: $IMAGE_REF" >&2
74+
exit 1
75+
fi
76+
77+
PINNED_IMAGE="${IMAGE_BASE}@${DIGEST}"
78+
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
79+
echo "pinned_image=${PINNED_IMAGE}" >> "$GITHUB_OUTPUT"
80+
echo "Resolved image: ${PINNED_IMAGE}"
81+
82+
- name: Verify provenance attestation
83+
shell: bash
84+
env:
85+
GH_TOKEN: ${{ github.token }}
86+
PINNED_IMAGE: ${{ steps.resolve.outputs.pinned_image }}
87+
OWNER: ${{ inputs.owner }}
88+
run: |
89+
set -euo pipefail
90+
gh attestation verify "oci://${PINNED_IMAGE}" --owner "$OWNER"
91+
echo "Verified attestation for ${PINNED_IMAGE}"

0 commit comments

Comments
 (0)