Image verification#

Every Strata image published to ghcr.io/danchupin/strata is signed, attested, and inventoried by the release pipeline (.github/workflows/release-image.yml). This page is the operator playbook for verifying that supply-chain evidence before the image reaches a node, plus two Kubernetes admission-control shapes that make the check mandatory at deploy time.

The pipeline produces four pieces of evidence, all anchored to the same image digest:

EvidenceProducerStoryVerified with
cosign signature (keyless OIDC)sigstore/cosignUS-008cosign verify
SLSA L3 provenanceslsa-github-generatorUS-006cosign verify-attestation
SPDX 2.3 SBOManchore/sbom-action (syft)US-007grype / trivy sbom
License inventorygo-licensesUS-009go-licenses report

What signing buys you#

Keyless signing gives you a non-repudiable provenance trail from source to running image: the GitHub Actions OIDC identity that built the image is the signer, Fulcio issues a short-lived certificate bound to that identity, and the signature lands in the Rekor public transparency log. Because the signature covers the image digest (not a mutable tag), a verified image is tamper-proof — repush a different layer and the signature no longer matches. There is no maintainer-held private key to leak or rotate; the trust root is GitHub’s OIDC issuer plus the public Sigstore infrastructure. The Rekor entry is append-only and publicly queryable, so a forensic investigation can prove exactly which workflow run, at which commit, produced a given digest — even years later.

Verify recipe#

Run the full chain before deploying. Replace v0.0.1-alpha.1 with the tag you are about to ship; the first released tag is v0.0.1-alpha.1.

TAG=v0.0.1-alpha.1
IMAGE=ghcr.io/danchupin/strata:${TAG}

(a) cosign signature — keyless OIDC identity#

The regexp pair is anchored (^…$), escaped (\.), and suffix-matched (/.*) so it accepts any workflow path under the repo while rejecting look-alike identities (danchupin/strata-evil, evil-danchupin/strata). Copy-paste-runnable:

cosign verify \
  --certificate-identity-regexp='^https://github\.com/danchupin/strata/.*' \
  --certificate-oidc-issuer-regexp='^https://token\.actions\.githubusercontent\.com$' \
  "${IMAGE}"

A pass prints the verified signature(s) and the certificate subject. Any identity or issuer mismatch exits non-zero — never deploy on a non-zero exit.

(b) SLSA provenance attestation#

Proves the image was built by the SLSA L3 generator, not a local docker push:

cosign verify-attestation \
  --type slsaprovenance \
  --certificate-identity-regexp='^https://github\.com/danchupin/strata/.*' \
  --certificate-oidc-issuer-regexp='^https://token\.actions\.githubusercontent\.com$' \
  "${IMAGE}"

(c) SBOM vulnerability scan#

Download the SPDX SBOM attached to the GitHub Release and scan it for CVEs:

gh release download "${TAG}" -p '*sbom*' -R danchupin/strata
grype "sbom:strata-${TAG}-sbom.spdx.json"
# or:  trivy sbom "strata-${TAG}-sbom.spdx.json"

(d) License inventory check#

Verify the SBOM carries no banned (forbidden / restricted) licenses. The release CSV inventory is the authoritative artifact (US-009); to re-derive from source at a tag:

git checkout "${TAG}"
go-licenses report ./... 2>/dev/null > license-report.csv
go-licenses check ./... --disallowed_types=forbidden,restricted

A zero exit confirms no GPL / AGPL / LGPL / CDDL / EPL imports slipped in.

Rekor transparency-log query#

For forensic use, query Rekor directly by the image digest — this proves a signature entry exists in the public append-only log independent of the registry:

DIGEST=$(cosign triangulate --type digest "${IMAGE}")   # sha256:…
rekor-cli search --sha "${DIGEST#sha256:}"
# inspect a returned log index:
rekor-cli get --log-index <index>

The returned entry carries the Fulcio certificate (signer identity), the inclusion proof, and the signed-entry timestamp — sufficient to attribute the build to a specific workflow run.

Kubernetes admission control#

Make verification mandatory: reject any pod whose image is not a validly signed ghcr.io/danchupin/strata digest. Two equivalent shapes ship in deploy/k8s/admission-controllers/ — pick whichever policy engine your cluster already runs.

Kyverno#

kyverno-strata-image-signed.yaml is a ClusterPolicy with a verifyImages rule. It matches the ghcr.io/danchupin/strata* glob, requires a keyless cosign signature whose certificate identity + issuer match the same anchored regexps as the CLI recipe, and mutates the image reference to its verified digest so later layer swaps cannot bypass the check:

verifyImages:
  - imageReferences:
      - "ghcr.io/danchupin/strata*"
    attestors:
      - entries:
          - keyless:
              subject: "https://github.com/danchupin/strata/*"
              issuer: "https://token.actions.githubusercontent.com"

Install Kyverno, then kubectl apply -f deploy/k8s/admission-controllers/kyverno-strata-image-signed.yaml. With failurePolicy: Fail an unsigned image is rejected at admission.

Sigstore policy-controller#

sigstore-policy-strata.yaml is a ClusterImagePolicy (the Sigstore policy-controller CRD). Same contract, native Sigstore shape:

authorities:
  - keyless:
      identities:
        - issuer: "https://token.actions.githubusercontent.com"
          subjectRegExp: "^https://github\\.com/danchupin/strata/.*"

Install the policy-controller, label the namespace (policy.sigstore.dev/include: "true"), then apply the CR. Pods pulling an unsigned Strata image are denied.

Troubleshooting#

ErrorCauseFix
certificate verification failed / identity mismatchThe --certificate-identity-regexp or --certificate-oidc-issuer-regexp does not match the signer.Copy the regexp pair from the Verify recipe verbatim — anchored, escaped, suffix-matched. A look-alike repo (strata-foo) is correctly rejected.
no matching attestationsYou ran verify-attestation against an image that has a signature but no SLSA provenance, or a wrong --type.Use --type slsaprovenance. Older locally-built images have no provenance — only pipeline-released tags do.
image not signed / no signatures foundThe tag predates the signing pipeline, was built locally, or you verified a mutable tag that was repushed unsigned.Verify a pipeline-released tag (v0.0.1-alpha.1 onward). Pin to a digest, not a floating tag.
Rekor entry not foundSigstore’s transparency log was unreachable at verify time, or you searched the wrong digest.Re-run with network access to rekor.sigstore.dev; confirm the digest via cosign triangulate. For air-gapped verify, attach a bundled Rekor proof with --bundle.
error getting fulcio roots / TUF init hangFirst-run TUF metadata fetch is blocked or slow.Ensure egress to tuf-repo-cdn.sigstore.dev; pre-warm with cosign initialize.

See also#