switch to SPDX SBOM (#74) #5
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json | |
### This workflow uses the SLSA container provenance generator workflow | |
### (see https://github.com/slsa-framework/slsa-github-generator/blob/main/internal/builders/container/README.md) | |
### to generate a SLSA provenance type predicate for a container image built by ko and attesting to it and others | |
### using keyless signing. | |
name: release | |
on: | |
push: | |
tags: | |
- "*" | |
workflow_dispatch: {} | |
env: | |
REGISTRY: gcr.io/kubecost1 | |
IMAGE_NAME: disk-autoscaler | |
jobs: | |
# Publish with ko build | |
build: | |
runs-on: ubuntu-latest | |
permissions: | |
packages: write | |
contents: read | |
id-token: write | |
outputs: | |
image: ${{ steps.ko-build.outputs.image }} | |
digest: ${{ steps.set-digest.outputs.digest }} | |
sbom-digest: ${{ steps.calculate-sbom-hash.outputs.sbom_digest }} | |
# Re-declaring the global env vars as outputs as a workaround for reusable workflows | |
REGISTRY: ${{ env.REGISTRY }} | |
IMAGE_NAME: ${{ env.IMAGE_NAME }} | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | |
- name: Setup Golang | |
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 | |
with: | |
go-version: 1.22 | |
- name: Setup ko | |
run: go install github.com/google/ko@latest | |
- name: Authenticate to GCP | |
uses: google-github-actions/auth@v2 | |
with: | |
workload_identity_provider: projects/416184586880/locations/global/workloadIdentityPools/integrations/providers/github-provider | |
service_account: [email protected] | |
- name: Setup gcloud | |
uses: google-github-actions/setup-gcloud@v2 | |
with: | |
project_id: kubecost1 | |
- name: gcloud auth configure-docker | |
run: | | |
gcloud --quiet auth configure-docker | |
- name: Publish image and SBOM (SPDX) | |
id: ko-build | |
# IMAGE will be in format <registry>/<org>/<repo>@<digest> ex ghcr.io/johndoe/redis@sha256:1b85db3f261af51914867eeda20a25bedf72fa406619bcdd60f0658f27b2722d | |
run: | | |
tag=$(echo ${{ github.ref }} | cut -c11-) | |
export VERSION=$tag | |
export COMMIT_HASH=${{ github.sha }} | |
IMAGE=$(ko build ./cmd/diskautoscaler --bare -t latest -t ${{ github.sha }} -t ${tag} --sbom=spdx --sbom-dir=./) | |
echo "The image generated is: $IMAGE" | |
echo "## Image summary" >> $GITHUB_STEP_SUMMARY | |
echo "Built image: $IMAGE" >> $GITHUB_STEP_SUMMARY | |
echo "IMAGE=$IMAGE" >> $GITHUB_ENV | |
echo "image=$IMAGE" >> $GITHUB_OUTPUT | |
echo "Renaming output SBOM file to sbom.json." | |
for file in *.spdx.json; do | |
mv -- "$file" "sbom.json" | |
break # Only rename the first file | |
done | |
env: | |
KO_DOCKER_REPO: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
- name: Set digest | |
id: set-digest | |
# DIGEST will be in format sha256:<hash> ex sha256:1b85db3f261af51914867eeda20a25bedf72fa406619bcdd60f0658f27b2722d | |
run: | | |
DIGEST=$(echo -n $IMAGE | cut -d '@' -f2) | |
echo "Digest from image is: $DIGEST" | |
echo "digest=$DIGEST" >> $GITHUB_OUTPUT | |
- name: Calculate SBOM file hash | |
id: calculate-sbom-hash | |
run: | | |
SBOM_DIGEST=$(sha256sum sbom.json | awk '{print $1}') | |
echo "sbom_digest=$SBOM_DIGEST" >> $GITHUB_OUTPUT | |
echo "Hash of sbom.json is: $SBOM_DIGEST" | |
- name: Upload SBOM | |
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 | |
with: | |
name: sbom.json | |
path: sbom.json | |
if-no-files-found: error | |
# Use the container SLSA provenance generator. | |
provenance: | |
permissions: | |
id-token: write | |
contents: write | |
actions: read | |
packages: write | |
needs: build | |
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected] | |
# Can't use env global vars in a reusable workflow. See https://github.com/actions/runner/issues/2372 | |
with: | |
digest: "${{ needs.build.outputs.digest }}" | |
image: ${{ needs.build.outputs.REGISTRY }}/${{needs.build.outputs.IMAGE_NAME}} | |
gcp-workload-identity-provider: projects/416184586880/locations/global/workloadIdentityPools/integrations/providers/github-provider | |
gcp-service-account: [email protected] | |
# Scan the image using Trivy. Check the hash of the scan file to ensure no tampering between jobs. | |
scan: | |
runs-on: ubuntu-latest | |
needs: [provenance, build] | |
permissions: | |
contents: read | |
outputs: | |
scan-digest: ${{ steps.calculate-scan-hash.outputs.scan_digest }} | |
env: | |
IMAGE: "${{ needs.build.outputs.image }}" | |
steps: | |
- name: Scan for vulnerabilities | |
uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # v0.20.0 (Trivy v0.51.1) | |
with: | |
image-ref: ${{ env.IMAGE }} | |
format: cosign-vuln | |
ignore-unfixed: true | |
output: scan.json | |
- name: Calculate scan file hash | |
id: calculate-scan-hash | |
run: | | |
SCAN_DIGEST=$(sha256sum scan.json | awk '{print $1}') | |
echo "scan_digest=$SCAN_DIGEST" >> $GITHUB_OUTPUT | |
echo "Hash of scan.json is: $SCAN_DIGEST" | |
- name: Upload vulnerability scan report | |
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 | |
with: | |
name: scan.json | |
path: scan.json | |
if-no-files-found: error | |
# Uses Cosign to sign and attest the scan and SBOM. Uses keyless signing for all steps. | |
attest: | |
runs-on: ubuntu-latest | |
permissions: | |
contents: write | |
actions: read | |
packages: write | |
id-token: write # Needed for OIDC and keyless signing | |
env: | |
IMAGE: "${{ needs.build.outputs.image }}" | |
SCAN_DIGEST: "${{ needs.scan.outputs.scan-digest }}" | |
SBOM_DIGEST: "${{ needs.build.outputs.sbom-digest }}" | |
needs: [provenance, scan, build] | |
steps: | |
- name: Checkout code | |
uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 | |
- name: Authenticate to GCP | |
uses: google-github-actions/auth@v2 | |
with: | |
workload_identity_provider: projects/416184586880/locations/global/workloadIdentityPools/integrations/providers/github-provider | |
service_account: [email protected] | |
- name: Setup gcloud | |
uses: google-github-actions/setup-gcloud@v2 | |
with: | |
project_id: kubecost1 | |
- name: gcloud auth configure-docker | |
run: | | |
gcloud --quiet auth configure-docker | |
- name: Download files | |
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 | |
with: | |
merge-multiple: true | |
path: ./ | |
# Detect potential tampering with the files by comparing the pre-upload digest in the previous | |
# job with the post-download digest in this job. Fail if there is a mismatch. | |
- name: Verify scan and SBOM files | |
run: | | |
set -euo pipefail | |
echo "Hash of scan.json should be: $SCAN_DIGEST" | |
COMPUTED_HASH=$(sha256sum scan.json | awk '{print $1}') | |
echo "The current computed hash for scan.json is: $COMPUTED_HASH" | |
echo "If the two above hashes don't match, scan.json has been tampered with." | |
echo "$SCAN_DIGEST scan.json" | sha256sum --strict --check --status || exit -2 | |
echo "--------------------------------" | |
echo "Hash of sbom.json should be: $SBOM_DIGEST" | |
COMPUTED_HASH=$(sha256sum sbom.json | awk '{print $1}') | |
echo "The current computed hash for sbom.json is: $COMPUTED_HASH" | |
echo "If the two above hashes don't match, sbom.json has been tampered with." | |
echo "$SBOM_DIGEST sbom.json" | sha256sum --strict --check --status || exit -2 | |
- name: Install Cosign | |
uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0 | |
with: | |
cosign-release: v2.2.4 | |
- name: Sign image | |
run: | | |
cosign sign -a sha=${{ github.sha }} -a run_id=${{ github.run_id }} -a repo=${{ github.repository }} -a workflow=${{ github.workflow }} ${{ env.IMAGE }} --output-signature=./signature.sig -y | |
echo "## Supply Chain Summary" >> $GITHUB_STEP_SUMMARY | |
echo "Image signed: :heavy_check_mark:" >> $GITHUB_STEP_SUMMARY | |
- name: Attest SBOM | |
run: | | |
cosign attest --predicate sbom.json --type spdxjson ${{ env.IMAGE }} -y | |
echo "Image SBOM attested: :heavy_check_mark:" >> $GITHUB_STEP_SUMMARY | |
- name: Attest Scan | |
run: | | |
cosign attest --predicate scan.json --type vuln ${{ env.IMAGE }} -y | |
echo "Image vulnerability scan attested: :heavy_check_mark:" >> $GITHUB_STEP_SUMMARY | |
- name: Update install.yaml with new image | |
run: | | |
yq -i 'select(.kind=="Deployment") .spec.template.spec.containers[0].image = "${{ env.IMAGE }}"' manifests/install.yaml | |
- name: Add files to release assets | |
uses: softprops/action-gh-release@v2 | |
with: | |
files: | | |
scan.json | |
sbom.json | |
signature.sig | |
manifests/install.yaml |