Image Verification

Verify sockguard images and release tarballs with cosign keyless GitHub Actions OIDC signatures.

Every sockguard release is signed with cosign using GitHub Actions OIDC keyless signing. Before running a sockguard image in production, verify it against the expected signing identity — this is the only reliable way to be sure you're running a binary we actually published.

What cosign verifies

The cosign verify command below verifies the image signature:

  • The image digest was signed by a workflow whose OIDC identity matches the release pipeline in this repo.
  • The signature is present in Sigstore transparency metadata.
  • The certificate chains to the expected GitHub Actions OIDC issuer.

The release pipeline also requests SBOM output during image build and publishes GitHub build-provenance attestations when repository visibility supports them. Treat those as separate supply-chain artifacts: validate the image or release tarball signature first, then inspect SBOM/provenance with your registry or GitHub attestation tooling when you need that extra evidence. A successful cosign verify alone does not prove SBOM or provenance contents.

The signing identity is the full GitHub Actions workflow reference:

  • Certificate identity pattern: ^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$
  • OIDC issuer: https://token.actions.githubusercontent.com

If either value differs from what cosign finds in the signature, the image was not produced by our release pipeline — do not run it.

Prerequisites

Install cosign (v2 or later is required for keyless verification):

# macOS
brew install cosign

# Linux (pre-built binary, pin the sha as you see fit)
curl -Lo cosign https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign
sudo mv cosign /usr/local/bin/

One-liner verify

Replace <TAG> with the release tag you're pulling (for example 1.3.0 or latest):

cosign verify \
  --certificate-identity-regexp '^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  ghcr.io/codeswhat/sockguard:<TAG>

A successful verification looks like:

Verification for ghcr.io/codeswhat/sockguard:<TAG> --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates
[{...}]

The last line is a JSON object with the signing certificate details; any well-formed JSON there is proof the signature matched. If cosign returns a non-zero exit code, the signature is missing or does not match the expected identity.

Pinning by digest

For reproducible deployments, prefer verifying and pulling by SHA256 digest rather than by tag — a tag can be re-pointed, a digest cannot.

# Resolve the current digest for a tag
digest=$(docker buildx imagetools inspect \
  ghcr.io/codeswhat/sockguard:<TAG> --format '{{json .Manifest.Digest}}' \
  | tr -d '"')

# Verify against the digest
cosign verify \
  --certificate-identity-regexp '^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  "ghcr.io/codeswhat/sockguard@${digest}"

# Then reference the same digest in your compose file
# image: ghcr.io/codeswhat/sockguard@sha256:...

If verification fails

Do not run the image. Treat a verification failure as a potential supply-chain compromise until proven otherwise:

  • Double-check you pasted the full certificate-identity-regexp and that the escaping survived your shell (^, $, and .+ must all appear in the regex).
  • Run cosign version and confirm you have cosign v2 or later.
  • Try the verification again with a fresh registry pull; a stale cached manifest can sometimes produce confusing errors.
  • If the identity or issuer values differ from the ones published here, stop — open a private security advisory via the process in SECURITY.md so we can investigate.

Verifying release tarballs

The GitHub release assets (sockguard-v<TAG>.tar.gz) are also cosign-signed. The release workflow uploads a sigstore bundle (.sigstore.json) alongside each tarball:

cosign verify-blob \
  --bundle "sockguard-v<TAG>.tar.gz.sigstore.json" \
  --certificate-identity-regexp '^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  "sockguard-v<TAG>.tar.gz"

Same interpretation as the image verify: a clean exit code is a passing signature, anything else means stop and investigate.

Sockguard v2 and later publish only the sigstore bundle above. Published releases in the v1.7 line also carry a legacy detached signature (.sig) and signing certificate (.pem), so tooling scripted against the older cosign verify-blob --signature --certificate invocation keeps working. v1.7.2 has no GitHub release or release assets because its publish workflow failed.

cosign verify-blob \
  --certificate "sockguard-v<TAG>.tar.gz.pem" \
  --signature   "sockguard-v<TAG>.tar.gz.sig" \
  --certificate-identity-regexp '^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  "sockguard-v<TAG>.tar.gz"

Cosign's own sign-blob deprecated those output flags in favor of the bundle format. Use --bundle for current releases.

When release provenance is published, the release also includes sockguard-v<TAG>.tar.gz.intoto.jsonl. Verify that attestation separately from the tarball's cosign bundle before relying on it for SLSA evidence.

Verifying the per-platform binary archives

sockguard-v<TAG>.tar.gz above is the source snapshot. The archives you download to actually run sockguard are the per-platform ones, sockguard_<VERSION>_<OS>_<ARCH>.tar.gz (note the underscores and the missing v). They're easy to confuse, so check which file you have before picking a command.

Each per-platform archive ships its own .sigstore.json bundle, and so does checksums.txt. Verify a single archive directly:

cosign verify-blob \
  --bundle "sockguard_<VERSION>_linux_amd64.tar.gz.sigstore.json" \
  --certificate-identity-regexp '^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  "sockguard_<VERSION>_linux_amd64.tar.gz"

Or verify checksums.txt once and check any archive against it, which is the cheaper route when you're pulling several platforms:

cosign verify-blob \
  --bundle "checksums.txt.sigstore.json" \
  --certificate-identity-regexp '^https://github.com/CodesWhat/sockguard/.github/workflows/release-from-tag.yml@refs/tags/.+$' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  "checksums.txt"

sha256sum --check --ignore-missing checksums.txt

Sockguard v2 and later publish only .sigstore.json bundles. v1.7.5 dual-published those bundles with the legacy .sig/.pem pair for the source tarball, checksums, and binary archives. v1.7.3-v1.7.4 published only the legacy pair for checksums and binary archives; v1.7.0-v1.7.1 did not sign those binary artifacts. v1.7.2 has no GitHub release or release assets because its publish workflow failed. Use the assets actually attached to the historical release rather than assuming every v1.7 tag has the same set.

The archives also carry GitHub build-provenance attestations, which record the workflow and commit that produced them:

gh attestation verify "sockguard_<VERSION>_linux_amd64.tar.gz" \
  --repo CodesWhat/sockguard

Each archive additionally ships a CycloneDX SBOM as sockguard_<VERSION>_<OS>_<ARCH>.tar.gz.cyclonedx.json.

Signatures and provenance on the per-platform archives start with the first release cut after this change. Earlier releases have the SBOM only, so a missing .sigstore.json on an older tag is expected rather than a red flag.