Skip to main content
🎀 Speaking at KubeCon EU 2026 Lessons Learned Orchestrating Multi-Tenant GPUs on OpenShift AI View Session
🎀 Speaking at Red Hat Summit 2026 GPUs take flight: Safety-first multi-tenant Platform Engineering with NVIDIA and OpenShift AI Learn More
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.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens TechMeOut