Blog

Bringing first-class support to SBOMs and attestations for Constellation containers

Fabian Kammel


Constellation, our Confidential Kubernetes engine, is now open source. Check it out on GitHub or say "Hi" on Discord.


In a previous post, we explored how to generate a Software Bill of Materials (SBOM) and subsequently scan them for vulnerabilities. In this post, we show you how SBOMs can be signed and then stored in the same container registry as the scanned image. This improves security & discoverability!


Why should I sign my SBOM?


Up until now, our generated SBOM is a plain JSON file with some metadata and a list of dependencies. This leaves lots of room for malicious actors to manipulate our SBOM before it gets to our users.


How could a malicious actor benefit?

  1. They could inject additional (outdated) dependencies into the SBOM, to claim the software is vulnerable and damage the reputation of a vendor.
  2. They could remove dependencies from the SBOM, to destroy the transparency SBOMs enable in the first place. This would prevent a user from patching an existing vulnerability because the critical information is missing.


Signing our SBOMs enables end users to trust the dependency information we publish and make sure it has not been tampered with!


What is an attestation?


On the other side, the consumer needs more than just the signature. A valid signature proves that the private key and signed data were in the same place at the same time. The signed data needs to make a claim (predicate) about a thing (subject).


Our signature proves that the SBOM was generated for our specific artifact at a given point in time.


Finally, in-toto attestations are a developing standard and define a format to hold exactly this information: a signature and payload, which contains a subject and predicate.


Where to store the attestation?


In our v2.1.0 release of Constellation, we stored the SBOMs for our container images in our GitHub release, while our images were stored in the GitHub Container Registry (ghcr.io). This made it difficult for users to discover our SBOMs.

$ cosign tree ghcr.io/edgelesssys/constellation/verification-
service:v2.1.0 📦 
Supply Chain Security Related artifacts for an image: 
ghcr.io/edgelesssys/constellation/verification-service:v2.1.0 No Supply Chain 
Security Related Artifacts artifacts found for image 
ghcr.io/edgelesssys/constellation/verification-service:v2.1.0


Since then we learned that cosign attach can be used to store this critical meta information directly with the artifact in the same registry!

$ cosign attach --help Provides utilities for attaching artifacts to other 
artifacts in a registry Usage: cosign attach [command] Available Commands: 
attestation Attach attestation to the supplied container image sbom Attach sbom 
to the supplied container image signature Attach signatures to the supplied 
container image


Bringing it all together


The actual implementation is quite easy, once you understand what each step is for. Syft and sigstore already took care of the integration work, so Syft can directly sign with our cosign.key.

IMAGE_REF=ghcr.io/edgelesssys/constellation/verification-service:v2.2.0 syft 
attest --key cosign.key -o cyclonedx-json ${IMAGE_REF} > verification-
service.att.json cosign attach attestation --attestation verification-
service.att.json ${IMAGE_REF} cosign verify-attestation ${IMAGE_REF} --type 
'https://cyclonedx.org/bom' --key cosign.pub


The attached metadata can be discovered and consumed automatically.

$ cosign tree ghcr.io/edgelesssys/constellation/verification-
service@sha256:fe31333a0696e4b044a2e1e7c6d6c88d7c387753350f68e7533383e1c70e6360 
📦 Supply Chain Security Related artifacts for an image: 
ghcr.io/edgelesssys/constellation/verification-
service@sha256:fe31333a0696e4b044a2e1e7c6d6c88d7c387753350f68e7533383e1c70e6360 
└── 💾 Attestations for an image tag: 
ghcr.io/edgelesssys/constellation/verification-service:sha256-
fe31333a0696e4b044a2e1e7c6d6c88d7c387753350f68e7533383e1c70e6360.att └── 🍒 
sha256:8a369a8bac7d8d9ea8540e1d90478842b2e79cb4a2ea248636d99daa0b60186b


Additional info: Finding the predicate type


At the time of writing this, an open issue exists, which requires the user to manually specify the --type parameter when using cosign verify-attestation.


It is not trivial to find the value for this parameter because a lot of encoded and nested data structures need to be unpacked to find the actual value. As commented on the issue, cosign download can be used to fetch the in-toto attestation. The payload field contains the base64 encoded in-toto statement, which contains the predicateType we are looking for.

A handy one-liner can be used to fetch the information easily: 

cosign download attestation <image_ref> | jq -r .payload | base64 -d | jq .predicateType


Follow Edgeless Systems, to learn how we continue to secure the supply chain of Constellation, and don't forget to star it on GitHub!


Author: Fabian Kammel


Related reading

View all