Skip to content

Add SBOMs, full provenance, cosign signatures and vulnerability scans - #109

Merged
SteveGilvarry merged 1 commit into
ZoneMinder:masterfrom
SteveGilvarry:supply-chain
Oct 25, 2026
Merged

SteveGilvarry merged 1 commit into
ZoneMinder:masterfrom
SteveGilvarry:supply-chain

Conversation

@SteveGilvarry

Copy link
Copy Markdown
Member

Users can now check what's in an image, where it came from, and that nobody changed it after CI built it.

SBOM and provenance. Every pushed image carries an SBOM, a list of every package inside it with versions. It also carries a full build provenance record: commit, workflow and build arguments. Both are stored as attestations next to the image, so pulls don't get bigger. Read them with docker buildx imagetools inspect <image> --format '{{json .SBOM}}'. Docker Scout and Grype use the SBOM directly.

Signing. The merge job signs each multi-arch index with cosign, keyless through GitHub's OIDC token, so there's no key to manage. Signatures are stored per repo, so it signs in zoneminder/zoneminder, zoneminderhq/zoneminder and GHCR. The CUDA workflow now pushes with build-push-action, using the cache from its build step, instead of docker push. That gives it the same attestations, and it signs the same way. The README shows how to check a signature:

cosign verify zoneminder/zoneminder:latest \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github\.com/ZoneMinder/zmdockerfiles/\.github/workflows/(images|pyzm-cuda)\.yml@refs/heads/master$'

Scanning. The amd64 build jobs scan the smoke-tested zm-es and pyzm-serve images with Grype.

  • Every run's summary lists fixable findings scoring 7.0 or more. On master, the full results go to the Security tab. Fork PRs only get the summary, because their token is read-only.
  • Scans never block a push. Holding an image back for one CVE would also hold back the Debian security fixes in that rebuild.
  • I chose Grype over Trivy because of the March 2026 trivy-action compromise. Hijacked tags stole CI secrets.
  • The scan runs before any registry login, so the step never has Docker Hub credentials available. All three new actions are pinned to a full commit: anchore/scan-action v7.4.2 and github/codeql-action v4.39.1 (both immutable releases), and sigstore/cosign-installer v4.1.2.

What the first scan found, run locally on yesterday's zm-es:

  • go2rtc 1.9.14 was built with Go 1.25.6. That gives a critical in Go's standard library plus about 30 highs in stdlib, x/crypto, x/net and pion. 1.9.14 is still go2rtc's latest release, from January. A follow-up could build go2rtc from source with a current Go.
  • ZoneMinder bundles firebase/php-jwt v5.0.0, which has a critical advisory (GHSA-8xf4-w7qw-pjjw). That's for ZoneMinder upstream to fix.
  • libbrotli1 had a Debian fix the image didn't have yet. Rebuild without cache on schedule, verify downloads, use pipefail for CUDA #106's daily no-cache rebuild picks that up.

Tested:

  • actionlint passes.
  • Grype v0.121.0 against zmsmoke:es on warlock, and the summary table against its real SARIF output.
  • On local registries on warlock, the merge-and-sign script ran with a stand-in cosign. It signs once per repo with the index digest, which is identical across registries. SBOM and provenance survive the merge and the cross-registry copy.
  • Not run yet: keyless signing for real, which needs GitHub's OIDC token. It runs on merge, or through test_push from a branch on this repo. Docker Hub may show cosign's sha256-<digest> signature entries in its tag list, depending on how it stores them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CKoPm5qySMSsCYJmCo8r2J

- Every pushed image now carries an SBOM and mode=max provenance as
  attestations. They survive the per-arch merge and the copies to
  zoneminderhq and GHCR.
- The merge job signs each multi-arch index with cosign, keyless through
  GitHub's OIDC token, in every repo. The CUDA workflow pushes with
  build-push-action instead of docker push, so it gets the same
  attestations. It then signs its image the same way.
- The amd64 build jobs scan the smoke-tested zm-es and pyzm-serve images
  with Grype before any registry login. Fixable findings scoring 7.0 or
  more go into the job summary, and on master the full results go to the
  Security tab. Scans never block a push.
- New third-party actions are pinned to a commit: anchore/scan-action,
  sigstore/cosign-installer and github/codeql-action.
- The README has a new section on verifying images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKoPm5qySMSsCYJmCo8r2J
@SteveGilvarry
SteveGilvarry merged commit 7647bfc into ZoneMinder:master Oct 25, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant