CME-712

Container Image Signing and Provenance Verification (Cosign/Sigstore)

Description

Require cryptographic signatures on container images before deployment. Cosign/Sigstore verify image provenance against a trust policy, ensuring only images from authorized build pipelines are admitted. Blocks supply chain attacks where a compromised registry or build system pushes malicious images. Verification can be enforced at admission time via policy controllers (Kyverno, Gatekeeper with cosign) or at pull time via container runtime trust policies (Podman policy.json, CRI-O).

CVSS Vector Impacts

Metric Transition Rationale
Attack Complexity (AC) L → H Attacker must forge a valid cryptographic signature or compromise the signing key to inject a malicious container image; simply pushing to a registry is insufficient
Integrity (I) H → L Unsigned or tampered images are rejected at admission; modifications to images after signing are detected and blocked

CWE Relationships

Verification

Verify image signature verification is enforced at admission or runtime level

$ kubectl get clusterimagepolicy --no-headers 2>/dev/null | wc -l || kubectl get policy.sigstore.dev --no-headers 2>/dev/null | wc -l
# Expected: >=1
Platform: kubernetes
$ kubectl get clusterpolicy -o jsonpath='{range .items[*]}{.spec.rules[*].verifyImages}{end}' 2>/dev/null | grep -c 'image'
# Expected: >=1
Platform: kubernetes
$ podman image trust show 2>/dev/null | grep -c 'signedBy'
# Expected: >=1
Platform: linux
$ cat /etc/containers/policy.json 2>/dev/null | grep -c 'signedBy'
# Expected: >=1
Platform: linux
$ cosign verify --key <cosign_public_key> <image> 2>&1 | grep -c 'The following checks were performed'
# Expected: 1
Platform: any
$ kubectl run cme-unsigned-test --image=docker.io/library/busybox:latest --restart=Never --dry-run=server 2>&1 | grep -c -i 'denied\|forbidden\|failed.*verif\|signature'
# Expected: >=1
Platform: kubernetes

Remediation

kubernetes Kyverno ClusterPolicy with cosign image signature verification

cat <<'POLICY' | kubectl apply -f -
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-image-signature
spec:
  validationFailureAction: Enforce
  background: false
  rules:
    - name: verify-image-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "<registry>/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      <cosign_public_key>
POLICY
linux Podman/CRI-O container image trust policy via policy.json

cp /etc/containers/policy.json /etc/containers/policy.json.bak 2>/dev/null; cat > /etc/containers/policy.json <<'POLICY'
{
  "default": [
    {
      "type": "reject"
    }
  ],
  "transports": {
    "docker": {
      "<registry>": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/etc/pki/rpm-gpg/<signing_key_file>"
        }
      ],
      "registry.access.redhat.com": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
        }
      ]
    },
    "atomic": {
      "<registry>": [
        {
          "type": "signedBy",
          "keyType": "GPGKeys",
          "keyPath": "/etc/pki/rpm-gpg/<signing_key_file>"
        }
      ]
    }
  }
}
POLICY
cp <path_to_signing_public_key> /etc/pki/rpm-gpg/<signing_key_file>
← CME-711: Admission Controller Policy Enforcement (OPA/Gatekeeper/Kyverno) CME-713: Read-Only Root Filesystem Enforcement →