Skip to main content
πŸŽ“ Claude Code Masterclass Learn AI-assisted development on Udemy β€” plus the companion book on Leanpub & Amazon. Start Learning
Container image signing
DevOps

Container Image Signing and Verification Pipelines

Build a complete container signing pipeline with Cosign, Kyverno, and GitLab CI. Ensure only verified images run in your Kubernetes clusters.

LB
Luca Berton
Β· 1 min read

Why Sign Container Images

You docker pull nginx:latest. But is that image really from the Nginx team? Or did someone compromise the registry and push a modified image with a cryptocurrency miner?

Image signing proves provenance β€” that the image was built by your CI pipeline, from your source code, and hasn’t been tampered with.

The Pipeline

Source Code β†’ Build β†’ Sign β†’ Push β†’ Verify β†’ Deploy
                ↓
            SBOM + Provenance
            (attached as attestations)

Step 1: Build and Sign in CI

# .gitlab-ci.yml
stages:
  - build
  - sign
  - deploy

build:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build -t $REGISTRY/$IMAGE:$CI_COMMIT_SHA .
    - docker push $REGISTRY/$IMAGE:$CI_COMMIT_SHA
    # Get the digest for signing
    - |
      DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' $REGISTRY/$IMAGE:$CI_COMMIT_SHA)
      echo "DIGEST=$DIGEST" >> build.env
  artifacts:
    reports:
      dotenv: build.env

sign:
  stage: sign
  image: gcr.io/projectsigstore/cosign:v2
  needs: [build]
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
  script:
    # Keyless signing β€” identity from GitLab OIDC
    - cosign sign --yes $DIGEST

    # Generate and attach SBOM
    - syft $DIGEST -o cyclonedx-json > sbom.cdx.json
    - cosign attest --yes
        --predicate sbom.cdx.json
        --type cyclonedx
        $DIGEST

    # Attach vulnerability scan results
    - trivy image $DIGEST --format cosign-vuln > vuln.json
    - cosign attest --yes
        --predicate vuln.json
        --type vuln
        $DIGEST

Step 2: Verify Before Deployment

With Cosign CLI

cosign verify $DIGEST \
  --certificate-identity-regexp="https://gitlab.com/myorg/.*" \
  --certificate-oidc-issuer="https://gitlab.com"

# Verify SBOM attestation
cosign verify-attestation $DIGEST \
  --type cyclonedx \
  --certificate-identity-regexp="https://gitlab.com/myorg/.*" \
  --certificate-oidc-issuer="https://gitlab.com"

With Kyverno (Kubernetes Admission Controller)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  webhookTimeoutSeconds: 30
  rules:
    - name: verify-signature
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences:
            - "registry.company.com/*"
          attestors:
            - entries:
                - keyless:
                    subject: "https://gitlab.com/myorg/*"
                    issuer: "https://gitlab.com"
                    rekor:
                      url: https://rekor.sigstore.dev

    - name: require-sbom
      match:
        any:
          - resources:
              kinds: ["Pod"]
      verifyImages:
        - imageReferences:
            - "registry.company.com/*"
          attestations:
            - type: https://cyclonedx.org/bom
              conditions:
                - all:
                    - key: "{{ components[].name }}"
                      operator: AllNotIn
                      value: ["banned-package"]

Now no unsigned image can run in your cluster. Period.

Step 3: Monitoring and Audit

# Prometheus alert for policy violations
- alert: ImageVerificationFailed
  expr: increase(kyverno_policy_results_total{rule_result="fail", policy="verify-image-signatures"}[5m]) > 0
  labels:
    severity: critical
  annotations:
    summary: "Unsigned image deployment attempted"

Multi-Cluster Rollout

For organizations running multiple clusters, I automate the policy deployment with Ansible:

- name: Deploy image signing policies
  hosts: k8s_clusters
  tasks:
    - name: Install Kyverno
      helm:
        name: kyverno
        chart_ref: kyverno/kyverno
        release_namespace: kyverno
        create_namespace: true

    - name: Apply signing policies
      kubernetes.core.k8s:
        state: present
        src: "{{ item }}"
      loop:
        - policies/verify-signatures.yml
        - policies/require-sbom.yml
        - policies/require-provenance.yml

Ansible automation patterns at Ansible Pilot. Kubernetes policy patterns at Kubernetes Recipes.

The Trust Chain

GitLab OIDC β†’ Fulcio (certificate) β†’ Cosign (signature) β†’ Rekor (transparency log) β†’ Kyverno (enforcement)

Every link is verifiable, auditable, and tamper-proof. This is the supply chain security the EU Cyber Resilience Act expects. Build it now, before compliance deadlines hit.

#container-security #cosign #kyverno #image-signing #cicd #kubernetes
Share:
Kubernetes & Containerization

Need help with Kubernetes & Containerization?

Master Kubernetes and container orchestration with hands-on workshops and architecture consulting.

Learn more about Kubernetes & Containerization

Want to operate this yourself, in production?

Take the free AI Platform Engineer Readiness Scorecard to see which skills transfer β€” then build a production-shaped AI platform in the 4-week Bootcamp.

Take the Scorecard β†’
Luca Berton β€” AI & Cloud Advisor, Docker Captain

Luca Berton

AI & Cloud Advisor Β· Docker Captain Β· KubeCon Speaker

15+ years in enterprise infrastructure. Author of 8 technical books, creator of Ansible Pilot (1M+ YouTube views, 648K site users). Former Red Hat engineer. Speaker at KubeCon EU 2026 and Red Hat Summit 2026.

Free 30-min AI & Cloud consultation

Book Now