Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4987de6

Browse files
matifaliThomasK33
andauthored
chore: enable SBOM attestations for docker images (#16894)
- Enable SBOM and provenance attestations in Docker builds - Installs `cosign` and `syft` in dogfood image - Adds [github attestations](https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds) Signed-off-by: Thomas Kosiewski <[email protected]> --------- Signed-off-by: Thomas Kosiewski <[email protected]> Co-authored-by: Thomas Kosiewski <[email protected]>
1 parent 30179ae commit 4987de6

File tree

5 files changed

+339
-3
lines changed

5 files changed

+339
-3
lines changed

.github/workflows/ci.yaml

+146
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,11 @@ jobs:
10241024
# Necessary to push docker images to ghcr.io.
10251025
packages: write
10261026
# Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage)
1027+
# Also necessary for keyless cosign (https://docs.sigstore.dev/cosign/signing/overview/)
1028+
# And for GitHub Actions attestation
10271029
id-token: write
1030+
# Required for GitHub Actions attestation
1031+
attestations: write
10281032
env:
10291033
DOCKER_CLI_EXPERIMENTAL: "enabled"
10301034
outputs:
@@ -1069,6 +1073,16 @@ jobs:
10691073
- name: Install zstd
10701074
run: sudo apt-get install -y zstd
10711075

1076+
- name: Install cosign
1077+
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
1078+
with:
1079+
cosign-release: "v2.4.3"
1080+
1081+
- name: Install syft
1082+
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
1083+
with:
1084+
syft-version: "v1.20.0"
1085+
10721086
- name: Setup Windows EV Signing Certificate
10731087
run: |
10741088
set -euo pipefail
@@ -1170,6 +1184,138 @@ jobs:
11701184
done
11711185
fi
11721186
1187+
# GitHub attestation provides SLSA provenance for the Docker images, establishing a verifiable
1188+
# record that these images were built in GitHub Actions with specific inputs and environment.
1189+
# This complements our existing cosign attestations which focus on SBOMs.
1190+
#
1191+
# We attest each tag separately to ensure all tags have proper provenance records.
1192+
# TODO: Consider refactoring these steps to use a matrix strategy or composite action to reduce duplication
1193+
# while maintaining the required functionality for each tag.
1194+
- name: GitHub Attestation for Docker image
1195+
id: attest_main
1196+
if: github.ref == 'refs/heads/main'
1197+
continue-on-error: true
1198+
uses: actions/attest@a63cfcc7d1aab266ee064c58250cfc2c7d07bc31 # v2.2.1
1199+
with:
1200+
subject-name: "ghcr.io/coder/coder-preview:main"
1201+
predicate-type: "https://slsa.dev/provenance/v1"
1202+
predicate: |
1203+
{
1204+
"buildType": "https://github.com/actions/runner-images/",
1205+
"builder": {
1206+
"id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
1207+
},
1208+
"invocation": {
1209+
"configSource": {
1210+
"uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
1211+
"digest": {
1212+
"sha1": "${{ github.sha }}"
1213+
},
1214+
"entryPoint": ".github/workflows/ci.yaml"
1215+
},
1216+
"environment": {
1217+
"github_workflow": "${{ github.workflow }}",
1218+
"github_run_id": "${{ github.run_id }}"
1219+
}
1220+
},
1221+
"metadata": {
1222+
"buildInvocationID": "${{ github.run_id }}",
1223+
"completeness": {
1224+
"environment": true,
1225+
"materials": true
1226+
}
1227+
}
1228+
}
1229+
push-to-registry: true
1230+
1231+
- name: GitHub Attestation for Docker image (latest tag)
1232+
id: attest_latest
1233+
if: github.ref == 'refs/heads/main'
1234+
continue-on-error: true
1235+
uses: actions/attest@a63cfcc7d1aab266ee064c58250cfc2c7d07bc31 # v2.2.1
1236+
with:
1237+
subject-name: "ghcr.io/coder/coder-preview:latest"
1238+
predicate-type: "https://slsa.dev/provenance/v1"
1239+
predicate: |
1240+
{
1241+
"buildType": "https://github.com/actions/runner-images/",
1242+
"builder": {
1243+
"id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
1244+
},
1245+
"invocation": {
1246+
"configSource": {
1247+
"uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
1248+
"digest": {
1249+
"sha1": "${{ github.sha }}"
1250+
},
1251+
"entryPoint": ".github/workflows/ci.yaml"
1252+
},
1253+
"environment": {
1254+
"github_workflow": "${{ github.workflow }}",
1255+
"github_run_id": "${{ github.run_id }}"
1256+
}
1257+
},
1258+
"metadata": {
1259+
"buildInvocationID": "${{ github.run_id }}",
1260+
"completeness": {
1261+
"environment": true,
1262+
"materials": true
1263+
}
1264+
}
1265+
}
1266+
push-to-registry: true
1267+
1268+
- name: GitHub Attestation for version-specific Docker image
1269+
id: attest_version
1270+
if: github.ref == 'refs/heads/main'
1271+
continue-on-error: true
1272+
uses: actions/attest@a63cfcc7d1aab266ee064c58250cfc2c7d07bc31 # v2.2.1
1273+
with:
1274+
subject-name: "ghcr.io/coder/coder-preview:${{ steps.build-docker.outputs.tag }}"
1275+
predicate-type: "https://slsa.dev/provenance/v1"
1276+
predicate: |
1277+
{
1278+
"buildType": "https://github.com/actions/runner-images/",
1279+
"builder": {
1280+
"id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
1281+
},
1282+
"invocation": {
1283+
"configSource": {
1284+
"uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
1285+
"digest": {
1286+
"sha1": "${{ github.sha }}"
1287+
},
1288+
"entryPoint": ".github/workflows/ci.yaml"
1289+
},
1290+
"environment": {
1291+
"github_workflow": "${{ github.workflow }}",
1292+
"github_run_id": "${{ github.run_id }}"
1293+
}
1294+
},
1295+
"metadata": {
1296+
"buildInvocationID": "${{ github.run_id }}",
1297+
"completeness": {
1298+
"environment": true,
1299+
"materials": true
1300+
}
1301+
}
1302+
}
1303+
push-to-registry: true
1304+
1305+
# Report attestation failures but don't fail the workflow
1306+
- name: Check attestation status
1307+
if: github.ref == 'refs/heads/main'
1308+
run: |
1309+
if [[ "${{ steps.attest_main.outcome }}" == "failure" ]]; then
1310+
echo "::warning::GitHub attestation for main tag failed"
1311+
fi
1312+
if [[ "${{ steps.attest_latest.outcome }}" == "failure" ]]; then
1313+
echo "::warning::GitHub attestation for latest tag failed"
1314+
fi
1315+
if [[ "${{ steps.attest_version.outcome }}" == "failure" ]]; then
1316+
echo "::warning::GitHub attestation for version-specific tag failed"
1317+
fi
1318+
11731319
- name: Prune old images
11741320
if: github.ref == 'refs/heads/main'
11751321
uses: vlaurin/action-ghcr-prune@0cf7d39f88546edd31965acba78cdcb0be14d641 # v0.6.0

.github/workflows/release.yaml

+167
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ jobs:
122122
# Necessary to push docker images to ghcr.io.
123123
packages: write
124124
# Necessary for GCP authentication (https://github.com/google-github-actions/setup-gcloud#usage)
125+
# Also necessary for keyless cosign (https://docs.sigstore.dev/cosign/signing/overview/)
126+
# And for GitHub Actions attestation
125127
id-token: write
128+
# Required for GitHub Actions attestation
129+
attestations: write
126130
env:
127131
# Necessary for Docker manifest
128132
DOCKER_CLI_EXPERIMENTAL: "enabled"
@@ -246,6 +250,16 @@ jobs:
246250
apple-codesign-0.22.0-x86_64-unknown-linux-musl/rcodesign
247251
rm /tmp/rcodesign.tar.gz
248252
253+
- name: Install cosign
254+
uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1
255+
with:
256+
cosign-release: "v2.4.3"
257+
258+
- name: Install syft
259+
uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
260+
with:
261+
syft-version: "v1.20.0"
262+
249263
- name: Setup Apple Developer certificate and API key
250264
run: |
251265
set -euo pipefail
@@ -361,6 +375,7 @@ jobs:
361375
file: scripts/Dockerfile.base
362376
platforms: linux/amd64,linux/arm64,linux/arm/v7
363377
provenance: true
378+
sbom: true
364379
pull: true
365380
no-cache: true
366381
push: true
@@ -397,7 +412,52 @@ jobs:
397412
echo "$manifests" | grep -q linux/arm64
398413
echo "$manifests" | grep -q linux/arm/v7
399414
415+
# GitHub attestation provides SLSA provenance for Docker images, establishing a verifiable
416+
# record that these images were built in GitHub Actions with specific inputs and environment.
417+
# This complements our existing cosign attestations (which focus on SBOMs) by adding
418+
# GitHub-specific build provenance to enhance our supply chain security.
419+
#
420+
# TODO: Consider refactoring these attestation steps to use a matrix strategy or composite action
421+
# to reduce duplication while maintaining the required functionality for each distinct image tag.
422+
- name: GitHub Attestation for Base Docker image
423+
id: attest_base
424+
if: ${{ !inputs.dry_run && steps.image-base-tag.outputs.tag != '' }}
425+
continue-on-error: true
426+
uses: actions/attest@a63cfcc7d1aab266ee064c58250cfc2c7d07bc31 # v2.2.1
427+
with:
428+
subject-name: ${{ steps.image-base-tag.outputs.tag }}
429+
predicate-type: "https://slsa.dev/provenance/v1"
430+
predicate: |
431+
{
432+
"buildType": "https://github.com/actions/runner-images/",
433+
"builder": {
434+
"id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
435+
},
436+
"invocation": {
437+
"configSource": {
438+
"uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
439+
"digest": {
440+
"sha1": "${{ github.sha }}"
441+
},
442+
"entryPoint": ".github/workflows/release.yaml"
443+
},
444+
"environment": {
445+
"github_workflow": "${{ github.workflow }}",
446+
"github_run_id": "${{ github.run_id }}"
447+
}
448+
},
449+
"metadata": {
450+
"buildInvocationID": "${{ github.run_id }}",
451+
"completeness": {
452+
"environment": true,
453+
"materials": true
454+
}
455+
}
456+
}
457+
push-to-registry: true
458+
400459
- name: Build Linux Docker images
460+
id: build_docker
401461
run: |
402462
set -euxo pipefail
403463
@@ -416,18 +476,125 @@ jobs:
416476
# being pushed so will automatically push them.
417477
make push/build/coder_"$version"_linux.tag
418478
479+
# Save multiarch image tag for attestation
480+
multiarch_image="$(./scripts/image_tag.sh)"
481+
echo "multiarch_image=${multiarch_image}" >> $GITHUB_OUTPUT
482+
483+
# For debugging, print all docker image tags
484+
docker images
485+
419486
# if the current version is equal to the highest (according to semver)
420487
# version in the repo, also create a multi-arch image as ":latest" and
421488
# push it
489+
created_latest_tag=false
422490
if [[ "$(git tag | grep '^v' | grep -vE '(rc|dev|-|\+|\/)' | sort -r --version-sort | head -n1)" == "v$(./scripts/version.sh)" ]]; then
423491
./scripts/build_docker_multiarch.sh \
424492
--push \
425493
--target "$(./scripts/image_tag.sh --version latest)" \
426494
$(cat build/coder_"$version"_linux_{amd64,arm64,armv7}.tag)
495+
created_latest_tag=true
496+
echo "created_latest_tag=true" >> $GITHUB_OUTPUT
497+
else
498+
echo "created_latest_tag=false" >> $GITHUB_OUTPUT
427499
fi
428500
env:
429501
CODER_BASE_IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }}
430502

503+
- name: GitHub Attestation for Docker image
504+
id: attest_main
505+
if: ${{ !inputs.dry_run }}
506+
continue-on-error: true
507+
uses: actions/attest@a63cfcc7d1aab266ee064c58250cfc2c7d07bc31 # v2.2.1
508+
with:
509+
subject-name: ${{ steps.build_docker.outputs.multiarch_image }}
510+
predicate-type: "https://slsa.dev/provenance/v1"
511+
predicate: |
512+
{
513+
"buildType": "https://github.com/actions/runner-images/",
514+
"builder": {
515+
"id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
516+
},
517+
"invocation": {
518+
"configSource": {
519+
"uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
520+
"digest": {
521+
"sha1": "${{ github.sha }}"
522+
},
523+
"entryPoint": ".github/workflows/release.yaml"
524+
},
525+
"environment": {
526+
"github_workflow": "${{ github.workflow }}",
527+
"github_run_id": "${{ github.run_id }}"
528+
}
529+
},
530+
"metadata": {
531+
"buildInvocationID": "${{ github.run_id }}",
532+
"completeness": {
533+
"environment": true,
534+
"materials": true
535+
}
536+
}
537+
}
538+
push-to-registry: true
539+
540+
# Get the latest tag name for attestation
541+
- name: Get latest tag name
542+
id: latest_tag
543+
if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }}
544+
run: echo "tag=$(./scripts/image_tag.sh --version latest)" >> $GITHUB_OUTPUT
545+
546+
# If this is the highest version according to semver, also attest the "latest" tag
547+
- name: GitHub Attestation for "latest" Docker image
548+
id: attest_latest
549+
if: ${{ !inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' }}
550+
continue-on-error: true
551+
uses: actions/attest@a63cfcc7d1aab266ee064c58250cfc2c7d07bc31 # v2.2.1
552+
with:
553+
subject-name: ${{ steps.latest_tag.outputs.tag }}
554+
predicate-type: "https://slsa.dev/provenance/v1"
555+
predicate: |
556+
{
557+
"buildType": "https://github.com/actions/runner-images/",
558+
"builder": {
559+
"id": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
560+
},
561+
"invocation": {
562+
"configSource": {
563+
"uri": "git+https://github.com/${{ github.repository }}@${{ github.ref }}",
564+
"digest": {
565+
"sha1": "${{ github.sha }}"
566+
},
567+
"entryPoint": ".github/workflows/release.yaml"
568+
},
569+
"environment": {
570+
"github_workflow": "${{ github.workflow }}",
571+
"github_run_id": "${{ github.run_id }}"
572+
}
573+
},
574+
"metadata": {
575+
"buildInvocationID": "${{ github.run_id }}",
576+
"completeness": {
577+
"environment": true,
578+
"materials": true
579+
}
580+
}
581+
}
582+
push-to-registry: true
583+
584+
# Report attestation failures but don't fail the workflow
585+
- name: Check attestation status
586+
if: ${{ !inputs.dry_run }}
587+
run: |
588+
if [[ "${{ steps.attest_base.outcome }}" == "failure" && "${{ steps.attest_base.conclusion }}" != "skipped" ]]; then
589+
echo "::warning::GitHub attestation for base image failed"
590+
fi
591+
if [[ "${{ steps.attest_main.outcome }}" == "failure" ]]; then
592+
echo "::warning::GitHub attestation for main image failed"
593+
fi
594+
if [[ "${{ steps.attest_latest.outcome }}" == "failure" && "${{ steps.attest_latest.conclusion }}" != "skipped" ]]; then
595+
echo "::warning::GitHub attestation for latest image failed"
596+
fi
597+
431598
- name: Generate offline docs
432599
run: |
433600
version="$(./scripts/version.sh)"

0 commit comments

Comments
 (0)