Skip to content

Supply Chain Attestation

Every image PaaS Runtime builds is signed and bound to an SBOM (Software Bill of Materials), so tenants — and auditors — can verify what was built, who built it, and that nobody tampered with the image between Harbor and the running pod.

Pipeline

sequenceDiagram
  participant K as Kaniko / Paketo
  participant S as syft
  participant C as cosign
  participant H as Harbor
  participant T as Tenant / Auditor

  K->>H: push image (digest @sha256:…)
  K->>S: invoke syft on the workspace
  S-->>K: sbom.json (CycloneDX 1.4)
  K->>C: cosign sign + cosign attest --predicate sbom.json
  C->>H: push .sig + .att artefacts beside image
  T->>H: cosign verify --key cosign.pub <image>
  H-->>T: verification ok + provenance annotations

The two Tekton Tasks (sbom-generate from anchore/syft and cosign-sign from Sigstore) live in the paas-build namespace and are referenced by both the Paketo (paas-build-pipeline) and Dockerfile (paas-dockerfile-pipeline) pipelines — a single image lineage regardless of how it was built.

Why supply-chain attestation matters

  • HDS / SecNumCloud — French health and government cloud certifications require provable image provenance.
  • EU CRA / sovereignty — auditors need to prove that the running binary matches a specific source commit, signed by a key that lives inside the cluster (no third-party CA dependency).
  • SLSA Level 2 — the build platform produces signed provenance for every artefact it ships.
  • Sigstore ecosystemcosign verify is the de-facto standard for OCI-image signature verification across Kubernetes tooling.

Verify an image locally

The cluster's cosign public key is exposed at a stable, unauthenticated URL so anyone — including external auditors — can fetch it without a PaaS account:

curl -sf https://runtime.di2amp.com/api/v1/cosign.pub > cosign.pub

cosign verify --key cosign.pub \
  harbor.paas-system.svc.cluster.local/builds/my-app:build-abc123

The cosign verify output includes the provenance annotations the PaaS injected at sign time:

{
  "annotations": {
    "tenant_id":  "octave",
    "app_id":     "my-app",
    "commit_sha": "deadbeef…",
    "build_id":   "build-abc123"
  }
}

You can pin a verification policy on those annotations — e.g. refuse to deploy anything whose tenant_id doesn't match yours, or require a specific commit SHA before promoting to production.

Read the SBOM

# Via cosign (downloads the CycloneDX attestation from Harbor):
cosign download sbom \
  harbor.paas-system.svc.cluster.local/builds/my-app:build-abc123 \
  | jq '.components | length'

# Or via the PaaS API (JWT required, returns the same JSON):
curl -sf -H "Authorization: Bearer $TOKEN" \
  https://runtime.di2amp.com/api/v1/apps/my-app/builds/build-abc123/sbom \
  | jq '.metadata.component.name, .components[0:5]'

API endpoints

Method Path Auth Description
GET /api/v1/cosign.pub public Cluster cosign public key (PEM)
GET /v1/apps/{app_id}/builds/{build_id}/sbom JWT CycloneDX SBOM (returns 404 if not yet generated)
GET /v1/apps/{app_id}/builds/{build_id}/signature JWT Cosign signature payload (returns 404 if not yet signed)

A 404 on the SBOM or signature endpoint means not yet produced — the build may still be running, or the cluster's cosign-keys Secret hasn't been generated yet (see HOTFIXES.md). The dashboard's SignedBadge treats 404 as the "not signed yet" baseline rather than an alarming failure.

Compliance summary

Control Status
SBOM format CycloneDX 1.4 (via syft v1.0.1)
Signature scheme sigstore cosign v2.2.3
Key storage K8s Secret cosign-keys in namespace paas-build (private key never leaves cluster)
Provenance annotations tenant_id, app_id, commit_sha, build_id
SLSA level L2 (signed build provenance)