Creating attestations

This page describes the steps to create a Binary Authorization attestation. At deploy time, a single attestation can provide verification for identical images that are stored in multiple different locations or different registries, such as Artifact Registry, Container Registry, or an external container registry.

For an end-to-end tutorial that describes attestation-based enforcement using Binary Authorization, see Getting started using the command-line tool or Getting started using Google Cloud Console.

Before you begin

  1. Enable Binary Authorization.
  2. Create a cluster.
  3. Create a cryptographic key pair and an attestor, either through the command-line tool or the Google Cloud Console.
  4. Configure a rule in the Binary Authorization policy to require attestations by the attestor. You can configure a policy through Google Cloud Console or a command-line tool.

Set the default project

Set your default Google Cloud project ID:

PROJECT_ID=PROJECT_ID
gcloud config set project ${PROJECT_ID}

Replace PROJECT_ID with the name of your project.

Set up the environment

  1. Create environment variables to store your project IDs:

    ATTESTOR_PROJECT_ID=ATTESTOR_PROJECT_ID
    ATTESTATION_PROJECT_ID=ATTESTATION_PROJECT_ID
    

    Replace the following:

    • ATTESTOR_PROJECT_ID: the name of the project where you are storing your attestors
    • ATTESTATION_PROJECT_ID: the name of the project where you are storing your attestations

    If you want your attestation created in the same project as your attestors, use the same project ID for both variables. For an end-to-end tutorial that demonstrates separation of duties with different projects, see Multi-project setup.

  2. Create environment variables:

    ATTESTOR_NAME=ATTESTOR_NAME
    IMAGE_PATH=IMAGE_PATH
    IMAGE_DIGEST=IMAGE_DIGEST
    IMAGE_TO_ATTEST="${IMAGE_PATH}@${IMAGE_DIGEST}"
    

    Replace the following:

    • ATTESTOR_NAME: the name of the attestor, for example, build-secure or prod-qa.
    • IMAGE_PATH: a URI representing an image path. Though the URI must comprise a domain and image name, it does not need to refer to an actual image. Images are not accessed during attestation creation. The following are examples of image paths:

      • us-docker.pkg.dev/google-samples/containers/gke/hello-app
      • gcr.io/example-project/quickstart-image
      • example.com/hello-app.
    • IMAGE_DIGEST: the digest of the image manifest. For example, sha256:37e5287945774f27b418ce567cd77f4bbc9ef44a1bcd1a2312369f31f9cce567 is the image digest associated with the example us-docker.pkg.dev/google-samples/containers/gke/hello-app image path. To learn how to get the digest of an image in Artifact Registry, see Managing images; for an image in Container Registry, see Listing the versions of an image.

Create an attestation

Create an attestation using a locally stored key

To create an attestations signed with a local key, do the following:

  1. Create a signature payload file:

    gcloud

    To create the signature payload file, enter the following command:

    gcloud container binauthz create-signature-payload \
        --artifact-url="${IMAGE_TO_ATTEST}" > /tmp/generated_payload.json
    

    The JSON-formatted payload file looks similar to the following output:

    {
      "critical": {
        "identity": {
          "docker-reference": "us-docker.pkg.dev/google-samples/containers/gke/hello-app"
        },
        "image": {
          "docker-manifest-digest": "sha256:37e5287945774f27b418ce567cd77f4bbc9ef44a1bcd1a2312369f31f9cce567"
        },
        "type": "Google cloud binauthz container signature"
      }
    }
    

    REST

    Create a payload file named /tmp/generated_payload.json using the environment variables you set earlier in this document:

    cat > /tmp/generated_payload.json << EOM
    {
      "critical": {
        "identity": {
          "docker-reference": "${IMAGE_PATH}"
        },
        "image": {
          "${IMAGE_DIGEST}"
        },
        "type": "Google cloud binauthz container signature"
      }
    }
    EOM
    
  2. Sign the payload with your private key to generate a signature file.

    This guide uses the recommended Elliptic Curve Digital Signing Algorithm (ECDSA) algorithm for signing. You can also use the RSA algorithm. For more information on signing algorithms, see Key purposes and algorithms. This guide also uses the Public-Key Infrastructure (X.509) (PKIX) signature format. You can also use PGP.

    PRIVATE_KEY_FILE=PRIVATE_KEY_FILE
    openssl dgst -sha256 -sign ${PRIVATE_KEY_FILE} /tmp/generated_payload.json > /tmp/ec_signature
    

    Replace PRIVATE_KEY_FILE with the path to the private key you generated when you created the attestor.

  3. Get the public key ID.

    You can retrieve the public key ID from the attestor by entering the following command:

    PUBLIC_KEY_ID=$(gcloud container binauthz attestors describe ${ATTESTOR_NAME} \
      --format='value(userOwnedGrafeasNote.publicKeys[0].id)')
    
  4. Create the attestation:

    gcloud

    To create and validate the attestation, enter the following:

    gcloud container binauthz attestations create \
        --project="${ATTESTATION_PROJECT_ID}" \
        --artifact-url="${IMAGE_TO_ATTEST}" \
        --attestor="projects/${ATTESTOR_PROJECT_ID}/attestors/${ATTESTOR_NAME}" \
        --signature-file=/tmp/ec_signature \
        --public-key-id="${PUBLIC_KEY_ID}" \
        --validate
    

    The validate flag checks that the attestation can be verified by the attestor you configured in your policy.

    Note: The key ID can be any string.

    REST

    To create the attestation:

    1. Retrieve the attestor associated with the attestation and extract the stored public key ID:

      curl \
          -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
          -H "x-goog-user-project: ${ATTESTOR_PROJECT_ID}" \
          "https://binaryauthorization.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/attestors/"
      

      Binary Authorization returns a JSON object similar to the following:

      {
        "name": "projects/example-project/attestors/test-attestor",
        "userOwnedGrafeasNote": {
          "noteReference": "projects/example-project/notes/test-attestor",
          "publicKeys": [
            {
              "id": "ni:///sha-256;EwVxs8fNUAHq9FI2AMfh8WNIXVBuuTMeGtPH72U-I70",
              "pkixPublicKey": {
                "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXnpuYEfvLl1kj4fjxViFRwY1a+zC\n5qzlf9LJIK+rnjq42tiKGyyXMbnZKJiYPPdMDGyltnkrABnztg2jJ48aYQ==\n-----END PUBLIC KEY-----\n",
                "signatureAlgorithm": "ECDSA_P256_SHA256"
              }
            }
          ],
          "delegationServiceAccountEmail": "service-363451293945@gcp-sa-binaryauthorization.iam.gserviceaccount.com"
        },
        "updateTime": "2019-06-26T16:58:33.977438Z"
      }
      

      The public key can be found in the id field.

    2. Create a JSON file in /tmp/attestation.json that describes the attestation:

      cat > /tmp/attestation.json << EOM
      {
        "resourceUri": "${IMAGE_TO_ATTEST}",
        "note_name": "${NOTE_URI}",
        "attestation": {
           "serialized_payload": "$(base64 --wrap=0 /tmp/generated_payload.json)",
           "signatures": [
              {
               "public_key_id": "${PUBLIC_KEY_ID}",
               "signature": "$(base64 --wrap=0 /tmp/ec_signature)"
              }
           ]
        }
       }
      EOM
      
    3. Create the attestation:

      curl -X POST \
          -H "Content-Type: application/json" \
          -H "X-Goog-User-Project: ${ATTESTATION_PROJECT_ID}" \
          -H "Authorization: Bearer $(gcloud auth print-access-token)" \
          --data-binary @/tmp/attestation.json \
          "https://containeranalysis.googleapis.com/v1/projects/${ATTESTATION_PROJECT_ID}/occurrences/"
      

Create an attestation using Cloud Key Management Service

To create an attestation with using Cloud Key Management Service:

  1. Create environment variables:

    KMS_KEY_PROJECT_ID=KMS_KEY_PROJECT_ID
    KMS_KEY_LOCATION=KMS_KEY_LOCATION
    KMS_KEYRING_NAME=KMS_KEYRING_NAME
    KMS_KEY_NAME=KMS_KEY_NAME
    KMS_KEY_VERSION=KMS_KEY_VERSION
    

    Replace the following:

    • KMS_KEY_PROJECT_ID: the ID of the project where your Cloud Key Management Service keys are stored
    • KMS_KEY_LOCATION: the location of the key (global is the default)
    • KMS_KEYRING_NAME: the name of the key ring
    • KMS_KEY_NAME: the name of the key
    • KMS_KEY_VERSION: the key version
  2. Sign and create the attestation:

    gcloud

    Enter the following command:

    gcloud beta container binauthz attestations sign-and-create \
        --project="${ATTESTATION_PROJECT_ID}" \
        --artifact-url="${IMAGE_TO_ATTEST}" \
        --attestor="${ATTESTOR_NAME}" \
        --attestor-project="${ATTESTOR_PROJECT_ID}" \
        --keyversion-project="${KMS_KEY_PROJECT_ID}" \
        --keyversion-location="${KMS_KEY_LOCATION}" \
        --keyversion-keyring="${KMS_KEYRING_NAME}" \
        --keyversion-key="${KMS_KEY_NAME}" \
        --keyversion="${KMS_KEY_VERSION}"
    

    REST

    1. Create a payload file named /tmp/generated_payload.json using the environment variables you set above:

      cat > /tmp/generated_payload.json << EOM
      {
        "critical": {
          "identity": {
            "docker-reference": "${IMAGE_PATH}"
          },
          "image": {
            "${IMAGE_DIGEST}"
          },
          "type": "Google cloud binauthz container signature"
        }
      }
      EOM
      
    2. Sign the payload file:

      curl \
        --header "Content-Type: application/json" \
        --header "Authorization: Bearer $(gcloud auth print-access-token)" \
        --header "x-goog-user-project: ${ATTESTOR_PROJECT_ID}" \
        --data '{"digest":  {"DIGEST_ALGORITHM": "'$(openssl dgst -sha256 -binary /tmp/generated_payload.json | openssl base64)'" }}' \
      https://cloudkms.googleapis.com/v1/projects/${KMS_KEY_PROJECT_ID}/locations/${KMS_KEY_LOCATION}/keyRings/${KMS_KEYRING_NAME}/cryptoKeys/${KMS_KEY_NAME}/cryptoKeyVersions/${KMS_KEY_VERSION}:asymmetricSign?alt=json
      

      Replace DIGEST_ALGORITHM with the algorithm to digest the input. Examples in this guide use a sha256 digest. You can use sha256, sha384, or sha512.

      In this example, the output looks similar to the following:

      {
        "signature": "<var>SIGNATURE</var>": "996305066",
        "name": "projects/<var>KMS_KEY_PROJECT_ID</var>/locations/<var>KMS_KEY_LOCATION</var>/keyRings/<var>KMS_KEYRING_NAME</var>/cryptoKeys/<var>KMS_KEY_NAME</var>/cryptoKeyVersions/<var>KMS_KEY_VERSION</var>"
      }
      

      In this output, SIGNATURE is the base64-encoded signature of the payload file.

    3. Store the signature in an environment variable:

      PAYLOAD_SIGNATURE=PAYLOAD_SIGNATURE
      
    4. Retrieve the attestor on whose behalf you are are signing the attestation and extract stored public key ID:

      curl \
          -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
          -H "x-goog-user-project: ${ATTESTOR_PROJECT_ID}" \
          "https://binaryauthorization.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/attestors/"
      

      Binary Authorization returns a JSON object similar to the following:

      {
        "name": "projects/example-project/attestors/test-attestor",
        "userOwnedGrafeasNote": {
          "noteReference": "projects/example-project/notes/test-attestor",
          "publicKeys": [
            {
              "id": "ni:///sha-256;EwVxs8fNUAHq9FI2AMfh8WNIXVBuuTMeGtPH72U-I70",
              "pkixPublicKey": {
                "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEXnpuYEfvLl1kj4fjxViFRwY1a+zC\n5qzlf9LJIK+rnjq42tiKGyyXMbnZKJiYPPdMDGyltnkrABnztg2jJ48aYQ==\n-----END PUBLIC KEY-----\n",
                "signatureAlgorithm": "ECDSA_P256_SHA256"
              }
            }
          ],
          "delegationServiceAccountEmail": "service-363451293945@gcp-sa-binaryauthorization.iam.gserviceaccount.com"
        },
        "updateTime": "2019-06-26T16:58:33.977438Z"
      }
      

      You can find the public key ID in the id field.

    5. Store the public key ID in an environment variable:

      PUBLIC_KEY_ID=PUBLIC_KEY_ID
      
    6. Create a JSON file in /tmp/attestation.json that describes the attestation:

      cat > /tmp/attestation.json << EOM
      {
        "resourceUri": "${IMAGE_TO_ATTEST}",
        "note_name": "${NOTE_URI}",
        "attestation": {
           "serialized_payload": "$(base64 --wrap=0 /tmp/generated_payload.json)",
           "signatures": [
               {
                   "public_key_id": "${PUBLIC_KEY_ID}",
                   "signature": "${$PAYLOAD_SIGNATURE}"
               }
           ]
        }
      }
      EOM
      
    7. Create the attestation:

      curl -X POST \
          -H "Content-Type: application/json" \
          -H "X-Goog-User-Project: ${ATTESTATION_PROJECT_ID}" \
          -H "Authorization: Bearer $(gcloud auth print-access-token)" \
          --data-binary @/tmp/attestation.json \
      "https://containeranalysis.googleapis.com/v1/projects/${ATTESTATION_PROJECT_ID}/occurrences/"
      

You have created the attestation.

Verify that the attestation was created

To verify that the attestation was created, you can list attestations associated with the image.

gcloud

To retrieve a list of attestations, enter the following command:

gcloud container binauthz attestations list\
    --project="${ATTESTATION_PROJECT_ID}"\
    --attestor="projects/${ATTESTOR_PROJECT_ID}/attestors/${ATTESTOR_NAME}"\
    --artifact-url="${IMAGE_TO_ATTEST}"

REST

To request a list of attestations, enter the following command:

curl -X GET \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: ${ATTESTOR_PROJECT_ID}" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
https://containeranalysis.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/notes/${NOTE_ID}/occurrences?filter=resourceUrl%3D%22https%3A%2F%2F$(jq -rn --arg x ${IMAGE_TO_ATTEST} '$x|@uri')%22

If there are many attestations, the response might contain a nextPageToken value. In this case, you can retrieve the next page of results by repeating the request, adding a pageToken query parameter, as follows:

NEXT_PAGE_TOKEN=NEXT_PAGE_TOKEN
curl -X GET \
    -H "Content-Type: application/json" \
    -H "X-Goog-User-Project: ${ATTESTOR_PROJECT_ID}" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
https://containeranalysis.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/notes/${NOTE_ID}/occurrences?filter=resourceUrl%3D%22https%3A%2F%2F$(jq -rn --arg x ${IMAGE_TO_ATTEST} '$x|@uri')%22&pageToken=${NEXT_PAGE_TOKEN}

Replace NEXT_PAGE_TOKEN with the nextPageToken value in the response from the previous request.

When nextPageToken is empty that means that there are no more results.

Delete the attestation

Before you delete the attestation, you should do the following:

  1. Understand the ramifications of deleting it. Deleting an attestation eventually blocks container images associated with the attestation from being deployed.

  2. Stop all running containers that are associated with the attestations you intend to delete.

  3. Delete all copies of the attestations wherever they reside, for example attestations in Artifact Registry and Container Analysis repositories.

  4. Ensure that the affected images are actually blocked from deploying by attempting to re-deploy them.

To delete an attestation, execute the following commands:

  1. List attestations:

    gcloud container binauthz attestations list \
      --attestor-project=${ATTESTOR_PROJECT_ID} \
      --attestor=${ATTESTOR_NAME}
    

    The attestation contains an occurrence ID. The output looks similar to the following:

    projects/ATTESTOR_PROJECT_ID/occurrences/OCCURRENCE_ID
    
  2. Save the occurrence ID.

    Save the occurrence ID of the attestation you want to delete.

    OCCURRENCE_ID=OCCURRENCE_ID
    
  3. Delete the attestation:

    curl -H "Authorization: Bearer $(gcloud auth print-access-token)" -X DELETE \
      https://containeranalysis.googleapis.com/v1beta1/projects/${ATTESTATION_PROJECT_ID}/occurrences/${OCCURRENCE_ID}
    

    Verify that the attestations were deleted by listing attestations again.

What's next