Supply chain: SBOMs, signatures and provenance

What a platform release publishes so that you can check what you run, and the commands to check it.

What a Nistor platform release (vX.Y.Z, vX.Y.Z-rc.N) publishes so that you can check what you run, and the commands to check it. Everything here is signed and uploaded only by the release jobs of the public project's pipeline (ci/release.yml), started by hand on a protected release tag.

The images#

Two images per release, in the project's container registry:

Image Contents
registry.gitlab.com/nistor-io/nistor/nistor-runtime:<tag> every server role, installed from the release's lock
registry.gitlab.com/nistor-io/nistor/nistor-shell:<tag> the web shell, served by nginx

Before anything is pushed, both images are scanned for known vulnerabilities; a critical or high vulnerability with an available fix stops the release. Each image is then pushed and, by its digest:

  • an SBOM: a CycloneDX JSON document generated with syft;
  • a signature: a keyless cosign signature, made with the release job's short-lived GitLab OIDC identity (no long-lived signing key exists);
  • an SBOM attestation: the same CycloneDX document attached to the image as a signed in-toto attestation (cosign attest --type cyclonedx).

Inside the images:

  • nistor-runtime carries its lock and its description as /etc/nistor/distribution.lock and /etc/nistor/distribution.json, and the licences of its third-party Python packages as /usr/share/nistor/THIRD_PARTY_NOTICES.md;
  • nistor-shell serves the licences of its third-party npm packages at /THIRD_PARTY_NOTICES.md.

The image build refuses a third-party package whose licence the project's policy does not allow.

Verify an image#

You need cosign 2.x. Work with the digest, not the tag: a tag can move, a digest cannot.

IMAGE=registry.gitlab.com/nistor-io/nistor/nistor-runtime
TAG=v1.0.0
DIGEST=$(docker buildx imagetools inspect "$IMAGE:$TAG" --format '{{json .Manifest.Digest}}' | tr -d '"')

# The signature: made by the release pipeline of nistor-io/nistor on a release tag.
cosign verify "$IMAGE@$DIGEST" \
  --certificate-oidc-issuer https://gitlab.com \
  --certificate-identity-regexp '^https://gitlab\.com/nistor-io/nistor//\.gitlab-ci\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$'

# The SBOM attestation, with the same identity; the predicate is the CycloneDX document.
cosign verify-attestation "$IMAGE@$DIGEST" --type cyclonedx \
  --certificate-oidc-issuer https://gitlab.com \
  --certificate-identity-regexp '^https://gitlab\.com/nistor-io/nistor//\.gitlab-ci\.yml@refs/tags/v[0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?$'

The same commands verify nistor-shell with IMAGE=registry.gitlab.com/nistor-io/nistor/nistor-shell.

The identity is the GitLab CI configuration of the public project at a release tag: a signature made from any other project, branch or fork does not match it.

The SBOM files#

Each GitLab release page links, under its assets, to:

  • SBOM (nistor-runtime, CycloneDX): sbom/nistor-runtime.cdx.json
  • SBOM (nistor-shell, CycloneDX): sbom/nistor-shell.cdx.json
  • distribution.lock (nistor-oss): the lock the runtime image was built from

These are the artefacts of the release job, kept for one year. They are the same documents as the attestations above; the attestation is the signed copy.

The Python packages#

The release's wheels and source distributions are published to PyPI with trusted publishing: PyPI accepts an upload only from the release job of the public project, which exchanges its GitLab OIDC identity for a short-lived upload token. No PyPI token is stored anywhere.

The release does not upload PEP 740 attestations to PyPI.

What is not covered#

  • The npm packages of the web workspace are not published with this release.
  • Tools (tool-<id>/vA.B.C) are released as Python packages with the same trusted publishing; they have no image.

Edit this page on GitLab

↑ ↓ to moveEnter to openEsc to close