The Registry Compromise Problem
You pull myorg/myapp:v1.0.0 from the registry. But how do you know it's actually the image your team built? The registry could be compromised, someone's credentials could be stolen, or the image could have been replaced in transit. Container image signing solves this: your build system cryptographically signs the image with a private key, and deployment systems verify the signature before running anything. If the image is modified, the signature fails verification. If it's unsigned, it never runs. This is how teams ensure that only authorized builds make it to production.
Container image signing uses cryptography to prove image authenticity and integrity—that the image was created by someone you trust, hasn't been modified, and came from your build system.
graph LR A["Build System<br/>Creates Image"] --> B["Image Hash<br/>abc123..."] B --> C["Sign with<br/>Private Key"] C --> D["Signature<br/>signature_xyz789"] D --> E["Push Image<br/>+ Signature<br/>to Registry"] E --> F["Deploy System<br/>Verifies with<br/>Public Key"] F --> G{"Valid?"} G -->|Yes| H["Deploy Image<br/>✅ Trusted"] G -->|No| I["Reject Image<br/>❌ Untrusted"] style H fill:#ccffcc style I fill:#ffccccWhat is Container Image Signing?
Container image signing is using cryptography to prove image authenticity and integrity:
Build system creates image: myapp:v1.0.0 → sha256:abc123... Build system signs the image: Signature: sign(private_key, image_hash) → signature_xyz789... Image + Signature stored in registry: Image: myapp:v1.0.0 Signature: signatures/v1.0.0 User verifies before deployment: verify(public_key, image_hash, signature) → ✅ VALID (image is authentic) OR → ❌ INVALID (image modified or forged)Cosign: The Signing Tool
Cosign is an open-source tool (part of the Sigstore ecosystem) that signs and verifies container images.
Cosign Workflow
# Step 1: Build imagedocker build -t ghcr.io/myorg/myapp:v1.0.0 . # Step 2: Push image to registrydocker push ghcr.io/myorg/myapp:v1.0.0 # Step 3: Sign the imagecosign sign ghcr.io/myorg/myapp:v1.0.0# Prompts for signing key passphrase# Creates signature in registry # Step 4: User verifies the signaturecosign verify ghcr.io/myorg/myapp:v1.0.0 \ --key cosign.pub# Output: Verified ✅Key Concepts
1. Private Key (Secret)
Used only by build system to sign images:
# Generate signing key paircosign generate-key-pair # Output:# - cosign.key (private, KEEP SECRET)# - cosign.pub (public, share widely) # Store private key securely# Option 1: Encrypted in CI/CD environment# Option 2: In HSM (hardware security module)# Option 3: In cloud key management (KMS)2. Public Key (Share)
Used by anyone to verify signatures:
# Publish public key to distribution system# Users retrieve it to verify signatures # Embed in policy:apiVersion: constraints.gatekeeper.sh/v1beta1kind: K8sSignedImagemetadata: name: require-signed-imagesspec: parameters: publicKeys: - | -----BEGIN PUBLIC KEY----- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE... -----END PUBLIC KEY-----3. Signature (Proof)
Cryptographic proof stored in registry:
Docker image: ghcr.io/myorg/myapp:v1.0.0Signature: stored separately, indexed by image digestKeyless Signing: Sigstore
Sigstore is an open-source project (which includes Cosign) that enables keyless signing—signing without managing private keys.
How Keyless Signing Works
Instead of managing a private key, you authenticate with OIDC to prove your identity to an identity provider (GitHub, Google, etc.), get a temporary certificate as the identity provider issues short-lived certificate, and sign with certificate by using the certificate to sign image without managing keys, then record in transparency log as the signature is stored in public Rekor transparency log.
User pushes code to GitHub: → GitHub Actions workflow runs → Workflow requests identity from GitHub (OIDC) → GitHub issues temporary certificate (30 minutes) → Cosign signs image with certificate → Signature stored in Rekor transparency log → Private key never needed, never stored, never compromised User verifies signature: → Retrieve signature from Rekor → Verify signature came from GitHub Actions → No need to manage public keys manuallySigstore Components
Component | Purpose |
|---|---|
Cosign | CLI tool for signing and verifying images |
Fulcio | Certificate Authority issuing short-lived certs |
Rekor | Transparency log recording all signatures |
ctlog | Certificate Transparency log component |
Benefits of Sigstore/Keyless
- No Key Management: No need to protect private keys
- Audit Trail: All signatures recorded in Rekor transparency log
- Transparency: Anyone can audit who signed what and when
- Scalability: Issue certificates on-demand without infrastructure
- Compliance: Transparency log provides compliance audit trail
Implementing Image Signing
GitHub Actions with Cosign
name: Build, Sign, and Push Image on: [push] jobs: build-and-sign: runs-on: ubuntu-latest permissions: contents: read id-token: write # Required for OIDC steps: - uses: actions/checkout@v3 - name: Build container image run: docker build -t ghcr.io/myorg/myapp:${{ github.sha }} . - name: Login to GitHub Container Registry uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Push image run: docker push ghcr.io/myorg/myapp:${{ github.sha }} - name: Install Cosign uses: sigstore/cosign-installer@v3 with: cosign-release: 'v2.0.0' - name: Sign image (keyless with Sigstore) run: | cosign sign \ --yes \ ghcr.io/myorg/myapp:${{ github.sha }} env: COSIGN_EXPERIMENTAL: 1 # Enable keyless signingVerifying Signed Images
# Verify signature (keyless)cosign verify \ --certificate-identity-regexp="https://github.com/" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ ghcr.io/myorg/myapp:v1.0.0 # Output: Verified ✅# Shows signature details and identityPolicy Enforcement: Requiring Signatures
Use Gatekeeper (OPA) to require signatures before deployment:
apiVersion: constraints.gatekeeper.sh/v1beta1kind: K8sSignedImagemetadata: name: require-signed-imagesspec: match: kinds: - apiGroups: [""] kinds: ["Pod"] parameters: mode: enforce # Block unsigned images publicKeys: - name: github-actions key: | -----BEGIN PUBLIC KEY----- [GitHub Actions public key] -----END PUBLIC KEY----- trustedIssuers: - "https://token.actions.githubusercontent.com"Result: Kubernetes prevents deployment of unsigned images.
# Attempt to deploy unsigned image:kubectl apply -f unsigned-deployment.yaml# Error: Image not signed# Deployment rejected # Deploy signed image:kubectl apply -f signed-deployment.yaml# ✅ Image signature verified# Deployment acceptedImage Attestations
Beyond signatures, you can create attestations—statements about the image:
# Create attestation (signed statement about image)cosign attest \ --predicate predicate.json \ --attestation-type vuln \ ghcr.io/myorg/myapp:v1.0.0 # Attestation contains:# - Image reference# - Signed statement (predicate)# - Type of statement (vulnerability report, SBOM, etc.)# - Signature proofAttestation types:
- SBOM Attestation: Signed SBOM showing all components
- Vulnerability Attestation: Signed vulnerability scan results
- Provenance Attestation: Signed build provenance (SLSA)
- Custom Attestation: Any custom statement about the image
Verifying Attestations
# List all attestations for an imagecosign verify-attestation \ ghcr.io/myorg/myapp:v1.0.0 # Verify specific attestationcosign verify-attestation \ --predicate-type vuln \ --certificate-identity-regexp="https://github.com/" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ ghcr.io/myorg/myapp:v1.0.0Transparency Logs (Rekor)
Rekor is a public transparency log that records all signatures:
# Search for signatures from specific identityrekor-cli search --email security@myorg.com # Search for signatures of specific imagerekor-cli search --uuid ghcr.io/myorg/myapp:v1.0.0 # Output: All signatures recorded in transparency log# Shows who signed what and whenBenefits:
- Audit Trail: Anyone can see all signatures
- Accountability: Can't deny signing an image (it's in public log)
- Timestamp Verification: Proves when image was signed
- Compliance: Satisfies audit and compliance requirements
Supply Chain Integrity: Complete Picture
Cosign and Sigstore integrate with SLSA and provenance through a comprehensive workflow. A build system such as GitHub Actions begins the process by building a container image. Next, it generates a software bill of materials (SBOM) to document all components, followed by generating provenance documentation that provides proof of the build. Cosign then signs all artifacts: the image itself, the SBOM, and the provenance. These signed artifacts are stored in the registry along with attestations. When a user makes a deployment decision, they verify all signatures: the image signature, the SBOM authenticity, the provenance authenticity, and the Rekor transparency log entries. Only after all signatures are validated does deployment proceed.
CleanStart and Image Signing
CleanStart Source Intelligence Core implements comprehensive image signing across the entire supply chain. The system signs all generated images using the Cosign/Sigstore tooling, ensuring every artifact carries cryptographic proof of its origin. CleanStart creates signed SBOMs for every artifact, documenting all components with cryptographic attestations. The system captures signed provenance for complete traceability of how each image was built and by whom. Signature policies are enforced through Kubernetes admission control, preventing unsigned images from being deployed to clusters. The system verifies all signatures before processing artifacts, rejecting anything that fails validation. All signing activities are recorded in transparency logs to ensure audit compliance and historical accountability.
Image Signing Best Practices
Always sign all container images before they are deployed to production or any other environment, establishing a baseline expectation that unsigned images are never acceptable. Use keyless signing through Sigstore, leveraging OIDC to manage identity instead of manually rotating cryptographic key material. Record attestations including SBOMs and provenance documentation alongside your signed images, providing comprehensive metadata about image composition and build process. Enforce signature verification at deployment time using admission control mechanisms that prevent the cluster from running unsigned images. Periodically audit transparency logs like Rekor to understand your supply chain signing activity and detect any anomalies. Distribute public keys widely through your infrastructure so that verification can be performed at scale without bottlenecks. If you must use traditional key-based signing rather than keyless signing, establish a regular key rotation schedule—typically every 90 days or when personnel changes occur. Document your signing and verification policies clearly so that all team members understand the expectations and implementation requirements.
Related Concepts
SLSA: Includes signatures and provenance as requirements. Build Provenance: Signed attestation of how image was built. SBOM: Signed inventory of components. Container Security: Signing is part of overall container integrity. Supply Chain Security: Signing enables end-to-end supply chain verification.
Further Reading
Sigstore Official Documentation - Complete Sigstore ecosystem guide. Cosign GitHub Repository - Cosign source and documentation. Rekor Transparency Log - Audit trail for signatures. Container Image Signing Best Practices - Practical guide.
