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"