Skopeo for Image Verification
๐ŸŠ

Skopeo for Image Verification

Tags
Tech
Gitlab
devops
CICD
Docker
Podman
mlops
Cosign
skopeo
github
Pipelines
Published
May 12, 2026
Author
Mohammad Malik

Skopeo + Cosign for image verification in your registry workflow

Published for Platform / SRE teamsย  |ย  ~800 words | Part 2
If you havenโ€™t read part 1 of this 3 part series:

Why image signing matters for SRE teams

A container registry is only as trustworthy as the images it holds. Without signing, there is no cryptographic guarantee that the image you pull matches the one your build pipeline produced. Skopeo handles the transport; Cosign handles the attestation.

Tools

  • skopeo โ€” copy and inspect images across registries
  • cosign (Sigstore) โ€” sign, verify, and attach attestations to OCI artifacts
  • rekor (Sigstore) โ€” (optional) transparency log for signatures (sigstore.dev)

Installing Cosign

# Linux (amd64)
curl -sSfL https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 -o /usr/local/bin/cosign && chmod +x /usr/local/bin/cosign
# Verify the cosign binary itself (bootstrap trust)
cosign verify-blob --certificate-identity=... cosign-linux-amd64
# macOS
brew install cosign

Workflow: sign after build, verify before deploy

The signing and verification steps are separate. (co)Sign in your build pipeline; verify in your admission controller or deploy pipeline.
Step 1: Generate a key pair (or use keyless)
# Key-based (store private key in a secret manager) cosign generate-key-pair # Produces: cosign.key (private), cosign.pub (public) # Keyless (uses OIDC โ€” works well with GitHub Actions, GKE Workload Identity) # No key management needed; identity is bound to the OIDC token

Step 2: Copy the image with Skopeo, then sign
IMAGE=registry.internal.example.com/app:$GIT_SHA # Copy the image into your registry skopeo copy \ --authfile /tmp/auth.json docker://build-cache.internal/app:$GIT_SHA \ docker://$IMAGE # Pin to the digest (sign the digest, not the mutable tag) DIGEST=$(skopeo inspect --format '{{.Digest}}' docker://$IMAGE) # Sign cosign sign --key cosign.key registry.internal.example.com/app@$DIGEST

Tip: Always sign the digest, not the tag. Tags are mutable. Signing a digest gives you a tamper-evident guarantee tied to specific image content.

Step 3: Verify before deploying
IMAGE=registry.internal.example.com/app:$GIT_SHA DIGEST=$(skopeo inspect --format '{{.Digest}}' docker://$IMAGE) cosign verify \ --key cosign.pub \ registry.internal.example.com/app@$DIGEST # Non-zero exit code = verification failed = block the deploy

Keyless signing in GitHub Actions

Keyless mode uses GitHub's OIDC token to bind the signature to the workflow identity. No secrets to manage.
- name: Sign Image env: COSIGN_EXPERIMENTAL: '1' ย  # enables keyless run: | DIGEST=$(skopeo inspect --raw \ docker://ghcr.io/${{ github.repository }}:${{ github.sha }} \ | jq -r '.config.digest') cosign sign \ ghcr.io/${{ github.repository }}@$DIGEST - name: Verify image env: COSIGN_EXPERIMENTAL: '1' run: | cosign verify \ --certificate-identity-regexp 'https://github.com/${{ github.repository }}' \ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \ ghcr.io/${{ github.repository }}@$DIGEST

Attaching SBOMs as OCI attestations

# Generate SBOM with syft syft registry.internal.example.com/app:$GIT_SHA -o spdx-json > sbom.spdx.json # Attach as attestation cosign attest --key cosign.key \ --predicate sbom.spdx.json \ --type spdxjson \ registry.internal.example.com/app@$DIGEST # Copy image + attestations to another registry skopeo copy --all \ docker://registry.internal.example.com/app:$GIT_SHA \ docker://prod-registry.example.com/app:$GIT_SHA # --all includes the cosign signature and attestation OCI tags

Enforcing verification in Kubernetes

Use Kyverno or Sigstore Policy Controller to reject pods whose images are unsigned or have an invalid signature:
# Kyverno ClusterPolicy snippet rules: - name: verify-image-signature match: resources: kinds: [Pod] verifyImages: - imageReferences: - 'registry.internal.example.com/*' attestors: - entries: - keys: publicKeys: |- -----BEGIN PUBLIC KEY----- <your cosign.pub content> -----END PUBLIC KEY-----

Stay tuned for part 3: Skopeo in Air-Gapped environments