From e3e1757a96293877680150e236519155cc242949 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Wed, 1 Jul 2026 15:18:05 +0200 Subject: [PATCH 1/3] ci: scan and sign published container images --- .github/workflows/ci.yml | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e5f973..f6bf1d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -172,6 +172,7 @@ jobs: permissions: contents: read packages: write + id-token: write # keyless cosign signing via OIDC steps: - name: Download digests uses: actions/download-artifact@v8 @@ -202,3 +203,49 @@ jobs: docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) + - name: Install cosign + uses: sigstore/cosign-installer@v3 + - name: Sign the published image (keyless) + run: | + tag=$(jq -cr '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON") + digest=$(docker buildx imagetools inspect "$tag" --format '{{.Manifest.Digest}}') + cosign sign --yes "ghcr.io/${{ github.repository }}@${digest}" + + # Build the runtime image and scan it for OS/dependency vulnerabilities. + # Non-blocking for now (exit-code 0): findings surface in the Security tab. + # Flip exit-code to 1 to gate once the baseline is clean. + image-scan: + name: Image Scan + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + - name: Build image (amd64) for scanning + uses: docker/build-push-action@v7 + with: + context: . + file: docker/Dockerfile + platforms: linux/amd64 + load: true + tags: postguard-business:scan + cache-from: type=gha + - name: Trivy vulnerability scan + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: postguard-business:scan + format: sarif + output: trivy-results.sarif + severity: HIGH,CRITICAL + ignore-unfixed: true + exit-code: '0' + - name: Upload Trivy results + if: always() + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: trivy-results.sarif + category: trivy From da8e1ae035e9d15eb4cce8c8dc6926c33cac2e57 Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Wed, 1 Jul 2026 15:22:29 +0200 Subject: [PATCH 2/3] ci: run Trivy from its official image (avoids compromised trivy-action) --- .github/workflows/ci.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f6bf1d2..63e7699 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -234,15 +234,20 @@ jobs: load: true tags: postguard-business:scan cache-from: type=gha + # Run Trivy from its official image rather than the GitHub Action, which + # had a supply-chain compromise advisory (GHSA-69fq-xp46-6x23). - name: Trivy vulnerability scan - uses: aquasecurity/trivy-action@0.28.0 - with: - image-ref: postguard-business:scan - format: sarif - output: trivy-results.sarif - severity: HIGH,CRITICAL - ignore-unfixed: true - exit-code: '0' + run: | + docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$PWD:/work" \ + aquasec/trivy:latest image \ + --severity HIGH,CRITICAL \ + --ignore-unfixed \ + --format sarif \ + --output /work/trivy-results.sarif \ + --exit-code 0 \ + postguard-business:scan - name: Upload Trivy results if: always() uses: github/codeql-action/upload-sarif@v3 From 23c09797f8222f9d7cb2a030a004392ca42d7e1e Mon Sep 17 00:00:00 2001 From: Ruben Hensen Date: Wed, 1 Jul 2026 15:39:36 +0200 Subject: [PATCH 3/3] =?UTF-8?q?ci:=20address=20review=20=E2=80=94=20pin=20?= =?UTF-8?q?Trivy,=20gate=20signing=20to=20non-PR,=20guard=20SARIF=20upload?= =?UTF-8?q?,=20needs=20build?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63e7699..9adcadf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -203,9 +203,15 @@ jobs: docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf 'ghcr.io/${{ github.repository }}@sha256:%s ' *) + # Sign only on pushes to main/releases — not PRs. Signing every PR would + # record throwaway pr-N images in the public Rekor transparency log and + # leave orphan .sig tags in GHCR. The cosign path was smoke-tested on the + # PR that introduced it (#118). - name: Install cosign + if: github.event_name != 'pull_request' uses: sigstore/cosign-installer@v3 - name: Sign the published image (keyless) + if: github.event_name != 'pull_request' run: | tag=$(jq -cr '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON") digest=$(docker buildx imagetools inspect "$tag" --format '{{.Manifest.Digest}}') @@ -216,6 +222,7 @@ jobs: # Flip exit-code to 1 to gate once the baseline is clean. image-scan: name: Image Scan + needs: build runs-on: ubuntu-latest permissions: contents: read @@ -241,7 +248,7 @@ jobs: docker run --rm \ -v /var/run/docker.sock:/var/run/docker.sock \ -v "$PWD:/work" \ - aquasec/trivy:latest image \ + aquasec/trivy:0.72.0 image \ --severity HIGH,CRITICAL \ --ignore-unfixed \ --format sarif \ @@ -249,7 +256,7 @@ jobs: --exit-code 0 \ postguard-business:scan - name: Upload Trivy results - if: always() + if: hashFiles('trivy-results.sarif') != '' uses: github/codeql-action/upload-sarif@v3 with: sarif_file: trivy-results.sarif