How to Install and Use Cosign on Linux to Sign and Verify Container Images

Modern software supply chain attacks frequently target container registries, injecting malicious payloads into untrusted container images or tampering with upstream build artefacts. To protect cloud-native environments and satisfy software supply chain security standards (such as SLSA and NIST guidelines), DevOps engineers and system administrators rely on Cosign—part of the Linux Foundation’s Sigstore open-source project.

Cosign enables container signing, signature verification, and software bill of materials (SBOM) attestation within Open Container Initiative (OCI) registries. Unlike legacy PGP-based workflows, Cosign stores cryptographic signatures directly inside the registry as standard OCI artefacts adjacent to the image itself. This comprehensive tutorial walks you through installing Cosign on Linux, generating cryptographic key pairs, signing local and remote container images, verifying signatures, and managing keyless signing workflows.


Prerequisites and Operational Requirements

Before installing Cosign, ensure your development or server environment meets the following conditions:

  • Linux Distribution: Ubuntu 22.04/24.04 LTS, Debian 11/12, Fedora 39/40, RHEL 9, or Arch Linux.
  • Administrative Privileges: Root or sudo access to install system binaries.
  • Container Runtime / Registry: Docker, Podman, or an active account on an OCI-compliant registry (such as Docker Hub, GitHub Container Registry ghcr.io, AWS ECR, or Quay.io).
  • Network Access: Outbound HTTPS access to download binaries and push signatures to your registry.

Step 1: Installing Cosign on Linux

Cosign is distributed as a standalone, statically compiled Go binary and as prebuilt packages across various distribution repositories.

Method A: Standalone Precompiled Binary (Recommended)

To obtain the latest stable release directly from the Sigstore GitHub releases page:

# Determine your system architecture
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')

# Fetch the latest stable release tag via GitHub API
COSIGN_VERSION=$(curl -s https://api.github.com/repos/sigstore/cosign/releases/latest | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')

# Download the Cosign binary
curl -LO "https://github.com/sigstore/cosign/releases/download/${COSIGN_VERSION}/cosign-linux-${ARCH}"

# Grant execution permissions and install to system PATH
chmod +x "cosign-linux-${ARCH}"
sudo mv "cosign-linux-${ARCH}" /usr/local/bin/cosign

Method B: Debian and Ubuntu Systems (DEB Package)

# Download and install the official .deb package
curl -LO "https://github.com/sigstore/cosign/releases/latest/download/cosign_${COSIGN_VERSION#v}_amd64.deb"
sudo dpkg -i "cosign_${COSIGN_VERSION#v}_amd64.deb"
sudo apt-get install -f

Method C: Fedora, RHEL, and CentOS Stream (RPM Package)

# Install Cosign via RPM
sudo rpm -ivh "https://github.com/sigstore/cosign/releases/latest/download/cosign-${COSIGN_VERSION#v}.x86_64.rpm"

Confirm successful installation by querying the installed version:

cosign version

Step 2: Generating a Cryptographic Key Pair

Cosign supports both key-based signing (using private/public ECDSA key pairs) and keyless signing (using OpenID Connect tokens and Sigstore’s Fulcio CA). For dedicated CI/CD build environments and private registries, generating a dedicated key pair provides complete offline control.

Execute the key generation command in your terminal:

cosign generate-key-pair

The prompt will ask for a secure private key password:

Enter password for private key: 
Enter again to confirm: 
Private key written to cosign.key
Public key written to cosign.pub

This command creates two files in your current working directory:

  • cosign.key: Your encrypted private key. Store this securely in your CI/CD secret manager (such as GitHub Actions Secrets, HashiCorp Vault, or AWS Secrets Manager). Never commit this file to source control.
  • cosign.pub: Your unencrypted public key. Distribute this public key to all hosts, Kubernetes clusters, or developers who must verify image authenticity.

Step 3: Authenticating with Your Container Registry

Cosign pushes signatures directly into your remote OCI registry as supplementary image manifests. You must authenticate using your registry credentials before signing:

# Logging into GitHub Container Registry (ghcr.io)
echo $GH_CR_TOKEN | cosign login ghcr.io -u <username> --password-stdin

# Or logging into Docker Hub
cosign login index.docker.io -u <username>

Cosign also respects standard Docker credentials stored in ~/.docker/config.json.


Step 4: Signing a Container Image

To sign a container image, supply your private key (cosign.key) and the target image URI with an explicit tag or immutable SHA256 digest:

# Set private key password as an environment variable (ideal for automated scripts)
export COSIGN_PASSWORD="YourStrongKeyPassword"

# Sign the image using the private key
cosign sign --key cosign.key ghcr.io/myorg/myapp:v1.0.0

Cosign computes the container digest, encrypts a cryptographic payload using your private key, and uploads a signature manifest named sha256-<digest>.sig to the registry repository.

Signing by Immutable Digest (Best Practice)

To prevent race conditions where an image tag might be overwritten during the signing process, sign against the immutable image digest:

cosign sign --key cosign.key ghcr.io/myorg/myapp@sha256:4b2277713f5e9f9bb492704f006183ae059f0f937419149683ff2d66636fc471

Step 5: Verifying Image Signatures

Any system or user with access to cosign.pub can verify the integrity and origin of the container image before pulling or deploying it to production:

cosign verify --key cosign.pub ghcr.io/myorg/myapp:v1.0.0

If the signature is authentic and unaltered, Cosign outputs structured JSON payload verification details:

Verification for ghcr.io/myorg/myapp:v1.0.0 --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key

[{"critical":{"identity":{"docker-reference":"ghcr.io/myorg/myapp"},"image":{"docker-manifest-digest":"sha256:4b22777..."},"type":"cosign container image signature"},"optional":null}]

If the image has been modified, tampered with, or signed by an untrusted key, Cosign rejects the verification with an exit code of 1:

Error: no matching signatures:
failed to verify signature: crypto/rsa: verification error
main.go:62: error during command execution: no matching signatures

Step 6: Attesting and Verifying Software Bill of Materials (SBOM)

In addition to signing raw images, Cosign can attach and cryptographically attest metadata documents, including CycloneDX or SPDX Software Bill of Materials (SBOM) files:

  1. Generate an SBOM file using a scanner like Syft:
    syft ghcr.io/myorg/myapp:v1.0.0 -o spdx-json > sbom.spdx.json
  2. Attach and attest the SBOM to the container image using Cosign:
    cosign attest --key cosign.key --predicate sbom.spdx.json --type spdxjson ghcr.io/myorg/myapp:v1.0.0
  3. Verify the attestation predicate using your public key:
    cosign verify-attestation --key cosign.pub --type spdxjson ghcr.io/myorg/myapp:v1.0.0

Keyless Signing with OpenID Connect (OIDC)

In cloud-native CI/CD environments (such as GitHub Actions or GitLab CI), managing and rotating long-lived private key files presents a security risk. Cosign supports keyless signing, which exchanges short-lived OIDC workflow identity tokens for ephemeral certificates issued by the Sigstore Fulcio Certificate Authority.

In a GitHub Actions workflow, invoke keyless signing simply by omitting the --key flag:

# Keyless signing in GitHub Actions with id-token: write permissions
cosign sign ghcr.io/myorg/myapp:v1.0.0

To verify a keyless signature, validate the identity claim against the expected issuer and repository:

cosign verify \
  --certificate-identity https://github.com/myorg/myapp/.github/workflows/build.yml@refs/heads/main \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/myorg/myapp:v1.0.0

Cosign Command Reference

Command Operational Role
cosign generate-key-pair Generates ECDSA private (cosign.key) and public (cosign.pub) keys.
cosign sign --key <key> <image> Signs a container image and pushes the signature manifest to the registry.
cosign verify --key <pub> <image> Validates image integrity and signature authenticity against a public key.
cosign attest --key <key> --predicate <file> Attests an SBOM, provenance, or security scan report to an OCI image.
cosign triangulate <image> Outputs the exact registry URI of the image’s uploaded signature artefact.
cosign copy <src> <dst> Copies container images alongside all associated signatures and attestations.

Troubleshooting Common Cosign Errors

1. “Error: pushing signature: ACCESS_DENIED: unauthorized”

Cosign requires write permissions to the registry repository to push the .sig tag. Ensure your registry token possesses write access (e.g., write:packages on GitHub Container Registry).

2. “Error: no matching signatures”

This occurs when the target image has been re-tagged, re-built, or modified post-signing. Always pull and verify images using the exact immutable SHA256 digest (image@sha256:...) rather than a mutable floating tag.

3. “terminal is not a tty and no COSIGN_PASSWORD set”

When executing Cosign within non-interactive CI/CD pipelines, Cosign cannot display an interactive password prompt. Export the password environment variable prior to execution: export COSIGN_PASSWORD="YourPassword".

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.