diff --git a/docs/slsa-definitions.md b/docs/slsa-definitions.md new file mode 100644 index 000000000000..76f25d9847b9 --- /dev/null +++ b/docs/slsa-definitions.md @@ -0,0 +1,589 @@ +# SLSA definitions + +BuildKit supports the [creation of SLSA Provenance](./slsa-provenance.md) for builds that +it runs. + +The provenance format generated by BuildKit is defined by the +[SLSA Provenance format](https://slsa.dev/provenance/v0.2). + +This page describes how BuildKit populate each field, and whether the field gets +included when you generate attestations `mode=min` and `mode=max`. + +## `builder.id` [(SLSA)](https://slsa.dev/provenance/v0.2#builder.id) + +Included with `mode=min` and `mode=max`. + +The `builder.id` field is set to the URL of the build, if available. + +```json + "builder": { + "id": "https://github.com/docker/buildx/actions/runs/3709599520" + }, +``` + +This value can be set using the `builder-id` attestation parameter. + +## `buildType` [(SLSA)](https://slsa.dev/provenance/v0.2#buildType) + +Included with `mode=min` and `mode=max`. + +The `buildType` field is set to `https://mobyproject.org/buildkit@v1` can be +used to determine the structure of the provenance content. + +```json + "buildType": "https://mobyproject.org/buildkit@v1", +``` + +## `invocation.configSource` [(SLSA)](https://slsa.dev/provenance/v0.2#invocation.configSource) + +Included with `mode=min` and `mode=max`. + +Describes the config that initialized the build. + +```json + "invocation": { + "configSource": { + "uri": "https://github.com/moby/buildkit.git#refs/tags/v0.11.0-rc3", + "digest": { + "sha1": "4b220de5058abfd01ff619c9d2ff6b09a049bea0" + }, + "entryPoint": "Dockerfile" + }, + ... + }, +``` + +For builds initialized from a remote context, like a Git or HTTP URL, this +object defines the context URL and its immutable digest in the `uri` and `digest` fields. +For builds using a local frontend, such as a Dockerfile, the `entryPoint` field defines the path +for the frontend file that initialized the build (`filename` frontend option). + +## `invocation.parameters` [(SLSA)](https://slsa.dev/provenance/v0.2#invocation.parameters) + +Partially included with `mode=min`. + +Describes build inputs passed to the build. + +```json + "invocation": { + "parameters": { + "frontend": "gateway.v0", + "args": { + "build-arg:BUILDKIT_CONTEXT_KEEP_GIT_DIR": "1", + "label:FOO": "bar", + "source": "docker/dockerfile-upstream:master", + "target": "release" + }, + "secrets": [ + { + "id": "GIT_AUTH_HEADER", + "optional": true + }, + ... + ], + "ssh": [], + "locals": [] + }, + ... + }, +``` + +The following fields are included with both `mode=min` and `mode=max`: + +- `locals` lists any local sources used in the build, including the build + context and frontend file. +- `frontend` defines type of BuildKit frontend used for the build. Currently, + this can be `dockerfile.v0` or `gateway.v0`. +- `args` defines the build arguments passed to the BuildKit frontend. + + The keys inside the `args` object reflect the options as BuildKit receives + them. For example, `build-arg` and `label` prefixes are used for build + arguments and labels, and `target` key defines the target stage that was + built. The `source` key defines the source image for the Gateway frontend, if + used. + +The following fields are only included with `mode=max`: + +- `secrets` defines secrets used during the build. Note that actual secret + values are not included. +- `ssh` defines the ssh forwards used during the build. + +## `invocation.environment` [(SLSA)](https://slsa.dev/provenance/v0.2#invocation.environment) + +Included with `mode=min` and `mode=max`. + +```json + "invocation": { + "environment": { + "platform": "linux/amd64" + }, + ... + }, +``` + +The only value BuildKit currently sets is the `platform` of the current build +machine. Note that this is not necessarily the platform of the build result that +can be determined from the `in-toto` subject field. + +## `materials` [(SLSA)](https://slsa.dev/provenance/v0.2#materials) + +Included with `mode=min` and `mode=max`. + +Defines all the external artifacts that were part of the build. The value +depends on the type of artifact: + +- The URL of Git repositories containing source code for the image +- HTTP URLs if you are building from a remote tarball, or that was included + using an `ADD` command in Dockerfile +- Any Docker images used during the build + +The URLs to the Docker images will be in +[Package URL](https://github.com/package-url/purl-spec) format. + +All the build materials will include the immutable checksum of the artifact. +When building from a mutable tag, you can use the digest information to +determine if the artifact has been updated compared to when the build ran. + +```json + "materials": [ + { + "uri": "pkg:docker/alpine@3.17?platform=linux%2Famd64", + "digest": { + "sha256": "8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4" + } + }, + { + "uri": "https://github.com/moby/buildkit.git#refs/tags/v0.11.0", + "digest": { + "sha1": "4b220de5058abfd01ff619c9d2ff6b09a049bea0" + } + }, + ... + ], +``` + +## `buildConfig` [(SLSA)](https://slsa.dev/provenance/v0.2#buildConfig) + +Only included with `mode=max`. + +Defines the build steps performed during the build. + +BuildKit internally uses LLB definition to execute the build steps. The LLB +definition of the build steps is defined in `buildConfig.llbDefinition` field. + +Each LLB step is the JSON definition of the +[LLB ProtoBuf API](https://github.com/moby/buildkit/blob/v0.10.0/solver/pb/ops.proto). +The dependencies for a vertex in the LLB graph can be found in the `inputs` +field for every step. + +```json + "buildConfig": { + "llbDefinition": [ + { + "id": "step0", + "op": { + "Op": { + "exec": { + "meta": { + "args": [ + "/bin/sh", + "-c", + "go build ." + ], + "env": [ + "PATH=/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "GOPATH=/go", + "GOFLAGS=-mod=vendor", + ], + "cwd": "/src", + }, + "mounts": [...] + } + }, + "platform": {...}, + }, + "inputs": [ + "step8:0", + "step2:0", + ] + }, + ... + ] + }, +``` + +## `metadata.buildInvocationId` [(SLSA)](https://slsa.dev/provenance/v0.2#metadata.buildIncocationId) + +Included with `mode=min` and `mode=max`. + +Unique identifier for the build invocation. When building a multi-platform image +with a single build request, this value will be the shared by all the platform +versions of the image. + +```json + "metadata": { + "buildInvocationID": "rpv7a389uzil5lqmrgwhijwjz", + ... + }, +``` + +## `metadata.buildStartedOn` [(SLSA)](https://slsa.dev/provenance/v0.2#metadata.buildStartedOn) + +Included with `mode=min` and `mode=max`. + +Timestamp when the build started. + +```json + "metadata": { + "buildStartedOn": "2021-11-17T15:00:00Z", + ... + }, +``` + +## `metadata.buildFinishedOn` [(SLSA)](https://slsa.dev/provenance/v0.2#metadata.buildFinishedOn) + +Included with `mode=min` and `mode=max`. + +Timestamp when the build finished. + +```json + "metadata": { + "buildFinishedOn": "2021-11-17T15:01:00Z", + ... + }, +``` + +## `metadata.completeness` [(SLSA)](https://slsa.dev/provenance/v0.2#metadata.completeness) + +Included with `mode=min` and `mode=max`. + +Defines if the provenance information is complete. + +`completeness.parameters` is true if all the build arguments are included in the +`invocation.parameters` field. When building with `min` mode, the build +arguments are not included in the provenance information and parameters are not +complete. Parameters are also not complete on direct LLB builds that did not use +a frontend. + +`completeness.environment` is always true for BuildKit builds. + +`completeness.materials` is true if `materials` field includes all the +dependencies of the build. When building from un-tracked source in a local +directory, the materials are not complete, while when building from a remote Git +repository all materials can be tracked by BuildKit and `completeness.materials` +is true. + +```json + "metadata": { + "completeness": { + "parameters": true, + "environment": true, + "materials": true + }, + ... + }, +``` + +## `metadata.reproducible` [(SLSA)](https://slsa.dev/provenance/v0.2#metadata.reproducible) + +Defines if the build result is supposed to be byte-by-byte reproducible. This +value can be set by the user with the `reproducible=true` attestation parameter. + +```json + "metadata": { + "reproducible": false, + ... + }, +``` + +## `metadata.https://mobyproject.org/buildkit@v1#hermetic` + +Included with `mode=min` and `mode=max`. + +This extension field is set to true if the build was hermetic and did not access +the network. In Dockerfiles, a build is hermetic if it does not use `RUN` +commands or disables network with `--network=none` flag. + +```json + "metadata": { + "https://mobyproject.org/buildkit@v1#hermetic": true, + ... + }, +``` + +## `metadata.https://mobyproject.org/buildkit@v1#metadata` + +Partially included with `mode=min`. + +This extension field defines BuildKit-specific additional metadata that is not +part of the SLSA provenance spec. + +```json + "metadata": { + "https://mobyproject.org/buildkit@v1#metadata": { + "source": {...}, + "layers": {...}, + "vcs": {...}, + }, + ... + }, +``` + +### `source` + +Only included with `mode=max`. + +Defines a source mapping of LLB build steps, defined in the +`buildConfig.llbDefinition` field, to their original source code (for example, +Dockerfile commands). The `source.locations` field contains the ranges of all +the Dockerfile commands ran in an LLB step. `source.infos` array contains the +source code itself. This mapping is present if the BuildKit frontend provided it +when creating the LLB definition. + +### `layers` + +Only included with `mode=max`. + +Defines the layer mapping of LLB build step mounts defined in +`buildConfig.llbDefinition` to the OCI descriptors of equivalent layers. This +mapping is present if the layer data was available, usually when attestation is +for an image or if the build step pulled in image data as part of the build. + +### `vcs` + +Included with `mode=min` and `mode=max`. + +Defines optional metadata for the version control system used for the build. If +a build uses a remote context from Git repository, BuildKit extracts the details +of the version control system automatically and displays it in the +`invocation.configSource` field. But if the build uses a source from a local +directory, the VCS information is lost even if the directory contained a Git +repository. In this case, the build client can send additional `vcs:source` and +`vcs:revision` build options and BuildKit will add them to the provenance +attestations as extra metadata. Note that, contrary to the +`invocation.configSource` field, BuildKit doesn't verify the `vcs` values, and +as such they can't be trusted and should only be used as a metadata hint. + +## Output + +To inspect the provenance that was generated and attached to a container image, +you can use the `docker buildx imagetools` command to inspect the image in a +registry. Inspecting the attestation displays the format described in the +[attestation storage specification](./attestation-storage.md). + +For example, inspecting a simple Docker image based on `alpine:latest` results +in a provenance attestation similar to the following, for a `mode=min` build: + +```json +{ + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", + "subject": [ + { + "name": "pkg:docker//@?platform=", + "digest": { + "sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862" + } + } + ], + "predicate": { + "builder": { + "id": "" + }, + "buildType": "https://mobyproject.org/buildkit@v1", + "materials": [ + { + "uri": "pkg:docker/alpine@latest?platform=linux%2Famd64", + "digest": { + "sha256": "8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4" + } + } + ], + "invocation": { + "configSource": { + "entryPoint": "Dockerfile" + }, + "parameters": { + "frontend": "dockerfile.v0", + "args": {}, + "locals": [ + { + "name": "context" + }, + { + "name": "dockerfile" + } + ] + }, + "environment": { + "platform": "linux/amd64" + } + }, + "metadata": { + "buildInvocationID": "yirbp1aosi1vqjmi3z6bc75nb", + "buildStartedOn": "2022-12-08T11:48:59.466513707Z", + "buildFinishedOn": "2022-12-08T11:49:01.256820297Z", + "reproducible": false, + "completeness": { + "parameters": true, + "environment": true, + "materials": false + }, + "https://mobyproject.org/buildkit@v1#metadata": {} + } + } +} +``` + +For a similar build, but with `mode=max`: + +```json +{ + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.2", + "subject": [ + { + "name": "pkg:docker//@?platform=", + "digest": { + "sha256": "e8275b2b76280af67e26f068e5d585eb905f8dfd2f1918b3229db98133cb4862" + } + } + ], + "predicate": { + "builder": { + "id": "" + }, + "buildType": "https://mobyproject.org/buildkit@v1", + "materials": [ + { + "uri": "pkg:docker/alpine@latest?platform=linux%2Famd64", + "digest": { + "sha256": "8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4" + } + } + ], + "invocation": { + "configSource": { + "entryPoint": "Dockerfile" + }, + "parameters": { + "frontend": "dockerfile.v0", + "args": {}, + "locals": [ + { + "name": "context" + }, + { + "name": "dockerfile" + } + ] + }, + "environment": { + "platform": "linux/amd64" + } + }, + "buildConfig": { + "llbDefinition": [ + { + "id": "step0", + "op": { + "Op": { + "source": { + "identifier": "docker-image://docker.io/library/alpine:latest@sha256:8914eb54f968791faf6a8638949e480fef81e697984fba772b3976835194c6d4" + } + }, + "platform": { + "Architecture": "amd64", + "OS": "linux" + }, + "constraints": {} + } + }, + { + "id": "step1", + "op": { + "Op": null + }, + "inputs": ["step0:0"] + } + ] + }, + "metadata": { + "buildInvocationID": "46ue2x93k3xj5l463dektwldw", + "buildStartedOn": "2022-12-08T11:50:54.953375437Z", + "buildFinishedOn": "2022-12-08T11:50:55.447841328Z", + "reproducible": false, + "completeness": { + "parameters": true, + "environment": true, + "materials": false + }, + "https://mobyproject.org/buildkit@v1#metadata": { + "source": { + "locations": { + "step0": { + "locations": [ + { + "ranges": [ + { + "start": { + "line": 1 + }, + "end": { + "line": 1 + } + } + ] + } + ] + } + }, + "infos": [ + { + "filename": "Dockerfile", + "data": "RlJPTSBhbHBpbmU6bGF0ZXN0Cg==", + "llbDefinition": [ + { + "id": "step0", + "op": { + "Op": { + "source": { + "identifier": "local://dockerfile", + "attrs": { + "local.differ": "none", + "local.followpaths": "[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]", + "local.session": "q2jnwdkas0i0iu4knchd92jaz", + "local.sharedkeyhint": "dockerfile" + } + } + }, + "constraints": {} + } + }, + { + "id": "step1", + "op": { + "Op": null + }, + "inputs": ["step0:0"] + } + ] + } + ] + }, + "layers": { + "step0:0": [ + [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:c158987b05517b6f2c5913f3acef1f2182a32345a304fe357e3ace5fadcad715", + "size": 3370706 + } + ] + ] + } + } + } + } +} +``` diff --git a/docs/slsa-provenance.md b/docs/slsa-provenance.md new file mode 100644 index 000000000000..a1437e717027 --- /dev/null +++ b/docs/slsa-provenance.md @@ -0,0 +1,99 @@ +# SLSA provenance + +BuildKit supports automatic creation of provenance attestations for the build +process. Provenance attestations record information describing how a build was +created, and is important for tracking the security of your software artifacts. + +Provenance attestations created by BuildKit include details such as: + +- Build parameters and environment. +- Build timestamps. +- Version control metadata for your build sources. +- Build dependencies with their immutable checksums. For example, base images or external URLs used by the build. +- Descriptions of all build steps, with their source and layer mappings. + +Provenance generated by BuildKit is wrapped inside [in-toto attestations](https://github.com/in-toto/attestation) +in the [SLSA Provenance format](https://slsa.dev/provenance/v0.2). + +For more information about how the attestation fields get generated, see [SLSA definitions](./slsa-definitions.md). + +## Build with provenance attestations + +To build an image with provenance attestations using `buildctl`, use the `attest:provenance` option: + +```bash +buildctl build \ + --frontend=dockerfile.v0 \ + --local context=. \ + --local dockerfile=. \ + --opt attest:provenance= +``` + +You can also customize the attestations using parameters: + +```bash +buildctl build \ + --frontend=dockerfile.v0 \ + --local context=. \ + --local dockerfile=. \ + --opt attest:provenance=mode=min,inline-only=true +``` + +All BuildKit exporters support attaching attestations to build results. +When the final output format is a container image (`image` or `oci` exporter), provenance is attached +to the image using the format described in the [attestation storage specification](./attestation-storage.md). +When creating a multi-platform image, each platform version of the image gets its own provenance. + +If you use the `local` or `tar` exporter, the provenance will be written to a file named `provenance.json` +and exported with your build result, in the root directory. + +## Parameters + +| Parameter | Type | Default | Description | +| -------------- | -------------- | ---------------- | ----------------------------------------------------------------------------------------------------------- | +| `mode` | `min`,`max` | `max` | Configures the amount of provenance to be generated. See [mode](#mode) | +| `builder-id` | String | | Explicitly set SLSA [`builder.id`](https://slsa.dev/provenance/v0.2#builder.id) field | +| `filename` | String | `provenance.json` | Set filename for provenance attestation when exported with `local` or `tar` exporter | +| `reproducible` | `true`,`false` | `false` | Explicitly set SLSA [`metadata.reproducible`](https://slsa.dev/provenance/v0.2#metadata.reproducible) field | +| `inline-only` | `true`,`false` | `false` | Only embed provenance into exporters that support inline content. See [inline-only](#inline-only) | + +### `mode` + +Provenance can be generated in one of two modes: `min` or `max`. By default, +when provenance is enabled, the `mode` parameter will be set to `max`. + +In `min` mode, BuildKit generates only the bare minimum amount of provenance, +including: + +- Build timestamps +- The frontend used +- The build materials + +However, the values of build arguments, the identities of secrets, and rich +layer metadata will not be included. `mode=min` should be safe to set on all +builds, as it does not leak information from any part of the build environment. + +In `max` mode, BuildKit generates all of the above, as well as: + +- The source Dockerfile, and rich layer metadata with sourcemaps to connect the + source with the build result +- The values of passed build arguments +- Metadata about secrets and ssh mounts + +Wherever possible, you should prefer `mode=max` as it contains significantly +more detailed information for analysis. However, on some builds it may not be +appropriate, as it includes the values of various build arguments and metadata +about secrets - these builds should be refactored to prefer passing hidden +values through secrets wherever possible to prevent unnecessary information +leakage. + +### `inline-only` + +By default, provenance is by included in all exporters that support +attestations. The `inline-only` parameter allows configuring this behavior, to +only include the provenance results in exporters that support inline content, +specifically only exporters that produce container images. + +Since other exporters produce attestations into separate files, in their +filesystems, you may not want to include the provenance in these cases. +