Container Image Signing with Sigstore Cosign: Zero-Trust Supply Chain Security

Container Image Signing with Sigstore Cosign

Container image signing Cosign has become the industry standard for verifying that container images running in production are exactly what your CI/CD pipeline built. Sigstore’s Cosign tool provides cryptographic signatures that prove image provenance, detect tampering, and enforce trust policies at deployment time. With keyless signing through OIDC identity, there are no private keys to manage or rotate.

This guide covers the complete signing workflow: from signing images in CI pipelines to verifying signatures with Kubernetes admission controllers. You will learn how to implement a zero-trust container supply chain that prevents unauthorized or modified images from ever reaching your clusters.

Understanding Sigstore and Cosign

Sigstore is an open-source project that provides free code signing infrastructure. Cosign, its container signing tool, uses three components: a transparency log (Rekor) that records all signing events, a certificate authority (Fulcio) that issues short-lived certificates, and OIDC identity providers for keyless signing.

Container image signing security architecture
Sigstore ecosystem: Cosign, Fulcio CA, and Rekor transparency log working together
Keyless Signing Flow:

1. Developer/CI triggers cosign sign
2. Cosign requests OIDC token (GitHub, Google, etc.)
3. Fulcio CA verifies OIDC token, issues short-lived cert
4. Cosign signs the image digest with the cert
5. Signature + cert stored in OCI registry alongside image
6. Signing event recorded in Rekor transparency log

Verification Flow:
1. Kubernetes admission controller intercepts pod creation
2. Policy engine fetches signature from OCI registry
3. Verifies signature against Fulcio root CA
4. Checks Rekor log for signing event
5. Validates identity (who signed) and claims
6. Admits or rejects the pod

Signing Images in CI/CD Pipelines

The most common approach is keyless signing in GitHub Actions, where the OIDC token from the workflow identity automatically authenticates with Fulcio. No secrets or keys are needed.

# .github/workflows/build-sign.yml
name: Build, Push, and Sign Container Image
on:
  push:
    branches: [main]

permissions:
  contents: read
  packages: write
  id-token: write  # Required for keyless signing

jobs:
  build-sign:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and Push
        id: build
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/myorg/myapp:latest,ghcr.io/myorg/myapp:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Install Cosign
        uses: sigstore/cosign-installer@v3

      - name: Sign Image (Keyless)
        env:
          DIGEST: ${{ steps.build.outputs.digest }}
        run: |
          cosign sign --yes \
            ghcr.io/myorg/myapp@${DIGEST}

      - name: Attach SBOM
        env:
          DIGEST: ${{ steps.build.outputs.digest }}
        run: |
          # Generate SBOM with syft
          syft ghcr.io/myorg/myapp@${DIGEST} -o spdx-json > sbom.spdx.json
          # Attach SBOM as signed attestation
          cosign attest --yes \
            --predicate sbom.spdx.json \
            --type spdxjson \
            ghcr.io/myorg/myapp@${DIGEST}

      - name: Verify Signature
        run: |
          cosign verify \
            --certificate-identity-regexp "https://github.com/myorg/.*" \
            --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
            ghcr.io/myorg/myapp@${DIGEST} | jq .

Kubernetes Admission Control with Kyverno

Moreover, signing images is only half the story. You need admission controllers that reject unsigned or incorrectly signed images. Kyverno and Sigstore Policy Controller both integrate with Cosign verification.

# Kyverno policy to enforce Cosign signatures
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-cosign-signature
spec:
  validationFailureAction: Enforce
  background: false
  webhookTimeoutSeconds: 30
  rules:
    - name: verify-image-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
              namespaces: ["production", "staging"]
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/myorg/*"
                    issuer: "https://token.actions.githubusercontent.com"
                    rekor:
                      url: https://rekor.sigstore.dev
          attestations:
            - type: https://spdx.dev/Document
              conditions:
                - all:
                    - key: "{{ creationInfo.created }}"
                      operator: NotEquals
                      value: ""
    - name: block-unsigned-images
      match:
        any:
          - resources:
              kinds: ["Pod"]
              namespaces: ["production"]
      validate:
        message: "All images must be signed with Cosign"
        deny:
          conditions:
            any:
              - key: "{{ request.object.spec.containers[].image }}"
                operator: AnyNotIn
                value: "ghcr.io/myorg/*"
Kubernetes security and admission control
Enforcing container image verification with Kubernetes admission policies

Key-Based Signing for Air-Gapped Environments

# Generate a key pair for environments without internet
cosign generate-key-pair

# Sign with private key
cosign sign --key cosign.key \
  --annotations "build-id=abc123" \
  --annotations "git-sha=def456" \
  registry.internal.com/myapp:v1.0.0

# Verify with public key
cosign verify --key cosign.pub \
  registry.internal.com/myapp:v1.0.0

# Distribute public key as Kubernetes secret
kubectl create secret generic cosign-pub \
  --from-file=cosign.pub=cosign.pub \
  -n kyverno

Additionally, for air-gapped environments, you can run a private Rekor instance and Fulcio CA internally. This gives you the same transparency guarantees without external network dependencies.

When NOT to Use Container Image Signing

Image signing adds complexity to your CI/CD pipeline and deployment workflow. For development environments, personal projects, or internal tooling with low security requirements, the overhead may not be justified. Furthermore, if your images are built and consumed within a single trusted environment (same CI system and cluster), network-level controls may provide sufficient security.

Consequently, implement signing for production workloads, regulated environments, and any pipeline where images cross trust boundaries. As a result, start with keyless signing in your main CI pipeline and expand to admission control once you have validated the signing workflow is reliable.

DevSecOps supply chain security workflow
Building a zero-trust container supply chain from CI to production

Key Takeaways

Container image signing Cosign with Sigstore provides a practical, free, and industry-standard approach to container supply chain security. Keyless signing eliminates key management overhead, transparency logs provide auditability, and Kubernetes admission controllers enforce trust policies. Start with keyless signing in GitHub Actions, add Kyverno policies for production namespaces, and attach SBOMs for complete supply chain visibility.

Key Takeaways

  • Start with a solid foundation and build incrementally based on your requirements
  • Test thoroughly in staging before deploying to production environments
  • Monitor performance metrics and iterate based on real-world data
  • Follow security best practices and keep dependencies up to date
  • Document architectural decisions for future team members

For more security topics, explore our guide on Kubernetes security best practices and DevSecOps pipeline security. The Sigstore documentation and Cosign GitHub repository are the authoritative references.

In conclusion, Container Image Signing Sigstore is an essential topic for modern software development. By applying the patterns and practices covered in this guide, you can build more robust, scalable, and maintainable systems. Start with the fundamentals, iterate on your implementation, and continuously measure results to ensure you are getting the most value from these approaches.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top