Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Sign and Verify Artifacts with Sigstore cosign

By Kokil Thapa | Last reviewed: September 2026

Your deployment pipeline builds a release artifact every week. A compromised registry mirror or a leaked CI token could swap that binary before anyone notices. Learning to sign and verify artifacts with Sigstore cosign gives you cryptographic proof of origin without running your own PKI. Sigstore handles short-lived certificates through Fulcio, records signatures in Rekor, and lets cosign attach those signatures to OCI images, plain files, and SBOMs. This guide walks through install, signing modes, verification, and the CI patterns I use on production Linux deployment pipelines.

What is Sigstore cosign and why should you sign artifacts?

Cosign is the CLI from the Sigstore project for signing and verifying software artifacts. It supports container images, Helm charts, generic blobs, and signed attestations such as SBOMs and SLSA provenance.

Traditional GPG signing works, but key management is painful at team scale. Cosign offers two practical paths: keyless signing through OpenID Connect, or long-lived encrypted key pairs you store in your secrets manager. Both produce signatures verifiable by anyone with cosign installed.

On client projects where I maintain GitLab CI plus Deployer releases, artifact trust is not optional. A signed container image or release tarball lets the deploy step reject anything that did not come from your pipeline. That single check closes a common supply-chain gap between build and production.

Sigstore Cosign ArchitectureCosign CLIsign / verifyFulcio CAOIDC certsRekor LogtransparencyOIDC ProviderGitHub / GitLabArtifact Storeregistry / blobSign and verify artifacts with Sigstore cosign end-to-end
Sigstore cosign ties OIDC identity, short-lived certificates, and a public transparency log to signed artifacts

The three Sigstore components matter when you debug verification failures. Fulcio issues X.509 certificates bound to your CI identity. Rekor stores a tamper-evident log entry for each signature. Cosign attaches the signature to the artifact itself or stores it as OCI referrers metadata alongside container images.

If you already sign Git commits with GPG or SSH, cosign covers the next layer: the built output. Read the companion piece on signing commits for supply chain trust for a full chain from source to deploy.

Which artifact types cosign supports

  • OCI container images — Docker, GHCR, GitLab Container Registry, ECR, and any OCI-compliant registry
  • Generic blobs — tarballs, ZIP archives, PHP phar files, compiled binaries
  • Helm charts — packaged charts pushed as OCI artifacts
  • Attestations — in-toto statements for SBOMs, vulnerability scans, and SLSA provenance

How do you install cosign and choose a signing mode?

Install cosign on your workstation and CI runners before you touch production registries. The binary is a single static executable with no runtime dependencies.

Install cosign on Linux

# Download latest release (check https://github.com/sigstore/cosign/releases)
COSIGN_VERSION=v2.4.1
curl -LO "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-amd64"
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
cosign version

Verify the binary checksum against the release page before you install it on production servers. I treat cosign like any other supply-chain tool: download once, checksum, then promote the same binary to all runners.

Keyless signing vs static keys

Keyless signing uses your CI platform OIDC token. Cosign exchanges that token with Fulcio for a certificate valid about ten minutes. The private key never leaves memory. Rekor records the signature publicly.

Static key signing generates a long-lived key pair. You encrypt the private key with a passphrase and store it in GitLab CI variables, GitHub Actions secrets, or Ansible Vault. Verification uses the exported public key or a pinned key in your policy file.

CriteriaKeyless (OIDC)Static encrypted key
Key rotationAutomatic per jobManual rotation schedule
Identity bindingCI workflow + repo URLWhoever holds the key file
Offline verifyNeeds Rekor lookupPublic key only
Best forCloud CI with OIDCAir-gapped or legacy runners
Audit trailRekor transparency logYour own logging

For GitLab CI pipelines on shared EC2 infrastructure — the same pattern I use on sister legal-tech sites — keyless signing through id_tokens is the default choice in 2026. Static keys remain useful when OIDC is unavailable or when you sign artifacts outside CI.

Generate a static key pair

cosign generate-key-pair
# Creates cosign.key (private) and cosign.pub (public)
# Store cosign.key encrypted; commit cosign.pub or publish it

Never commit the private key. Export the public key to your repository root or a .well-known path so downstream teams can verify without asking you for files.

How do you sign container images, binaries, and blobs with cosign?

Signing is one command per artifact type. The syntax differs slightly between OCI images and flat files, but the verification model stays consistent.

Sign and Verify WorkflowCI Buildcompile imagecosign signattach sigPush RegistryGHCR / GitLabDeploy Gatecosign verifyRejected if signature missing or identity mismatchDeploy stops — no unsigned artifact reaches productionSign and verify artifacts with Sigstore cosign before every deploy
Production deploy gates should call cosign verify and fail closed on any mismatch

Sign a container image (keyless via GitHub Actions)

permissions:
  id-token: write
  contents: read
  packages: write

steps:
  - uses: sigstore/cosign-installer@v3
  - run: docker build -t ghcr.io/org/app:${{ github.sha }} .
  - run: docker push ghcr.io/org/app:${{ github.sha }}
  - run: |
      cosign sign --yes ghcr.io/org/app:${{ github.sha }}

The --yes flag skips the interactive prompt in CI. Fulcio binds the certificate to your GitHub workflow identity. Rekor stores the entry automatically.

Sign a container image (static key)

export COSIGN_PASSWORD="your-passphrase"
cosign sign --key cosign.key registry.example.com/myapp:1.4.2

Pass the passphrase through an environment variable, not a CLI flag. Shell history and process listings leak flags; environment injection from CI secret stores does not.

Sign a generic blob or release tarball

cosign sign-blob --key cosign.key \
  --output-signature app.tar.gz.sig \
  --output-certificate app.tar.gz.pem \
  app.tar.gz

Distribute three files together: the artifact, the .sig file, and the certificate. Verification uses cosign verify-blob with the same trio. This pattern works well for Laravel release archives built on CI and deployed via Deployer symlink swaps.

Attach an SBOM attestation

cosign attach sbom --sbom sbom.spdx.json \
  registry.example.com/myapp:1.4.2

cosign sign --key cosign.key \
  --attachment sbom \
  registry.example.com/myapp:1.4.2

SBOM attestations let security teams trace dependencies without rebuilding the image. Pair this with your existing artifact repository strategy so signed images and SBOMs live in one trusted registry.

  1. Build and tag the artifact with an immutable digest or version tag.
  2. Sign immediately after the build step — never sign a artifact that passed through manual upload.
  3. Push the artifact and signature to the registry in the same pipeline job.
  4. Record the digest in your deployment manifest or GitOps repo.
  5. Verify at deploy time against the pinned digest plus signature.

How do you verify signed artifacts in CI/CD pipelines?

Signing without verification is theatre. Every deploy job, Kubernetes admission hook, and manual pull script should call cosign verify and exit non-zero on failure.

Verify a keyless-signed image

cosign verify \
  --certificate-identity-regexp="https://github.com/myorg/.*" \
  --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \
  ghcr.io/myorg/app@sha256:abc123...

Pin the digest, not a floating tag. Tags are mutable; digests are not. A common mistake is verifying :latest while the registry silently retagged it overnight.

Verify with a static public key

cosign verify --key cosign.pub registry.example.com/myapp:1.4.2

Commit cosign.pub to your infrastructure repo. Rotation means generating a new key pair, re-signing all active artifacts, and updating the pinned public key in one coordinated change.

Verify a signed blob before install

cosign verify-blob \
  --key cosign.pub \
  --signature app.tar.gz.sig \
  app.tar.gz

On air-gapped servers, copy the public key out of band once. Verification then runs fully offline with no Rekor dependency.

Signing Mode DecisionCI has OIDC?Yes: KeylessFulcio + RekorNo: Static keyencrypted cosign.keyyesnoBoth modes: always verify before deploySign and verify artifacts with Sigstore cosign at every stage
Choose keyless OIDC when your CI platform supports it; fall back to encrypted static keys otherwise

GitLab CI example with OIDC

sign-image:
  image: gcr.io/projectsigstore/cosign:v2.4.1
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    - cosign sign --yes "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}"

GitLab documents the id_tokens block in their CI/CD reference. Set aud: sigstore exactly — a wrong audience makes Fulcio reject the token with an opaque error.

Policy enforcement with CUE or Rego

Large teams move verification rules into policy files. Cosign supports cosign verify --policy policy.cue to enforce allowed identities, certificate extensions, and annotation requirements in one place. Start with shell-level verify flags; adopt policy files when you manage more than five images.

Wire verification into the same pipeline stage that runs testing and optimization checks. A failed signature should block deploy exactly like a failed PHPUnit suite.

What are common cosign mistakes and how do you avoid them?

Most cosign failures I see in production are configuration issues, not crypto bugs. The fixes are usually one flag or one missing CI permission.

Gotcha: missing OIDC permissions

GitHub Actions needs id-token: write. GitLab needs an explicit id_tokens entry. Without OIDC, keyless signing fails silently or prompts for a key file that does not exist on the runner.

Gotcha: verifying tags instead of digests

Always verify image@sha256:…. An attacker with registry write access can move a tag to an unsigned image. Digest pinning closes that hole.

Gotcha: clock skew on runners

Fulcio certificates expire quickly. A VM with drifted system time gets "certificate expired" errors. Run NTP on all CI runners — the same baseline I apply during server maintenance work.

Cosign Production GotchasMissing OIDCid-token: writeFloating tagsuse sha256 digestClock skewsync NTP timeFix: fail-closed verify step in deploy jobPair with commit signing and private registry access controlsThree fixes prevent most Sigstore cosign verification failures
OIDC permissions, digest pinning, and accurate system time prevent the majority of cosign failures in CI

Store registry credentials in your secrets manager, not in the Dockerfile. Cosign signatures prove who built the artifact; registry ACLs prove who can push. You need both layers.

For Kubernetes clusters, install the Sigstore Policy Controller or write a validating admission webhook that calls cosign verify on every pod spec image reference. That catches unsigned images even when a developer bypasses the deploy script locally.

On a legal-tech portal deployment, I treat cosign the same way I treat SSL certificates: renewals and rotations go on a calendar, and verification runs automatically. Manual trust decisions do not scale when you maintain multiple sister sites on one shared pipeline.

Related reading: signing container images with cosign for OCI-specific flags, and adding automated checks to CI for pipeline structure patterns.

Key Takeaways

  • Install cosign on CI runners and verify checksums before promoting the binary to production infrastructure.
  • Prefer keyless OIDC signing when your platform supports it; use encrypted static keys for air-gapped or legacy runners.
  • Sign immediately after build in the same job — never sign artifacts that passed through manual upload channels.
  • Verify with pinned sha256 digests and fail closed; floating tags defeat the purpose of signature checks.
  • Attach SBOM attestations alongside signatures so security teams can audit dependencies without rebuilding.
  • Combine cosign with commit signing, registry ACLs, and admission policy for defence in depth across the supply chain.

People Also Ask

Is cosign free to use in production?

Yes. Cosign and the public Sigstore infrastructure (Fulcio, Rekor) are open source and free for standard use. Large enterprises can also run private Sigstore instances if public transparency logs are not acceptable for compliance reasons.

Can cosign sign artifacts other than Docker images?

Yes. The cosign sign-blob and cosign verify-blob commands handle any file — tarballs, binaries, configuration bundles, and firmware images. Signatures are stored as separate .sig files or embedded in an OCI artifact wrapper.

Do I still need GPG if I use cosign?

They solve different problems. GPG or SSH commit signing proves who authored source code. Cosign proves who built and published the release artifact. Use both for a complete chain from commit to deploy.

How do I rotate cosign signing keys?

For static keys, generate a new pair, update CI secrets, re-sign active release artifacts, publish the new public key, and retire the old key after all deployed versions are replaced. Keyless signing rotates automatically because each CI job receives a fresh short-lived certificate.

Build a verifiable supply chain on your next project

You now have the commands to sign and verify artifacts with Sigstore cosign across images, blobs, and attestations. Start with one pipeline job: sign on merge to main, verify before deploy. Expand to SBOM attachments and Kubernetes admission policy once the basics are stable.

If you want help wiring cosign into GitLab CI, Deployer releases, or a private registry on Ubuntu, I can audit your current pipeline and add verification gates that fail closed. See the Notary Kathmandu deployment pipeline for an example of multi-site CI/CD work, or explore custom software development services for full-stack delivery including infrastructure hardening.

For quick JSON policy checks while you draft CUE verification rules, use the free JSON formatter tool. When you are ready to talk through your stack, contact us with your registry URL and CI platform — we can map a cosign rollout in one working session.

Frequently Asked Questions

Cosign is the CLI from the Sigstore project for signing and verifying software artifacts. It supports container images, Helm charts, generic blobs, and signed attestations such as SBOMs and SLSA provenance.

Yes. Cosign and the public Sigstore infrastructure (Fulcio, Rekor) are open source and free for standard use. Large enterprises can also run private Sigstore instances if public transparency logs are not acceptable for compliance reasons.

Yes. The cosign sign-blob and cosign verify-blob commands handle any file — tarballs, binaries, configuration bundles, and firmware images. Signatures are stored as separate .sig files or embedded in an OCI artifact wrapper.

Keyless signing exchanges your CI platform OIDC token with Fulcio for a short-lived certificate; the private key never leaves memory and Rekor records each signature publicly. Static key signing uses a long-lived encrypted key pair stored in GitLab CI variables, GitHub Actions secrets, or Ansible Vault. Keyless gives automatic rotation and strong identity binding to your workflow; static keys suit air-gapped runners or environments without OIDC. On GitLab CI pipelines I maintain on shared EC2 infrastructure, keyless signing through id_tokens is the default choice in 2026.

Download the latest release binary from the Sigstore cosign GitHub releases page, verify the checksum against the release page, chmod it executable, and move it to a path such as /usr/local/bin/cosign. The article pins cosign v2.4.1 as an example. Cosign is a single static executable with no runtime dependencies. I treat it like any other supply-chain tool: download once, checksum, then promote the same binary to all runners rather than installing ad hoc on each server.

Grant id-token: write, contents: read, and packages: write permissions, install cosign via sigstore/cosign-installer@v3, build and push your image, then run cosign sign --yes with the full image reference including the commit SHA tag. The --yes flag skips the interactive prompt in CI. Fulcio binds the certificate to your GitHub workflow identity and Rekor stores the entry automatically. Sign immediately after the build step in the same pipeline job — never sign an artifact that passed through manual upload channels.

Run cosign verify and fail closed on any mismatch. For keyless signatures, pin the digest and pass certificate-identity-regexp and certificate-oidc-issuer flags matching your CI platform — for GitHub Actions, the issuer is https://token.actions.githubusercontent.com. For static keys, run cosign verify --key cosign.pub against the image reference. Wire verification into the same pipeline stage that runs tests; a failed signature should block deploy exactly like a failed PHPUnit suite. On production deploy gates, cosign verify must exit non-zero on failure.

Tags are mutable; digests are not. An attacker with registry write access can move a tag to an unsigned image overnight while your deploy script still trusts the tag name. Always verify image@sha256:… rather than :latest or a floating version tag. Digest pinning closes that hole and is the verification model cosign expects in production. Record the digest in your deployment manifest or GitOps repo and verify at deploy time against the pinned digest plus signature.

Fulcio issues X.509 certificates bound to your CI identity when you sign keylessly through OIDC; certificates are valid for about ten minutes. Rekor stores a tamper-evident transparency log entry for each signature so anyone can audit what was signed and when. Cosign attaches the signature to the artifact itself or stores it as OCI referrers metadata alongside container images. When verification fails, checking whether Fulcio rejected the OIDC token or Rekor lacks the expected entry narrows the problem quickly — most failures are configuration, not crypto bugs.

Use cosign sign-blob with your static key, passing --output-signature and --output-certificate to produce companion .sig and .pem files alongside the artifact. Distribute all three files together. Verification uses cosign verify-blob with the same trio and your public key. This pattern works well for Laravel release archives built on CI and deployed via Deployer symlink swaps. On air-gapped servers, copy the public key out of band once; verification then runs fully offline with no Rekor dependency.

The most common cause is missing OIDC permissions. GitHub Actions needs id-token: write in the job permissions block. GitLab CI needs an explicit id_tokens entry with aud: sigstore exactly — a wrong audience makes Fulcio reject the token with an opaque error. Without OIDC configured, keyless signing fails silently or prompts for a key file that does not exist on the runner. Clock skew is another culprit: Fulcio certificates expire quickly, so runners with drifted system time throw certificate expired errors. Run NTP on all CI runners as baseline maintenance.

They solve different problems in the supply chain. GPG or SSH commit signing proves who authored source code in your repository. Cosign proves who built and published the release artifact — the container image, tarball, or binary your pipeline produces. Use both for a complete chain from commit to deploy. If you already sign Git commits, cosign covers the next layer: the built output. Read a companion piece on signing commits for supply chain trust to connect source integrity with artifact integrity.

For static keys, generate a new pair with cosign generate-key-pair, update CI secrets with the new encrypted private key, re-sign all active release artifacts, publish the new public key to your repository root or .well-known path, and retire the old key after all deployed versions are replaced. Coordinate the public key update in one change. Keyless signing rotates automatically because each CI job receives a fresh short-lived certificate from Fulcio — no manual rotation schedule. Treat static key rotations like SSL renewals: put them on a calendar.

First run cosign attach sbom --sbom sbom.spdx.json against your image reference, then sign with cosign sign --key cosign.key --attachment sbom on the same image. SBOM attestations let security teams trace dependencies without rebuilding the image. Pair this with your existing artifact repository strategy so signed images and SBOMs live in one trusted registry. Build and tag the artifact with an immutable digest or version tag, attach the SBOM, sign immediately after the build step, and push artifact plus signature in the same pipeline job.

Three patterns account for most failures I see. First, verifying floating tags instead of sha256 digests — tags can be retargeted by anyone with registry write access. Second, missing OIDC permissions or wrong GitLab id_tokens audience. Third, clock skew on CI runners causing Fulcio certificate expiry errors. Store registry credentials in your secrets manager, not in the Dockerfile; cosign signatures prove who built the artifact, but registry ACLs prove who can push — you need both layers. For Kubernetes, add admission policy via Sigstore Policy Controller or a validating webhook that calls cosign verify on every pod image reference.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: