Supply chain verification (cosign & SBOM)
Audience: DevOps / platform engineers Time: ~25 min Prerequisites: Custom deployment (Kubernetes & Helm), cosign installed on your build workstation
Control Core supports optional container image signing and SBOM generation on your engineering release path. Signing requires your organization to provision and manage signing keys. Nothing is signed by default — your pipeline must be configured to enable it.
What is implemented
| Capability | What you receive | Default |
|---|---|---|
| Image signing (cosign) | A signing hook in the build script that calls cosign sign when keys are configured | Skipped when keys or tooling absent |
| SLSA provenance attestation | docker buildx --provenance attaches a SLSA level-1 provenance envelope to each image | On when DOCKER_PLATFORM set |
| SBOM attestation | docker buildx --sbom attaches a CycloneDX SBOM to each image | On when DOCKER_PLATFORM set |
| Merged CycloneDX SBOM | A SBOM generation script merges dependency data from all components | Regenerate when deps change |
| Vulnerability gate | Run the security scan script (trivy, pip-audit, npm audit, bandit, semgrep) before promoting | Required before production rollout |
Enable signing on your release builds
Configure your keys once, then run the build script as usual:
# Option A — AWS KMS-backed key (recommended for production)
export COSIGN_KMS_KEY="awskms:///arn:aws:kms:REGION:ACCOUNT:key/KEY_ID"
# or by alias:
# export COSIGN_KMS_KEY="awskms:///alias/your-cosign-key"
# Option B — local key file (development / air-gapped)
export COSIGN_KEY="/path/to/cosign.key"
# Build and push with signing
export DOCKER_PLATFORM=linux/amd64
scripts/build-and-push-ecr.sh --target all --tag YOUR_RELEASE_TAG
The script signs each image immediately after push. If cosign is not in PATH or no key is configured, signing is skipped with a warning — the build succeeds but the image is unsigned.
Troubleshooting:
cosign not installed — skipping signing— install cosign from docs.sigstore.dev. For SOC 2 CC6.8 evidence, do not promote unsigned images to a bank pilot without a documented exception in your change management system.
Verify a pushed image
Replace IMAGE with your registry path and tag:
export IMAGE="REGISTRY/controlcoreio/control-plane-api:RELEASE_TAG"
# Verify signature with KMS key
cosign verify --key "${COSIGN_KMS_KEY}" "$IMAGE"
# Verify signature with public key file
cosign verify --key /path/to/cosign.pub "$IMAGE"
Expected output: exit code 0 and a JSON array of verified signature payloads. Any non-zero exit means the image was not signed with the expected key.
Troubleshooting:
no matching signatures— the image was pushed without signing, or the wrong key is configured. Re-push withCOSIGN_*set and record the image digest in your change management record.
Read SLSA provenance attestations
# Retrieve and display the SLSA provenance envelope
cosign verify-attestation \
--key "${COSIGN_KMS_KEY}" \
--type slsaprovenance \
"$IMAGE" | jq '.payload | @base64d | fromjson'
The decoded payload includes: builder ID, build configuration (Dockerfile, args), build timestamp, and the materials (git commit SHA, base image digest) used to produce the image. Use this record as your build-time audit trail for SOC 2 CC6.8.
Read SBOM attestations
# Retrieve the CycloneDX SBOM attached to the image
cosign verify-attestation \
--key "${COSIGN_KMS_KEY}" \
--type sbom \
"$IMAGE" | jq '.payload | @base64d | fromjson'
The SBOM lists all OS packages, Python/Go libraries, and their versions. Feed this into your preferred SBOM analysis tool (Dependency-Track, Grype, Trivy) for vulnerability correlation.
Enforce signature verification with Kyverno (recommended)
Install Kyverno in your cluster, then apply an ImageVerification policy that blocks unsigned or improperly signed images:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: controlcore-image-verification
spec:
validationFailureAction: Enforce
rules:
- name: verify-controlcore-images
match:
any:
- resources:
kinds: [Pod]
namespaces: [controlcore]
verifyImages:
- imageReferences:
- "REGISTRY/controlcoreio/*:*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
# Paste your cosign public key (PEM) here
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
attestations:
- predicateType: https://slsa.dev/provenance/v0.2
conditions:
- all:
- key: "{{ builder.id }}"
operator: Equals
value: "https://your-ci.example.com"
Troubleshooting: Pod fails
ImageVerificationFailed— confirm the public key PEM matches the key used to sign, and that the image reference pattern covers your registry prefix. View Kyverno policy reports:kubectl get policyreport -n controlcore.
Enforce with OPA Gatekeeper (alternative)
Apply a ConstraintTemplate that calls cosign (or an admission webhook) to verify signatures before pod admission. Refer to Gatekeeper's external data provider for the verification sidecar pattern.
SBOM and vulnerability posture
Regenerate the merged SBOM whenever application dependencies change using the SBOM generation script included with your release package.
Before promoting a tag to production, pair SBOM review with vulnerability scanning:
# Container layer scan
trivy image --severity HIGH,CRITICAL "$IMAGE"
# Python dependency audit (from the API requirements file)
pip-audit -r requirements.txt
# Node dependency audit (from the Control Plane UI source)
npm audit --audit-level=high
Customer packages on controlcore2026 contain deliverables only — no engineering monorepo source.
SOC 2 alignment (honest scope)
| TSC | What Control Core provides | What you operate |
|---|---|---|
| CC6.8 | Signing hooks, SLSA provenance, SBOM metadata, local vuln gate | Key custody, mandatory verify before prod, exception process, admission policy |
| CC9.2 | SBOM + provenance attestations | Vendor review, accept/fix/suppress decisions |
Request a SOC 2 evidence packet from your account team when preparing for audit.