With this hello-transparent-release repo we showcase the use of the
Container-based SLSA3 builder,
which uses a Docker-based build tool. This tool used to be part of the
Transparent Release
project, hence the name of this repo 😄
In this repo lives a
Java program
printing Hello Transparent Release to stdout.
We want to generate a non-forgeable provenance statement for the
Hello Transparent Release binary. To do this, we use the Container-based
SLSA3 builder GitHub workflow. The workflow first builds the binary, then
generates a SLSA v1.0 provenance statement,
signs it, and publishes the signature to
Rekor.
First, we are going to give an overview of:
Then we will describe how to:
- Create a buildconfig file
 - Create a GitHub Actions workflow to call the container-based SLSA3 builder
 - Run the builder tool locally
 - Create a custom Docker image to use with the container-based SLSA3 builder
 
To use the container-based SLSA3 builder, you need
- An OCI builder image, in which the build command for building the binary is executed.
 - A GitHub repository containing your source code.
 - A buildconfig file, containing the build configuration info.
 - A GitHub actions workflow to call the container-based SLSA3 builder.
 
For most of this tutorial, we will use a pre-built Maven Docker image. But you can, as well, create and use a custom image, as described below.
The Docker-based build tool uses Docker CLI to build one or more binaries from your source code. It is used by the container-based SLSA3 builder to build your binaries, but you can also use it locally for testing.
The Docker-based build tool first fetches the source code from your Git repository at a given commit hash. When called by the container-based SLSA3 builder, the commit hash is taken from the GitHub context, and you do not have to explicitly provide it. When running the Docker-based build tool locally, you have to explicitly specify the SHA1 digest of the Git commit as one of the inputs to the tool.
Once the Docker-based build tool has checked out the Git repository at the
given commit, it runs the docker run command to build the binary. For this,
you need to provide the Docker-based build tool with an OCI builder image, and
a build command.
You have to explicitly specify the OCI builder image both when using container-based SLSA3 builder, and when using the Docker-based build tool locally. Similarly, in both cases, you have to explicitly specify the build command, by including it in a buildconfig file, and providing that as input to the container-based SLSA3 builder or directly to Docker-based build tool.
A buildconfig file is a simple toml file. Currently you are required to
provide two fields: command and artifact_path. In the future, we may add
support for additional (possibly optional) build configuration information (see
the tracking issue).
command is a string array containing the command that will be passed to
docker run together with the builder image (e.g., the Maven image mentioned
above). The docker run runs at the root of your Git repository and is
expected to build one or more files (binaries or other software artifacts) in a
path under the root of your repository.
artifact_path is a path, relative to the root of your repository, where you'd
expect to find the built files once the execution of the docker run command
is completed. You can use wildcards to specify the path. This is useful when
your command generates multiple files.
Here is the buildconfig we
use for building the hello-transparent-release binary as a jar file.
Now we have everything that we need to use the Container-based SLSA3 builder.
The workflow that we use in this repository contains a single job:
jobs:
  generate_provenance:
    permissions:
      actions: read
      id-token: write
      contents: write
    uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
    with:
      builder-image: "maven"
      builder-digest: "sha256:545933763425d1afde0cb5da093dd14c8bf49c0849ca78d7f4f827894d7b1f1e"
      config-path: "buildconfigs/hello_transparent_release_mvn.toml"
      provenance-name: "hello_transparent_release.intoto"
      compile-builder: trueHere we have specified the builder image by its name (maven) and digest
(sha256:545933763425d1afde0cb5da093dd14c8bf49c0849ca78d7f4f827894d7b1f1e).
You can find this information on the container registry that you intend to use.
For this tutorial we used the latest
official maven image for linux/amd64,
form 27 March 2023. The info page on hub.docker.com contains the image
digest.
The third input parameter is config-path, which is the path to the buildconfig
file that we created above.
The next input parameter is provided for convenience. We have specified a custom name for the provenance that the container-based SLSA3 builder generates. This is useful where you have multiple jobs in your workflow, each building and generating a provenance for a different binary.
Note that while the workflow is called the "container-based SLSA3 builder", the
file where this reusable workflow is implemented is called
builder_container-based_slsa3.yml! This is to be explicit about the use of the
Docker-based build tool in this workflow.
See the complete workflow here. We run this workflow both on pull-request events, and on push events. But only the provenances that are generated on push events are signed.
For a detailed description about how to use the container-based SLSA3 builder, see this documentation.
As mentioned above, you can use the Docker-based build tool locally. You can use it to verify your buildconfig, build your binary or output artifacts, or verify a previously generated provenance. In this section, we only describe how to build the artifacts using the Docker-based build tool. Please consult this documentation for other use cases, and a full list of features.
First you need to to build the Docker-based build tool from source.
First, checkout the slsa-github-generator repository.
$ git clone --depth 1 [email protected]:slsa-framework/slsa-github-generator.gitThen build the Docker-based build tool using the following commands:
$ cd slsa-github-generator/
$ cd cd internal/builders/docker/
$ go build -o docker-builder *.goThis builds a docker-builder, which you can use to build your own binaries with.
Use the following command to build the binary, and measure its SHA256 digest:
$ ./docker-builder build \
  --build-config-path buildconfigs/hello_transparent_release_mvn.toml \
  --builder-image maven@sha256:545933763425d1afde0cb5da093dd14c8bf49c0849ca78d7f4f827894d7b1f1e \
  --git-commit-digest sha1:211e4cdf273680b8ed8e0ba4de0131c2dfe7cc94 \
  --source-repo git+https://github.com/project-oak/hello-transparent-release \
  --subjects-path subjects.json \
  --output-folder /tmp/build-outputs \
  --force-checkoutThis command will:
- Create a temp directory
 - Checkout the 
hello-transparent-releaserepository into that temp directory - Check out the commit 
sha1:211e4cdf273680b8ed8e0ba4de0131c2dfe7cc94 - Run the 
docker runcommand usingmaven@sha256:545933763425d1afde0cb5da093dd14c8bf49c0849ca78d7f4f827894d7b1f1eas the builder image, andbuildconfigs/hello_transparent_release_mvn.tomlfrom thehello-transparent-releaserepository as the buildconfig
 - Generates the 
subject.jsonfile and stores the SHA256 digest of the generatedjarfile in it - The temp directory, including the generated jar file, is removed when the execution of the command is completed.
 
Here is the content of the subject.json file in this case:
[
  {
    "name": "hello-transparent-release-1.0-SNAPSHOT.jar",
    "digest": {
      "sha256": "bce4fb947412570f5408256613c94f38e7e5339a7d5a6a2556142de070d54540"
    }
  }
]Note that you may get a different digest if you run this command. This is because the build with Maven is not reproducible. Below we show how you can use Bazel and a custom Dockerfile to make the build reproducible.
If you want to keep the binary, and inspect it after the execution of the command, you can use the following command:
$ cd <root-of-the-repo>
$ <path-to-docker-builder> build \
  --build-config-path buildconfigs/hello_transparent_release_mvn.toml \
  --builder-image maven@sha256:545933763425d1afde0cb5da093dd14c8bf49c0849ca78d7f4f827894d7b1f1e \
  --git-commit-digest sha1:eb70c4bd784368b5c363b4e7132bcf5fb1d698d1 \
  --source-repo git+https://github.com/project-oak/hello-transparent-release \
  --subjects-path subjects.json \
  --output-folder /tmp/build-outputsNote that this command is very similar to the one above, but we are running it
in the root of the repo, and we have removed the --force-checkout option. In
this case, instead of fetching the sourced from the GitHub repository, we use
the local source code. Also, we have used the latest commit on the local repo
as the commit hash. However, this is not very accurate, since your local
repository may contain uncommitted changes. This is not an issue if you are
using the tool only for testing.
So far, we have been relying on a pre-built Maven image for building the binary. However, you could use a custom image with the container-based SLSA3 builder. One way to create a custom image is to use a Dockerfile. In this section, we walk you through creating a custom builder image using a Dockerfile.
This time, we are going to build the binary using Bazel instead of Maven.
First, we need to make sure to have Bazel set-up.
Then, we can build with
./scripts/buildWe run the binary:
./out/HelloTransparentReleaseThis is the binary we want to release: our HelloTransparentRelease binary.
Now, let's compute a sha256 digest of the HelloTransparentRelease binary:
sha256sum ./out/HelloTransparentRelease
8a87337c16d1386510f9d3dd36a744d267945370e40c18113c78bb67e2934cae HelloTransparentReleaseThis sha256 digest might or might not be the same on your machine.
Our goal is to make this to be the same for whoever builds the
HelloTransparentRelease binary.
That is why we build a builder Docker image that has everything installed to
build the HelloTransparentRelease binary.
We need to provide a Dockerfile to build our builder Docker
image. We name our Docker image hello-transparent-release.
We want the HelloTransparentRelease binary built by the builder Docker image
to have the same permissions as the user. To do so, we use
rootless Docker.
To build our builder Docker image we run
./scripts/docker_build.
Our builder Docker image has to be publicly available, so we push it to a
registry
(given the right permissions):
europe-west2-docker.pkg.dev/oak-ci/hello-transparent-release/
with ./scripts/docker_push. To achieve public
availability, the image must be made Readable by all users on Google Artifact
Registry.
We can now see the latest builder Docker image here.
Pushing the Docker image to a registry will give us a manifest and a DIGEST
to identify the image
published in the registry.
We will need this digest to configure the cmd/builder tool.
sha256:d682d6f0f2bbec373f4a541b55c03d43e52e774caa40c4b121be6e96b5d01f56You can now update the GitHub Actions workflow to use the your custom builder
image. For this, all you have to do is to update the builder-image and the
builder-digest as follows:
with:
  builder-image: "europe-west2-docker.pkg.dev/oak-ci/hello-transparent-release/hello-transparent-release"
  builder-digest: "sha256:eb0297df0a4df8621837369006421dd972cc3e68e6da94625539f669d49f1525"
  # ...