Verifying signed BEL artifacts

To verify the authenticity of BEL container images and Helm charts, use the following commands.

Verifying Helm charts

The BEL Helm charts may be verified using [GnuPG]. First, set up your GnuPG environment with the correct keys:

gpg --keyserver hkps://keys.openpgp.org --recv-keys 54A1FEAE3864F855F6724F03F09B21EC0428D8B8

Now, subsequent pulls of Helm charts can be done with verification:

helm repo add linkerd-buoyant https://helm.buoyant.cloud
helm repo update
helm pull --verify linkerd-buoyant/linkerd-enterprise-crds
helm pull --verify linkerd-buoyant/linkerd-enterprise-control-plane
helm pull --verify linkerd-buoyant/linkerd-enterprise-multicluster

Note: If helm --verify complains about a missing pubring.gpg file, your version of GPG is more modern than your version of Helm, and is using keybox files rather than keyring files. The easy fix is to export the key in legacy keyring form, with:

gpg --export 54A1FEAE3864F855F6724F03F09B21EC0428D8B8 > ~/.gnupg/pubring.gpg

Subsequent helm pull --verify commands should now be able to verify the charts as expected.

Verifying container images

BEL container images are published as multi-architecture OCI index images, which reference platform-specific images for linux/amd64 and linux/arm64. Each index image has an associated SBOM referencing the platform-specific images; and each platform-specific image has both SBOM and SLSA provenance attestations.

Cosign Attestations

BEL images are published with Cosign-signed attestations, as illustrated below:

  graph LR;
    subgraph I["OCI Index Image"]
      A["platform=linux/amd64"]
      B["platform=linux/arm64"]
    end

    IdxSPDX["predicateType=https://spdx.dev/Document"] -. index sbom .-> I

    AmdSPDX["predicateType=https://spdx.dev/Document"] -. linux/amd64 sbom .-> A
    AmdSLSA["predicateType=https://https://slsa.dev/provenance/v0.2"] -. linux/amd64 provenance .-> A

    ArmSPDX["predicateType=https://spdx.dev/Document"] -. linux/arm64 sbom .-> B
    ArmSLSA["predicateType=https://slsa.dev/provenance/v0.2"] -. linux/arm64 provenance .-> B

To verify the signatures and attestations of BEL multi-architecture index images, we can use the cosign and oras CLI tools. For example, to verify the BEL enterprise-2.19.0 proxy images:

ISSUER=https://token.actions.githubusercontent.com
IDENTITY=https://github.com/BuoyantIO/enterprise-linkerd/.github/workflows/push-oci.yml@refs/heads/enterprise-2.19

IMAGE=ghcr.io/buoyantio/proxy
TAG=enterprise-2.19.0

INDEX_IMAGE=$IMAGE@$(oras resolve $IMAGE:$TAG)
cosign verify --certificate-identity=$IDENTITY --certificate-oidc-issuer=$ISSUER $INDEX_IMAGE
cosign verify-attestation --type=spdxjson --certificate-identity=$IDENTITY --certificate-oidc-issuer=$ISSUER $INDEX_IMAGE

We can also verify platform-specific images and their attestations. For example, to verify the linux/arm64 proxy image for enterprise-2.19.0 (using the environment variables set in the previous step):

ARM64_IMAGE=$IMAGE@$(oras resolve --platform linux/arm64 $IMAGE:$TAG)
cosign verify --certificate-identity=$IDENTITY --certificate-oidc-issuer=$ISSUER $ARM64_IMAGE
cosign verify-attestation --type=spdxjson --certificate-identity=$IDENTITY --certificate-oidc-issuer=$ISSUER $ARM64_IMAGE
cosign verify-attestation --type=slsaprovenance02 --certificate-identity=$IDENTITY --certificate-oidc-issuer=$ISSUER $ARM64_IMAGE

OCI 1.1 Referrers

BEL publishes SBOM and SLSA provenance artifacts as OCI 1.1 referrers, meaning that they live in the registry alongside the images, rather than as separate files, and are digest-signed and immutable.

  graph LR;
    subgraph I["OCI Index Image"]
      A["platform=linux/amd64"]
      B["platform=linux/arm64"]
    end

    IdxSPDX["artifactType=application/spdx+json"] -. index sbom .-> I

    AmdSPDX["artifactType=application/spdx+json"] -. linux/amd64 sbom .-> A
    AmdSLSA["artifactType=application/slsa+json"] -. linux/amd64 provenance .-> A

    ArmSPDX["artifactType=application/spdx+json"] -. linux/arm64 sbom .-> B
    ArmSLSA["artifactType=application/slsa+json"] -. linux/arm64 provenance .-> B

These attestations can be discovered using the oras discover command:

$ oras discover $IMAGE:$TAG
ghcr.io/buoyantio/proxy@sha256:6a3a51946c8404bf0d73d5d58ef65a262e83eee2f3b0680a6510ad6b717510ef
└── application/spdx+json
    └── sha256:98bc985bfaa08e3424679f25f1adf92189cfe1d363f3221f7bec7d8766326ace
        └── [annotations]
            ├── org.opencontainers.image.created: "2025-10-31T23:48:39Z"
            ├── vnd.buoci.artifact-type: application/spdx+json
            ├── vnd.buoci.predicate-type: <https://spdx.dev/Document>
            ├── vnd.buoci.role: index-attestation
            ├── vnd.buoci.subject.digest: sha256:6a3a51946c8404bf0d73d5d58ef65a262e83eee2f3b0680a6510ad6b717510ef
            └── vnd.buoci.subject.name: proxy
$ oras discover $IMAGE:$TAG --platform linux/arm64
ghcr.io/buoyantio/proxy@sha256:0a8ad152750643d0f2097c7241288583d36a02fb7a5a945ce48a0e3fb214496d
├── application/slsa+json
│   └── sha256:7a455bd53a395313ae22669100b61ad1e5a38c8f6d62667bc730f6f99efa8bfd
│       └── [annotations]
│           ├── org.opencontainers.image.created: "2025-10-31T23:48:39Z"
│           ├── vnd.buoci.artifact-type: application/slsa+json
│           ├── vnd.buoci.predicate-type: https://slsa.dev/provenance/v0.2
│           ├── vnd.buoci.role: image-attestation
│           ├── vnd.buoci.subject.digest: sha256:0a8ad152750643d0f2097c7241288583d36a02fb7a5a945ce48a0e3fb214496d
│           └── vnd.buoci.subject.name: proxy
└── application/spdx+json
    └── sha256:2220a2707537cbbc961264bf9b3dfa107343f6eaa38d4ed8cd53c8d2eeafd725
        └── [annotations]
            ├── vnd.buoci.role: image-attestation
            ├── vnd.buoci.subject.digest: sha256:0a8ad152750643d0f2097c7241288583d36a02fb7a5a945ce48a0e3fb214496d
            ├── vnd.buoci.subject.name: proxy
            ├── org.opencontainers.image.created: "2025-10-31T23:48:39Z"
            ├── vnd.buoci.artifact-type: application/spdx+json
            └── vnd.buoci.predicate-type: https://spdx.dev/Document

Each of these referrer artifacts is signed with Cosign, and can be verified using cosign:

SPDX_REFERRER=$IMAGE@$(oras discover --format json --artifact-type=application/spdx+json $IMAGE:$TAG | jq -r '.referrers[] | .digest')
cosign verify --certificate-identity=$IDENTITY --certificate-oidc-issuer=$ISSUER $SPDX_REFERRER

Example: Enforcing runtime policy with Kyverno

Kyverno enforces image signature and SBOM verification policies at runtime. The following Kyverno policy ensures that BEL’s multi-architecture index images are signed and have signed SPDX attestations:

apiVersion: policies.kyverno.io/v1alpha1
kind: ImageValidatingPolicy
metadata:
  name: bel-2-19-index-attestations
spec:
  failurePolicy: Fail
  webhookConfiguration:
    timeoutSeconds: 20
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
        operations: ["CREATE","UPDATE"]

  matchImageReferences:
    - glob: "ghcr.io/buoyantio/proxy:enterprise-2.19.*"
    - glob: "ghcr.io/buoyantio/proxy-init:enterprise-2.19.*"
    - glob: "ghcr.io/buoyantio/controller:enterprise-2.19.*"
    - glob: "ghcr.io/buoyantio/extension-init:enterprise-2.19.*"

  validationActions: [Deny]
  validationConfigurations:
    mutateDigest: true
    required: true
    verifyDigest: true

  # Configure your signing attestor
  attestors:
    - name: bel-keyless
      cosign:
        ctlog:
          url: https://rekor.sigstore.dev
        keyless:
          identities:
            - issuer: https://token.actions.githubusercontent.com
              subject: https://github.com/BuoyantIO/enterprise-linkerd/.github/workflows/push-oci.yml@refs/heads/enterprise-2.19

  attestations:
    - name: spdx
      intoto:
        type: https://spdx.dev/Document

  validations:
    - expression: >-
        images.containers.map(image, verifyImageSignatures(image,  [attestors["bel-keyless"]])).all(e, e > 0)
      message: "Failed image signature verification"

    - expression: >-
        images.containers.map(image, verifyAttestationSignatures(image, attestations.spdx, [attestors["bel-keyless"]])).all(e, e > 0)
      message: "Failed to verify SBOM attestation with Cosign keyless"