Generate and store SLSA provenance

Supply-chain levels for software artifacts (SLSA and pronounced like "salsa") is an industry-consensus specification for describing and gradually improving artifact supply chain security.

When using Buildkite Pipelines with Package Registries, you can publish software packages and artifacts to registries with SLSA provenance in only four steps.

This guide uses the following Buildkite examples to demonstrate this process:

  • Buildkite organization: nova-corp
  • Pipeline: ruby-logger-gem, which builds a RubyGem package
  • Registry: ruby-gems to store the RubyGem

Although this guide uses RubyGems as examples, this process will work for all supported package ecosystems (with the exception of OCI-based packages like Container (Docker) and Helm OCI).

Step 1: Configure steps to generate SLSA provenance

The Generate Provenance Attestation Buildkite Plugin generates a SLSA provenance attestation for artifacts that have been uploaded to artifact storage in a pipeline step.

First, configure a step step that builds a RubyGem gem and uploads it to artifact storage.

steps:
  - label: "Build Gem"
    command: "gem build logger.gemspec"
    artifact_paths: "logger-*.gem"

A SLSA provenance attestation can be generated by adding this plugin to your pipeline step that builds the package or artifact:

steps:
  - label: "Build Gem"
    command: "gem build logger.gemspec"
    artifact_paths: "logger-*.gem"
    plugins:
      - generate-provenance-attestation#v1.1.0:
          artifacts: "logger-*.gem"
          attestation_name: "gem-attestation.json"

In the example above, a SLSA provenance attestation will be generated for artifacts matching logger-*.gem and be uploaded it to artifact storage as gem-attestation.json.

Screenshot of artifacts showing the attestation generated by the Generate Provenance Attestation Buildkite Plugin.

Once this step is complete, gem-attestation.json will be available to subsequent steps in the pipeline.

See an example of a SLSA provenance statement that this plugin generates. This SLSA provenance statement is then serialized and uploaded as a dead simple signing envelope (DSSE). This envelope.json DSSE file shows an example of the gem-attestation.json file format. Learn more about DSSE in DSSE Envelope.

Step 2: Configure steps to publish a package with SLSA provenance

The Publish to Packages plugin allows you to quickly and easily publish a package to Package Registries. When the attestations: attribute is set, the package will be published from artifact storage with the specified attestations.

steps:
  - label: "Publish Gem"
    plugins:
      - publish-to-packages#v2.2.0:
          artifacts: "logger-*.gem"
          registry: "nova-corp/ruby-gems"
          attestations:
            - "gem-attestation.json"

In the example above, artifacts matching logger-*.gem will be published to nova-corp/ruby-gems Package Registry. Additionally, they will be published with the gem-attestation.json attestation.

Step 3: Define an OIDC policy for the registry

The Publish to Packages plugin authenticates with Package Registries using an Agent OIDC token. Therefore, an OIDC policy must be configured on the Ruby registry.

OIDC policy for nova-corp/ruby-gems registry
- iss: "https://agent.buildkite.com"
  claims:
    organization_slug: "nova-corp"
    pipeline_slug: "ruby-logger-gem"

In the example above, the policy allows the Buildkite pipeline with slug ruby-logger-gem, configured in the Nova Corp Buildkite organization (with slug nova-corp) to publish packages to the Ruby registry named ruby-gems.

Screenshot of Artifacts showing the attestation generated by the Generate Provenance Attestation plugin.

Step 4: Complete the pipeline

All the steps above come together in a simple pipeline that builds and publishes a Ruby Gem with SLSA Provenance. A step dependency ensures that the Publish Gem step does not start until the Build Gem step has finished successfully.

pipeline.yml
steps:
  - label: "Build Gem"
    key: "build-gem"
    command: "gem build logger.gemspec"
    artifact_paths: "logger-*.gem"
    plugins:
      - generate-provenance-attestation#v1.1.0:
          artifacts: "logger-*.gem"
          attestation_name: "gem-attestation.json"

  - label: "Publish Gem"
    depends_on: "build-gem"
    plugins:
      - publish-to-packages#v2.2.0:
          artifacts: "logger-*.gem"
          registry: "nova-corp/ruby-gems"
          attestations:
            - "gem-attestation.json"

This generates a build that looks something like this:

Screenshot of Artifacts showing the attestation generated by the Generate Provenance Attestation plugin.

The SLSA provenance will then be visible under the Attestations tab of a package details page.

Screenshot of Artifacts showing the attestation generated by the Generate Provenance Attestation plugin.

Summary