diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d1d5bf9c2959c..d25cb84173326 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1180,6 +1180,33 @@ jobs: done fi + - name: SBOM Generation and Attestation + if: github.ref == 'refs/heads/main' + env: + COSIGN_EXPERIMENTAL: 1 + run: | + set -euxo pipefail + + # Define image base and tags + IMAGE_BASE="ghcr.io/coder/coder-preview" + TAGS=("${{ steps.build-docker.outputs.tag }}" "main" "latest") + + # Generate and attest SBOM for each tag + for tag in "${TAGS[@]}"; do + IMAGE="${IMAGE_BASE}:${tag}" + SBOM_FILE="coder_sbom_${tag//[:\/]/_}.spdx.json" + + echo "Generating SBOM for image: ${IMAGE}" + syft "${IMAGE}" -o spdx-json > "${SBOM_FILE}" + + echo "Attesting SBOM to image: ${IMAGE}" + cosign clean "${IMAGE}" + cosign attest --type spdxjson \ + --predicate "${SBOM_FILE}" \ + --yes \ + "${IMAGE}" + done + # GitHub attestation provides SLSA provenance for the Docker images, establishing a verifiable # record that these images were built in GitHub Actions with specific inputs and environment. # This complements our existing cosign attestations which focus on SBOMs. diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 07a57b8ad939b..eb3983dac807f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -496,6 +496,39 @@ jobs: env: CODER_BASE_IMAGE_TAG: ${{ steps.image-base-tag.outputs.tag }} + - name: SBOM Generation and Attestation + if: ${{ !inputs.dry_run }} + env: + COSIGN_EXPERIMENTAL: "1" + run: | + set -euxo pipefail + + # Generate SBOM for multi-arch image with version in filename + echo "Generating SBOM for multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" + syft "${{ steps.build_docker.outputs.multiarch_image }}" -o spdx-json > coder_${{ steps.version.outputs.version }}_sbom.spdx.json + + # Attest SBOM to multi-arch image + echo "Attesting SBOM to multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}" + cosign clean "${{ steps.build_docker.outputs.multiarch_image }}" + cosign attest --type spdxjson \ + --predicate coder_${{ steps.version.outputs.version }}_sbom.spdx.json \ + --yes \ + "${{ steps.build_docker.outputs.multiarch_image }}" + + # If latest tag was created, also attest it + if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then + latest_tag="$(./scripts/image_tag.sh --version latest)" + echo "Generating SBOM for latest image: ${latest_tag}" + syft "${latest_tag}" -o spdx-json > coder_latest_sbom.spdx.json + + echo "Attesting SBOM to latest image: ${latest_tag}" + cosign clean "${latest_tag}" + cosign attest --type spdxjson \ + --predicate coder_latest_sbom.spdx.json \ + --yes \ + "${latest_tag}" + fi + - name: GitHub Attestation for Docker image id: attest_main if: ${{ !inputs.dry_run }} @@ -612,16 +645,27 @@ jobs: fi declare -p publish_args + # Build the list of files to publish + files=( + ./build/*_installer.exe + ./build/*.zip + ./build/*.tar.gz + ./build/*.tgz + ./build/*.apk + ./build/*.deb + ./build/*.rpm + ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json + ) + + # Only include the latest SBOM file if it was created + if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then + files+=(./coder_latest_sbom.spdx.json) + fi + ./scripts/release/publish.sh \ "${publish_args[@]}" \ --release-notes-file "$CODER_RELEASE_NOTES_FILE" \ - ./build/*_installer.exe \ - ./build/*.zip \ - ./build/*.tar.gz \ - ./build/*.tgz \ - ./build/*.apk \ - ./build/*.deb \ - ./build/*.rpm + "${files[@]}" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CODER_GPG_RELEASE_KEY_BASE64: ${{ secrets.GPG_RELEASE_KEY_BASE64 }} @@ -663,6 +707,15 @@ jobs: ./build/*.apk ./build/*.deb ./build/*.rpm + ./coder_${{ steps.version.outputs.version }}_sbom.spdx.json + retention-days: 7 + + - name: Upload latest sbom artifact to actions (if dry-run) + if: inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true' + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: latest-sbom-artifact + path: ./coder_latest_sbom.spdx.json retention-days: 7 - name: Send repository-dispatch event diff --git a/scripts/build_docker.sh b/scripts/build_docker.sh index 7f1ba93840403..14d45d0913b6b 100755 --- a/scripts/build_docker.sh +++ b/scripts/build_docker.sh @@ -153,17 +153,6 @@ if [[ "$push" == 1 ]]; then docker push "$image_tag" 1>&2 fi -log "--- Generating SBOM for Docker image ($image_tag)" -syft "$image_tag" -o spdx-json >"${image_tag//[:\/]/_}.spdx.json" - -if [[ "$push" == 1 ]]; then - log "--- Attesting SBOM to Docker image for $arch ($image_tag)" - COSIGN_EXPERIMENTAL=1 cosign clean "$image_tag" - - COSIGN_EXPERIMENTAL=1 cosign attest --type spdxjson \ - --predicate "${image_tag//[:\/]/_}.spdx.json" \ - --yes \ - "$image_tag" -fi +# SBOM generation and attestation moved to the GitHub workflow echo "$image_tag"