Generate and validate build provenance

This page provides instructions on how to generate build provenance, view the output, and validate it.

Build provenance is a collection of verifiable data about a build. Provenance metadata includes details such as the digests of the built images, the input source locations, the build arguments, and the build duration. You can use this information to ensure that the built artifacts you are using are accurate and reliable, created by trusted sources and builders.

Cloud Build supports the generation of build provenance that meets Supply-chain Levels for Software Artifacts (SLSA) level 3 assurance based on the specifications for SLSA version 0.1 and 1.0.

As part of support for the SLSA v1.0 spec, Cloud Build provides buildType details in build provenance. You can use the buildType schema to understand the parameterized template used for the build process, including the values that Cloud Build records, and the source of those values. For more information, see Cloud Build buildType v1.

Limitations

  • Cloud Build only generates build provenance for artifacts stored in Artifact Registry.
  • To get both SLSA v1.0 and v0.1 provenance, you must build using triggers. If you start a build manually, by using the gcloud CLI, Cloud Build provides only SLSA v0.1 provenance.

Before you begin

  1. Enable the Cloud Build, Container Analysis, and Artifact Registry APIs.

    Enable the APIs

  2. To use the command-line examples in this guide, install and configure the Google Cloud SDK.

  3. Have your source code handy.

  4. Have a repository in Artifact Registry.

Generate build provenance

The following instructions explain how to generate build provenance for container images you store in Artifact Registry:

  1. In your build config file, add the images field to configure Cloud Build to store your built images in Artifact Registry after your build completes.

    Cloud Build can't generate provenance if you push your image to Artifact Registry using an explicit docker push step.

    The following snippet shows a build config to build a container image and store the image in a Docker repository in Artifact Registry:

    YAML

      steps:
      - name: 'gcr.io/cloud-builders/docker'
        args: [ 'build', '-t', 'LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE', '.' ]
      images: ['LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE']
    

    Where:

    • LOCATION: the regional or multi-regional location for your repository.
    • PROJECT_ID: your Google Cloud project ID.
    • REPOSITORY: the name of your Artifact Registry repository.
    • IMAGE: the name of your your container image.

    JSON

      {
      "steps": [
          {
              "name": "gcr.io/cloud-builders/docker",
              "args": [
                  "build",
                  "-t",
                  "LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE",
                  "."
              ]
          }
      ],
      "images": [
          "LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE"
      ]
      }
    

    Where:

    • LOCATION: the regional or multi-regional location for your repository.
    • PROJECT_ID: your Google Cloud project ID.
    • REPOSITORY: the name of your Artifact Registry repository.
    • IMAGE: the name of your your container image.
  2. In the options section of your build config, add the requestedVerifyOption option and set to the value VERIFIED.

    This setting enables provenance generation and configures Cloud Build to verify that provenance metadata is present. Builds will only be marked successful if provenance is generated.

    YAML

    options:
      requestedVerifyOption: VERIFIED
    

    JSON

    {
        "options": {
            "requestedVerifyOption": "VERIFIED"
        }
    }
    
  3. Start your build.

View build provenance

This section explains how to view the build provenance metadata created by Cloud Build. You can fetch this information for auditing purposes.

You can access build provenance metadata for containers by using the Security insights side panel within the Google Cloud console, or by using the gcloud CLI.

console

The Security insights side panel provides a high-level overview of security information for artifacts stored in Artifact Registry.

To view the Security insights panel:

  1. Open the Build History page in the Google Cloud console:

    Open the Build History page

  2. Select your project and click Open.

  3. In the Region drop-down menu, select the region in which you ran your build.

  4. In the table with the builds, locate the row with the build for which you want to view security insights.

  5. Under the Security insights column, click View.

    This displays the Security insights panel for the selected artifact.

    The Build card displays provenance details and a link. You can view the provenance snippet by clicking on the link icon.

To learn more about the side panel and how you can use Cloud Build to help protect your software supply chain, see View build security insights.

gcloud CLI

To view provenance metadata for container images, run the following command:

  gcloud artifacts docker images describe \
  LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH \
  --show-provenance --format=FORMAT

Replace the following:

  • LOCATION: the regional or multi-regional location for your repository.
  • PROJECT_ID: your Google Cloud project ID.
  • REPOSITORY: the name of your Artifact Registry repository.
  • IMAGE: the name of your your container image.
  • HASH: The sha256 hash value of the image. You can find this in the output of your build.
  • FORMAT: An optional setting where you can specify an output format.

Example output

The build provenance resembles the following:

      image_summary:
      digest: sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
      fully_qualified_digest: us-central1-docker.pkg.dev/my-project/my-repo/my-image@sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
      registry: us-central1-docker.pkg.dev
      repository: my-repo
      slsa_build_level: 0
    provenance_summary:
      provenance:
      - build:
          inTotoSlsaProvenanceV1:
            _type: https://in-toto.io/Statement/v1
            predicate:
              buildDefinition:
                buildType: https://cloud.google.com/build/gcb-buildtypes/google-worker/v1
                externalParameters:
                  buildConfigSource:
                    path: cloudbuild.yaml
                    ref: refs/heads/main
                    repository: git+https://github.com/my-username/my-git-repo
                  substitutions: {}
                internalParameters:
                  systemSubstitutions:
                    BRANCH_NAME: main
                    BUILD_ID: e73ca1d4-ec4a-4ea6-acdd-ac8bb16dcc79
                    COMMIT_SHA: 525c52c501739e6df0609ed1f944c1bfd83224e7
                    LOCATION: us-west1
                    PROJECT_NUMBER: '265426041527'
                    REF_NAME: main
                    REPO_FULL_NAME: my-username/my-git-repo
                    REPO_NAME: my-git-repo
                    REVISION_ID: 525c52c501739e6df0609ed1f944c1bfd83224e7
                    SHORT_SHA: 525c52c
                    TRIGGER_BUILD_CONFIG_PATH: cloudbuild.yaml
                    TRIGGER_NAME: github-trigger-staging
                  triggerUri: projects/265426041527/locations/us-west1/triggers/a0d239a4-635e-4bd3-982b-d8b72d0b4bab
                resolvedDependencies:
                - digest:
                    gitCommit: 525c52c501739e6df0609ed1f944c1bfd83224e7
                  uri: git+https://github.com/my-username/my-git-repo@refs/heads/main
                - digest:
                    sha256: 154fcd4d2d65c6a35b06b98053a0829c581e223d530be5719326f5d85d680e8d
                  uri: gcr.io/cloud-builders/docker@sha256:154fcd4d2d65c6a35b06b98053a0829c581e223d530be5719326f5d85d680e8d
              runDetails:
                builder:
                  id: https://cloudbuild.googleapis.com/GoogleHostedWorker
                byproducts:
                - {}
                metadata:
                  finishedOn: '2023-08-01T19:57:10.734471Z'
                  invocationId: https://cloudbuild.googleapis.com/v1/projects/my-project/locations/us-west1/builds/e73ca1d4-ec4a-4ea6-acdd-ac8bb16dcc79
                  startedOn: '2023-08-01T19:56:57.451553160Z'
            predicateType: https://slsa.dev/provenance/v1
            subject:
            - digest:
                sha256: 7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
              name: https://us-central1-docker.pkg.dev/my-project/my-repo/my-image
            - digest:
                sha256: 7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
              name: https://us-central1-docker.pkg.dev/my-project/my-repo/my-image:latest
        createTime: '2023-08-01T19:57:14.810489Z'
        envelope:
          payload:
          eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdMWQ0LWVjNGEtNGVhNi1hY2RkLWFjOGJiMTZkY2M3OSIsICJzdGFydGVkT24iOiIyMDIzLTA4LTAxVDE5OjU2OjU3LjQ1MTU1MzE2MFoiLCAiZmluaXNoZWRPbiI6IjIwMjMtMDgtMDFUMTk6NTc6MTAuNzM0NDcxWiJ9LCAiYnlwcm9kdWN0cyI6W3t9XX19fQ==...
          payloadType: application/vnd.in-toto+json
          signatures:
          - keyid: projects/verified-builder/locations/global/keyRings/attestor/cryptoKeys/google-hosted-worker/cryptoKeyVersions/1
            sig: MEUCIQCss8UlQL2feFePRJuKTE8VA73f85iqj4OJ9SvVPqTNwAIgYyuyuIrl1PxQC5B109thO24Y6NA4bTa0PJY34EHRSVE=
        kind: BUILD
        name: projects/my-project/occurrences/71787589-c6a6-4d6a-a030-9fd041e40468
        noteName: projects/argo-qa/notes/intoto_slsa_v1_e73ca1d4-ec4a-4ea6-acdd-ac8bb16dcc79
        resourceUri: https://us-central1-docker.pkg.dev/my-project/my-repo/my-image@sha256:7e9b6e7ba2842c91cf49f3e214d04a7a496f8214356f41d81a6e6dcad11f11e3
        updateTime: '2023-08-01T19:57:14.810489Z'
    

A few important things to notice in this example:

  • Source: The build was triggered from a GitHub repository.

  • Object reference: Fields named digest and fileHash refer to the same object. The digest field included in the example output is encoded in base 16 (hex-encoded). If you're using SLSA version 0.1 provenance, your output uses the fileHash field encoded in base 64.

  • Signatures: If you're using SLSA version 0.1 provenance, your output contains two signatures on the envelope field. The first signature, which has the key name provenanceSigner, uses a DSSE-conformant signature (formatted with Pre-Authentication Encoding (PAE)), which can be verified in Binary Authorization policies. We recommend that you use this signature in new usages of this provenance. The second signature, which has the key name builtByGCB, is provided for legacy usage.

  • Service accounts: The signatures that are automatically included in Cloud Build provenance help you verify the build service that executed a build. You can also configure Cloud Build to record verifiable metadata about the service account used to initiate a build. For more information, see sign container images with cosign.

  • Payload: The example provenance displayed on this page is shortened for readability. Actual output will be longer, as the payload is a base-64 encoded version of the all of the provenance metadata.

View provenance for non-container artifacts

Cloud Build generates SLSA provenance metadata for standalone Java (Maven), Python, and Node.js (npm) applications when you upload your build artifacts to Artifact Registry. You can retrieve the provenance metadata by making a direct API call.

  1. To generate the provenance metadata for your artifacts, run a build with Cloud Build. Use one of the following guides:

    When your build completes, note the BuildID.

  2. Retrieve provenance metadata by running the following API call in your terminal, where PROJECT_ID is the ID associated with your Google Cloud project:

    alias gcurl='curl -H"Authorization: Bearer $(gcloud auth print-access-token)"'
        gcurl 'https://containeranalysis.googleapis.com/v1/projects/PROJECT_ID/occurrences'
    

    You must use an API call to access the provenance metadata for this type of artifact. Provenance metadata for non-container artifacts isn't displayed in the Google Cloud console or accessible through gcloud CLI.

  3. In the occurrences for your project, search by BuildID to find the provenance information associated with a build artifact.

Validate provenance

This section explains how to validate build provenance for container images.

Validating build provenance helps you to:

  • confirm that build artifacts are being generated from trusted sources and builders
  • ensure that provenance metadata describing your build process is complete and authentic

For more information, see Safeguard builds.

Validate provenance using the SLSA verifier

The SLSA verifier is an open source CLI tool for validating build integrity based on the SLSA specifications.

If the verifier finds issues, it returns detailed error messages to help you update your build process and mitigate risks.

To use the SLSA verifier:

  1. Install version 2.1 or higher from the slsa-verifier repository:

    go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@VERSION
    
  2. In your CLI, set a variable for your image identifier:

    export IMAGE=LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH
    

    Replace the placeholder values in the command with the following:

    • LOCATION: Regional or multi-regional location.
    • PROJECT_ID: Google Cloud project ID.
    • REPOSITORY: Name of the repository.
    • IMAGE: Image name.
    • HASH: The sha256 hash value of the image. You can find this in the output of your build.
  3. Authorize the gcloud CLI so that the SLSA verifier can access your provenance data:

    gcloud auth configure-docker LOCATION-docker.pkg.dev
    
  4. Retrieve the provenance for your image and store it as JSON:

    gcloud artifacts docker images describe $IMAGE --format json --show-provenance > provenance.json
    
  5. Verify the provenance:

    slsa-verifier verify-image "$IMAGE" \
    --provenance-path provenance.json \
    --source-uri SOURCE \
    --builder-id=BUILDER_ID
    

    Where:

    • SOURCE is the source repository URI for your image, for example github.com/my-repo/my-application.
    • BUILDER_ID the unique ID for the builder, for example https://cloudbuild.googleapis.com/GoogleHostedWorker

    If you want to print the validated provenance for use in a policy engine, use the previous command with the --print-provenance flag.

    The output is similar to the following: PASSED: Verified SLSA provenance or FAILED: SLSA verification failed: <error details>.

For more information on optional flags, see options.

Validate provenance metadata with the gcloud CLI

If you want to verify that the build provenance metadata has not been tampered with, you can validate the provenance by performing the following steps:

  1. Create a new directory and go to that directory.

    mkdir provenance && cd provenance
    
  2. Using the information from the keyid field, get the public key.

    gcloud kms keys versions get-public-key 1 --location global --keyring attestor \
      --key builtByGCB --project verified-builder --output-file my-key.pub
    
  3. The payload contains the JSON representation of the provenance, encoded in base64url. Decode the data and store it in a file.

    gcloud artifacts docker images describe \
    LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
      --format=json | jq -r '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v0.1") | .envelope.payload' | tr '\-_' '+/' | base64 -d > provenance.json
    

    Both SLSA version 0.1 and 1.0 provenance types are stored when available. If you want to filter for version 1.0, change the predicateType to use https://slsa.dev/provenance/v1. For example:

    gcloud artifacts docker images describe \
    LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
      --format=json | jq -r '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v1") | .envelope.payload' | tr '\-_' '+/' | base64 -d > provenance.json
    
  4. The envelope also contains the signature over the provenance. Decode the data and store it in a file.

      gcloud artifacts docker images describe LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
      --format=json | '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v0.1") | .envelope.signatures[0].sig' | tr '\-_' '+/' | base64 -d > signature.bin
    

    If you want to filter for version 1.0, change the predicateType to use https://slsa.dev/provenance/v1. For example:

    gcloud artifacts docker images describe LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE@sha256:HASH --show-provenance \
    --format=json | jq -r '.provenance_summary.provenance[] | select(.build.intotoStatement.predicateType == "https://slsa.dev/provenance/v1") | .envelope.signatures[0].sig' | tr '\-_' '+/' | base64 -d > signature.bin
    
  5. The command above references the first provenance signature (.provenance_summary.provenance[0].envelope.signatures[0]) which is signed by the provenanceSigner key. The payload is signed over the PAE-formatted envelope. In order to verify it, run this command to transform the provenance into the expected PAE format of "DSSEv1" + SP + LEN(type) + SP + type + SP + LEN(body) + SP + body.

    echo -n "DSSEv1 28 application/vnd.in-toto+json $(cat provenance.json | wc -c) $(cat provenance.json)" > provenance.json
    
  6. Validate the signature.

    openssl dgst -sha256 -verify my-key.pub -signature signature.bin provenance.json
    

    After a successful validation, the output is Verified OK.

What's next