Introduction to Sigstore
Sigstore is an open-source project that provides a unified, standards-based approach to software supply chain security. It enables developers to digitally sign software artifacts such as container images, binaries, and source code, and to verify those signatures later. Sigstore is backed by the Linux Foundation and is widely adopted across the cloud-native ecosystem, including projects like Kubernetes, Docker, and npm.
At its core, Sigstore combines three key technologies: cosign for signing and verification, Rekor for an immutable transparency log, and Fulcio for short-lived, ephemeral code-signing certificates tied to OIDC identities. Together, these components eliminate the need for developers to manage long-lived signing keys, dramatically reducing the risk of key compromise.
Why Sigstore Matters
Software supply chain attacks have grown significantly in recent years. Attackers increasingly target build systems, package registries, and dependency networks rather than end-user machines. Without cryptographic signatures, consumers of software have no reliable way to verify that an artifact was produced by a trusted source and has not been tampered with.
Sigstore addresses this by providing:
- Keyless signing using OIDC identity providers like GitHub, Google, and GitLab.
- Transparency logs that make all signatures publicly auditable.
- Short-lived certificates that expire within minutes, reducing the blast radius of credential theft.
- Standardized formats based on OCI signatures and in-toto attestations.
Architecture Overview
Before diving into configuration, it is important to understand the three main components of Sigstore and how they interact.
Fulcio
Fulcio is the certificate authority (CA) for Sigstore. When a developer initiates a keyless signing operation, Fulcio issues a short-lived certificate that binds the developer's OIDC identity (such as their GitHub username and repository) to a public key. The certificate typically expires within ten minutes.
Rekor
Rekor is the transparency log. Every signature, along with its metadata, is recorded in Rekor's append-only, publicly auditable log. This allows anyone to verify when and by whom an artifact was signed, and to detect any attempt to backdate or forge signatures.
Cosign
Cosign is the command-line tool that developers use to sign and verify artifacts. It supports both keyless signing (via OIDC) and key-based signing (using a private key stored in a KMS system or on disk). Cosign integrates with container registries, CI/CD systems, and policy engines like Kyverno and OPA Gatekeeper.
Installing Cosign
Cosign is the primary tool you will interact with. Installation is straightforward on most platforms.
Installing on macOS
brew install cosign
Installing on Linux
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
Verifying the Installation
cosign version
You should see version information printed to the terminal. If not, ensure that the binary is on your PATH.
Keyless Signing with GitHub Actions
The most common and recommended way to use Sigstore is through keyless signing in a CI/CD pipeline. This section walks through configuring GitHub Actions to sign a container image using cosign.
Prerequisites
- A GitHub repository with Actions enabled.
- A container registry such as GitHub Container Registry (GHCR) or Docker Hub.
- The
id-token: writepermission, which is required for OIDC authentication.
Workflow Configuration
The following GitHub Actions workflow builds a container image, pushes it to GHCR, and signs it using cosign's keyless mode.
name: Build and Sign
on:
push:
branches:
- main
permissions:
contents: read
packages: write
id-token: write
jobs:
build-and-sign:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Sign image with keyless signing
run: |
cosign sign --yes ghcr.io/${{ github.repository }}:latest
The --yes flag automatically confirms the browser-free OIDC flow that cosign uses in CI environments. The id-token: write permission is critical; without it, cosign cannot obtain an OIDC token from GitHub and the signing step will fail.
Generating an Attestation
In addition to signing the image, you can attach an attestation that describes how the image was built. This is useful for provenance verification.
- name: Generate build provenance attestation
run: |
cosign attest --yes \
--predicate <<'EOF'
{
"builder": {"id": "github-actions"},
"buildType": "https://github.com/actions/runner",
"subject": [{"name": "ghcr.io/${{ github.repository }}:latest", "digest": {"sha256": "PLACEHOLDER"}}]
}
EOF
ghcr.io/${{ github.repository }}:latest
In practice, you would generate the predicate dynamically using a tool like slsa-github-generator, which produces a properly formatted in-toto statement with the correct digest values.
Key-Based Signing
While keyless signing is recommended for most use cases, some environments require key-based signing. This is common in air-gapped environments or when integrating with existing PKI infrastructure.
Generating a Key Pair
cosign generate-key-pair
This command generates two files: cosign.pub (the public key) and cosign.key (the encrypted private key). You will be prompted to set a password for the private key. Store the private key securely and never commit it to version control.
Signing an Image with a Key
export COSIGN_PASSWORD="your-strong-password"
cosign sign --key cosign.key ghcr.io/myorg/myimage:latest
Verifying a Signature with a Key
cosign verify --key cosign.pub ghcr.io/myorg/myimage:latest
If the signature is valid, cosign will print the signature details and exit with a zero status code. If verification fails, it will exit with a non-zero code, making it suitable for use in CI/CD gates.
Using a KMS Backend
For production deployments, storing the private key on disk is not ideal. Cosign supports several KMS backends, including AWS KMS, Google Cloud KMS, Azure Key Vault, and HashiCorp Vault.
Google Cloud KMS Example
First, create a key ring and a signing key in Google Cloud KMS:
gcloud kms keyrings create sigstore-ring --location global
gcloud kms keys create cosign-key \
--keyring sigstore-ring \
--location global \
--purpose asymmetric-signing \
--default-algorithm rsa-sign-pkcs1-2048-sha256
Then sign an image using the KMS key:
cosign sign \
--key gcpkms://projects/my-project/locations/global/keyRings/sigstore-ring/cryptoKeys/cosign-key \
ghcr.io/myorg/myimage:latest
To verify, you need to export the public key from KMS:
cosign public-key \
--key gcpkms://projects/my-project/locations/global/keyRings/sigstore-ring/cryptoKeys/cosign-key \
> kms-pubkey.pem
cosign verify --key kms-pubkey.pem ghcr.io/myorg/myimage:latest
Verifying Signatures
Verification is the other half of the equation. Consumers of your software need to verify signatures before trusting and deploying artifacts.
Keyless Verification with Identity
When using keyless signing, you verify by specifying the expected OIDC identity rather than a public key. This is the most powerful feature of Sigstore because it ties signatures to real-world identities.
cosign verify ghcr.io/myorg/myimage:latest \
--certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
The --certificate-identity flag specifies the exact workflow and branch that is allowed to sign the image. The --certificate-oidc-issuer flag specifies the OIDC provider. This combination ensures that only signatures produced by the expected workflow are accepted.
Verifying Attestations
cosign verify-attestation ghcr.io/myorg/myimage:latest \
--certificate-identity "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"
This command retrieves and verifies the attestation attached to the image. You can further filter attestations by type using the --type flag, for example --type slsaprovenance.
Running a Self-Hosted Sigstore Deployment
For organizations that cannot use the public Sigstore infrastructure, a self-hosted deployment is available. This is particularly relevant for air-gapped environments or organizations with strict data residency requirements.
Deploying with Helm
The Sigstore project provides a Helm chart for deploying Fulcio, Rekor, and the CT log on a Kubernetes cluster.
helm repo add sigstore https://sigstore.github.io/helm-charts
helm repo update
helm install sigstore sigstore/sigstore \
--namespace sigstore \
--create-namespace
Configuring the CT Log
The Certificate Transparency (CT) log is a critical component. You need to configure persistent storage for it to ensure durability across restarts.
cat > ct-log-values.yaml << 'EOF'
persistence:
enabled: true
storageClass: "standard"
size: 100Gi
config:
privateKeyPassword: "change-me-to-a-real-secret"
EOF
helm upgrade --install ct-log sigstore/ctlog \
--namespace sigstore \
-f ct-log-values.yaml
Pointing Cosign at Your Deployment
export COSIGN_EXPERIMENTAL=1
export FULCIO_URL="https://fulcio.sigstore.svc.cluster.local"
export REKOR_URL="https://rekor.sigstore.svc.cluster.local"
export CTLOG_URL="https://ctlog.sigstore.svc.cluster.local"
cosign sign --yes ghcr.io/myorg/myimage:latest
By setting these environment variables, cosign will use your self-hosted infrastructure instead of the public Sigstore service.
Policy Enforcement with Kyverno
Signing images is only valuable if you enforce verification at deployment time. Kyverno is a Kubernetes policy engine that can verify Sigstore signatures as part of admission control.
Kyverno ClusterPolicy for Image Verification
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
rules:
- name: verify-sigstore-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*/.github/workflows/*"
issuer: "https://token.actions.githubusercontent.com"
This policy requires that all images from ghcr.io/myorg/* must be signed by a GitHub Actions workflow in the myorg organization. Any pod that references an unsigned or improperly signed image will be rejected at admission time.
Best Practices
Prefer Keyless Signing
Keyless signing eliminates the operational burden of managing private keys and reduces the risk of key compromise. Use keyless signing whenever your CI/CD environment supports OIDC.
Pin Certificate Identities Tightly
When verifying signatures, always specify both the --certificate-identity and --certificate-oidc-issuer. Avoid wildcards unless absolutely necessary. The more specific your identity requirements, the harder it is for an attacker to forge a valid signature.
Use Transparency Logs for Monitoring
Rekor's transparency log is publicly queryable. Set up monitoring to alert on unexpected entries. For example, you can periodically query Rekor for signatures associated with your organization's identities and alert on any that originate from unexpected workflows or branches.
Store Private Keys in KMS
If you must use key-based signing, never store private keys on disk in CI environments. Use a KMS backend and grant least-privilege access to the CI service account.
Sign at Build Time, Not After
Always sign artifacts as part of the build pipeline, immediately after the artifact is produced. Signing after the fact introduces a window where an artifact could be tampered with before it is signed.
Verify Before Deployment
Implement verification at every stage where artifacts are consumed: in CI pipelines, in admission controllers, and in runtime environments. A signature that is never verified provides no security value.
Conclusion
Sigstore represents a fundamental shift in how the software industry approaches supply chain security. By combining keyless signing, transparency logs, and short-lived certificates, it removes the traditional barriers to adopting code signing at scale. Whether you are signing container images in a GitHub Actions pipeline, verifying signatures with Kyverno at admission time, or running a self-hosted deployment in an air-gapped environment, Sigstore provides the tools and standards needed to build verifiable trust into your software delivery process. Start by integrating cosign into your CI pipeline today, and progressively add verification gates and policy enforcement to create a defense-in-depth strategy against supply chain attacks.