Creating attestations

This page explains how to create an attestation in Binary Authorization from the command line.

The instructions on this page outline the steps that a signer needs to perform in order to authorize a container image for deployment. In a real-world scenario, you incorporate these steps into a script or automation that can be triggered by machine process or a human user, rather than enter them manually at the command line.

Overview

An attestation is a digitally signed document, made by a signer, that certifies that a required process in your pipeline was completed and that the resulting container image is authorized for deployment in GKE. The attestation itself contains the full path to the version of the container image as stored in your container image registry, as well as a signature created by signing the globally unique digest that identifies a specific container image build.

You can create an attestation using either a PKIX (recommended) or PGP signature. This guide uses the Public-Key Infrastructure (X.509) (PKIX) format for cryptographic keys and uses the recommended Elliptic Curve Digital Signing Algorithm (ECDSA) to sign and create the attestation. You can also use RSA or PGP keys for signing. See Key purposes and algorithms for more information on signing algorithms.

Set the default project

If you have not already set your default Google Cloud project:

PROJECT_ID=PROJECT_ID
gcloud config set project ${PROJECT_ID}

where PROJECT_ID is the name of your project.

Set up the environment

  1. Set up environment variables to store your project IDs:

    ATTESTOR_PROJECT_ID=ATTESTOR_PROJECT_ID
    ATTESTATION_PROJECT_ID=ATTESTATION_PROJECT_ID
    

    where:

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

    If your attestor and attestation projects are the same project, use the same project ID for both variables.

  2. Set up environment variables to store the name of the attestor that will verify the attestation and the registry path to the image you want to deploy:

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

    where:

    • ATTESTOR_NAME is the name of the attestor (for example, build-secure or prod-qa)
    • IMAGE_PATH is the path in Container Registry to the image you want to deploy (for example, gcr.io/example-project/quickstart-image)
    • PUBLIC_KEY_ID is the ID associated with the public key from the key pair you generated. The public key itself is stored in the attestor.
    • PRIVATE_KEY_FILE is the file containing the private key from the key pair you generated. The attestation payload is signed with this key.
    • IMAGE_DIGEST is the SHA-256 digest of the image manifest (for example, sha256:c62ead5b8c15c231f9e786250b07909daf6c266d0fcddd93fea882eb722c3be4). For information on getting the image digest, see Listing the versions of an image in Container Registry

Each container image stored in Container Registry or another registry has a unique path to its location, as well as a SHA-256 digest that uniquely identifies its version. Attestations reference the full image path and digest, which allows you to authorize specific versions of an image.

The following is an example of a full registry path:

gcr.io/example-project/quickstart-image@sha256:bedb3feb23e81d162e33976fd7b245adff00379f4755c0213e84405e5b1e0988

Create an attestation with a PKIX signature using a locally stored key

For attestations signed with a PKIX public key, do the following:

  1. Create an attestation payload to send to Binary Authorization that references the registry path
  2. Sign the payload and generate a PKIX signature file
  3. Get the public key ID
  4. Create the attestation with the signature file and public key ID

Create an attestation payload

The attestation payload is a JSON-formatted file that references the location of the container image.

To create the payload file:

gcloud

Enter the following:

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

The payload file looks similar to the following:

{
  "critical": {
    "identity": {
      "docker-reference": "gcr.io/google-samples/hello-app"
    },
    "image": {
      "docker-manifest-digest": "sha256:bedb3feb23e81d162e33976fd7b245
adff00379f4755c0213e84405e5b1e0988"
    },
    "type": "Google cloud binauthz container signature"
  }
}

REST

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

Sign the payload and generate a signature file

After you have created the payload file, you must sign it using the private cryptographic key from the key pair you generated earlier. Recall that the corresponding public key was stored in the associated attestor. The public key in that attestor will be used to verify this signature at deploy time.

To review how to create an attestor, see Creating attestors using the CLI or Creating attestors using the Console.

To see an end-to-end example that includes all of these steps, see Getting started using the CLI or Getting started using the Console.

To sign the payload file:

  1. Sign the payload with your local PKIX private key and output a signature file:

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

    The signature file is a digitally signed version of the payload JSON file you created above.

Get the public key ID

You must send a public key ID to Binary Authorization along with the signature file when you create an attestation.

To get the public key ID:

  1. Save the public key ID

    To view your attestor's public key ID after, use gcloud container binauthz attestors describe ${ATTESTOR_NAME}:

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

Create the attestation

To create the attestation:

gcloud

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}"

Note: The key ID can be any string.

REST

  1. 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"
    }
    
  2. In a text editor, 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": "KEY_ID",
             "signature": "$(base64 --wrap=0 /tmp/ec_signature)"
             }
         ]
      }
     }
    EOM
    

    where KEY_ID is the public key ID returned in the previous step.

  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 with a Cloud Key Management Service-based PKIX signature

To create an attestation with a Cloud Key Management Service-based PKIX signature:

  1. Set up environment variables to store information about the projects where your attestations, attestors and Cloud Key Management Service keys are stored, as well as information about your PKIX key pair:

    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
    

    where:

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

    gcloud

    Enter the following at the command line:

    gcloud alpha 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:

      gcloud --project="${KMS_KEY_PROJECT_ID}"  \
          alpha kms asymmetric-sign \
          --location="${KMS_KEY_LOCATION}" \
          --keyring="${KMS_KEYRING_NAME}" \
          --key="${KMS_KEY_NAME}" \
          --version="${KMS_KEY_VERSION}" \
          --digest-algorithm="DIGEST_ALGORITHM" \
          --input-file=/tmp/generated_payload.json \
          --signature-file=/tmp/generated_payload.json.sig
      

      where DIGEST_ALGORITHM is one of sha256, sha384, or sha512. This is the digest algorithm of the key version you are using for signing.

      This commands outputs a file named /tmp/generated_payload.json.sig that contains the digital signature.

    3. 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"
      }
      
    4. In a text editor, 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": "KEY_ID",
                   "signature": "$(base64 --wrap=0 /tmp/generated_payload.json.sig)"
               }
           ]
        }
      
      }
      EOM
      

      where KEY_ID is public key ID returned in the previous step.

    5. 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/"
      

Verify that the attestation was created

To verify that the attestation was created:

gcloud

Enter the following at the command line:

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

REST

Retrieve a list of attestations:

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

What's next