Create attestors using the REST API

This page explains how to create a custom attestor in Binary Authorization by using the REST API.

As an alternative, you can also perform these steps by using the Google Cloud CLI, or the Google Cloud console. This task is part of setting up Binary Authorization.

Cloud Build users: you can instead use the built-by-cloud-build attestor to deploy only images built by Cloud Build.

Overview

An attestor is a Google Cloud resource that Binary Authorization uses to verify an attestation. To learn more about attestations, see the Binary Authorization overview.

Creating an attestor requires you to do the following:

  • Create a note in Artifact Analysis to store trusted metadata used in the attestation process.
  • Set up a Public-Key Infrastructure (X.509) (PKIX) key pair that can be used to verify the identity of the attestor. (Asymmetric key pairs generated by Cloud Key Management Service (Cloud KMS) are in PKIX-compatible format.) You can also use PGP key pairs instead of PKIX keys.
  • Create the attestor itself in Binary Authorization, and associate the note and public key you created.

In a single-project setup, you create your attestor in the same Google Cloud project where you configure your Binary Authorization policy. In a multi-project setup, you most likely have a deployer project where your policy is configured and a separate attestor project where your attestors are stored.

Before you begin

  1. Enable Binary Authorization.

  2. Set up Binary Authorization for your platform.

Set the default project

Set the default Google Cloud project if you have not already done so:

PROJECT_ID=PROJECT_ID
gcloud config set project ${PROJECT_ID}

Set up the environment

Set up environment variables to store your project names and numbers:

DEPLOYER_PROJECT_ID=${PROJECT_ID}
DEPLOYER_PROJECT_NUMBER="$(
    gcloud projects describe "${DEPLOYER_PROJECT_ID}" \
      --format="value(projectNumber)"
)"
ATTESTOR_PROJECT_ID=${PROJECT_ID}
ATTESTOR_PROJECT_NUMBER="$(
    gcloud projects describe "${ATTESTOR_PROJECT_ID}" \
    --format="value(projectNumber)"
)"

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

You must also get the service account names for the projects:

DEPLOYER_SERVICE_ACCOUNT="service-${DEPLOYER_PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"
ATTESTOR_SERVICE_ACCOUNT="service-${ATTESTOR_PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"

Create an Artifact Analysis note

Binary Authorization uses Artifact Analysis to store trusted metadata used in the authorization process. For each attestor you create, you must create one Artifact Analysis note. Each attestation is stored as an occurrence of this note.

To create an Artifact Analysis note:

  1. Set up environment variables to store the note ID and a human-readable description:

    NOTE_ID=NOTE_ID
    NOTE_URI="projects/${ATTESTOR_PROJECT_ID}/notes/${NOTE_ID}"
    DESCRIPTION=DESCRIPTION
    

    Replace the following:

    • NOTE_ID is the internal name of the note in alphanumeric characters with no spaces (for example, test-attestor-note)
    • NOTE_URI is the fully-qualified path to the note resource
    • DESCRIPTION is a human-readable display name for the note (for example, Test Attestor Note)
  2. In a text editor, create a JSON file in /tmp/note_payload.json that describes the Artifact Analysis note:

    cat > /tmp/note_payload.json << EOM
    {
      "name": "${NOTE_URI}",
      "attestation": {
        "hint": {
          "human_readable_name": "${DESCRIPTION}"
        }
      }
    }
    EOM
    
  3. Create the note by sending an HTTP request to the Artifact Analysis REST API:

    curl -X POST \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
        -H "x-goog-user-project: ${ATTESTOR_PROJECT_ID}" \
        --data-binary @/tmp/note_payload.json  \
        "https://containeranalysis.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/notes/?noteId=${NOTE_ID}"
    

To verify that the note was created successfully, run the following command:

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

Set permissions on the note

You must also set permissions on the Artifact Analysis note you created so that it is accessible to the attestor project service account. You do this by updating the IAM policy for the note to assign the containeranalysis.notes.occurrences.viewer role to the account.

To set the permissions:

  1. Generate a JSON file that contains the information needed to set the IAM policy on your note:

    cat > /tmp/iam_request.json << EOM
    {
      'resource': '${NOTE_URI}',
      'policy': {
        'bindings': [
          {
            'role': 'roles/containeranalysis.notes.occurrences.viewer',
            'members': [
              'serviceAccount:${ATTESTOR_SERVICE_ACCOUNT}'
            ]
          }
        ]
      }
    }
    EOM
    
  2. Add the service account and requested access roles to the IAM policy for the note you created:

    curl -X POST  \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        -H "x-goog-user-project: ${ATTESTOR_PROJECT_ID}" \
        --data-binary @/tmp/iam_request.json \
        "https://containeranalysis.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/notes/${NOTE_ID}:setIamPolicy"
    

Set up cryptographic keys

Binary Authorization allows you to use PKIX keys to securely verify the identity of signer that created an attestation. This ensures that only verified parties can authorize a container image. As an alternative to PKIX, you can also use PGP keys.

Create a PKIX key pair

Binary Authorization allows you to use asymmetric PKIX key pairs to verify an attestation. The key pair consists of a private key, which the signer uses to digitally sign attestations, and a public key, which you add to the attestor. Later, the Binary Authorization enforcer uses the public key in the attestor to verify that the attestation was created by the signer.

In this guide, the recommended Elliptic Curve Digital Signature Algorithm (ECDSA) is used to generate a PKIX key pair. You can also use RSA or PGP keys for signing. See Key purposes and algorithms for more information on signing algorithms.

The asymmetric key pairs generated and stored in Cloud KMS are compliant with the PKIX format. To create a Cloud KMS key for use with Binary Authorization, see Creating Asymmetric Keys. Make sure that you choose Asymmetric Sign as the key purpose when you create the key.

PKIX (Cloud KMS)

To create the key pair in Cloud KMS, do the following:

  1. To set up environment variables needed to create the key pair, run the following commands:

    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
    KMS_KEY_PURPOSE=asymmetric-signing
    KMS_KEY_ALGORITHM=KMS_KEY_ALGORITHM
    KMS_PROTECTION_LEVEL=KMS_PROTECTION_LEVEL
    

    Replace the following:

    • KMS_KEY_PROJECT_ID: the ID of the project where the keys are stored.
    • KMS_KEY_LOCATION: the location of the key
    • KMS_KEYRING_NAME: the name of the key ring
    • KMS_KEY_NAME: the name of the key
    • KMS_KEY_VERSION: the key version
    • KMS_KEY_ALGORITHM: the algorithm; ec-sign-p256-sha256 is recommended
    • KMS_PROTECTION_LEVEL: the protection level—for example, software
  2. To create the keyring, run the following command:

    gcloud kms keyrings create ${KMS_KEYRING_NAME} \
        --location ${KMS_KEY_LOCATION}
    
  3. To create the key, run the following command:

    gcloud kms keys create ${KMS_KEY_NAME} \
        --location ${KMS_KEY_LOCATION} \
        --keyring ${KMS_KEYRING_NAME}  \
        --purpose ${KMS_KEY_PURPOSE} \
        --default-algorithm ${KMS_KEY_ALGORITHM} \
        --protection-level ${KMS_PROTECTION_LEVEL}
    

PKIX (local key)

To generate a new local asymmetric PKIX key pair and store it in a file:

  1. Generate the key:

    PRIVATE_KEY_FILE="/tmp/ec_private.pem"
    openssl ecparam -genkey -name prime256v1 -noout -out ${PRIVATE_KEY_FILE}
    
  2. Since this file contains both a public and private key, you need to extract the public key into a separate file so you can add it to the attestor:

    PUBLIC_KEY_FILE="/tmp/ec_public.pem"
    openssl ec -in ${PRIVATE_KEY_FILE} -pubout -out ${PUBLIC_KEY_FILE}
    

Create the attestor

The next step is to create the attestor itself in Binary Authorization with the associated Artifact Analysis note. You must also add the cryptographic public key.

To create the attestor:

  1. Set up an environment variable to store the name of the attestor as defined in Binary Authorization:

    ATTESTOR_NAME=ATTESTOR_NAME
    

    where ATTESTOR_NAME is the name of the attestor you want to create (for example, build-secure or prod-qa).

  2. Create the attestor and attach the public security key:

    PKIX (Cloud KMS)

    1. Set up additional environment variables to store information about the Cloud KMS key pair for the call to the Binary Authorization API.

      KMS_CRYPTO_KEY_URI="projects/${KMS_KEY_PROJECT_ID}/locations/${KMS_KEY_LOCATION}/keyRings/${KMS_KEYRING_NAME}/cryptoKeys/${KMS_KEY_NAME}"
      KMS_CRYPTO_KEY_VERSION_URI="${KMS_CRYPTO_KEY_URI}/cryptoKeyVersions/${KMS_KEY_VERSION}"
      KMS_KEY_ID="//cloudkms.googleapis.com/v1/${KMS_CRYPTO_KEY_VERSION_URI}"
      
    2. Download the public key file from Cloud KMS and save it to a file named /tmp/kms_public_key.pem on your local system.

    3. Generate a JSON file that contains the information needed to create the attestor:

      cat > /tmp/attestor.json << EOM
      {
          "userOwnedDrydockNote": {
              "noteReference": "${NOTE_URI}",
              "publicKeys": {
                  "id": "${KMS_KEY_ID}",
                  "pkixPublicKey": {
                      "signatureAlgorithm": "${KMS_KEY_ALGORITHM}",
                      "publicKeyPem": $( \
                          python < /tmp/kms_public_key.pem \
                          -c 'import json, sys; print(json.dumps(sys.stdin.read()))' \
                      )
                  }
              }
          }
      }
      EOM
      
    4. Create the attestor:

      curl -X POST  \
          -H "Content-Type: application/json" \
          -H "Authorization: Bearer $(gcloud auth print-access-token)" \
          -H "X-Goog-User-Project: ${ATTESTOR_PROJECT_ID}" \
          --data-binary @/tmp/attestor.json \
      "https://binaryauthorization.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/attestors?attestorId=${ATTESTOR_NAME}"
      

    PKIX (local key)

    1. Generate a JSON file that contains the information needed to create the attestor:

      cat > /tmp/attestor.json << EOM
      {
         "userOwnedGrafeasNote": {
             "noteReference": "${NOTE_URI}",
             "publicKeys": {
                 "pkixPublicKey": {
                     "signatureAlgorithm": "ecdsa_p256_sha256",
                     "publicKeyPem": $( \
                         python < ${PUBLIC_KEY_FILE} \
                         -c 'import json, sys; print(json.dumps(sys.stdin.read()))' \
                     )
                 }
             }
         }
      }
      EOM
      
    2. Create the attestor:

      curl -X POST  \
         -H "Content-Type: application/json" \
         -H "Authorization: Bearer $(gcloud auth print-access-token)" \
         -H "X-Goog-User-Project: ${ATTESTOR_PROJECT_ID}" \
         --data-binary @/tmp/attestor.json \
      "https://binaryauthorization.googleapis.com/v1/projects/${ATTESTOR_PROJECT_ID}/attestors?attestorId=${ATTESTOR_NAME}"
      
  3. Add an IAM role binding for the deployer project to the attestor. This is used by Binary Authorization when it evaluates a policy to determine whether the project has permissions to access the referenced attestor.

    Generate a JSON file that contains the information needed to set the IAM policy on your attestor:

    cat > /tmp/iam_request.json << EOM
    {
      'resource': 'projects/${ATTESTOR_PROJECT_ID}/attestors/${ATTESTOR_NAME}',
      'policy': {
        'bindings': [
          {
            'role': 'roles/binaryauthorization.attestorsVerifier',
            'members': [
              'serviceAccount:${DEPLOYER_SERVICE_ACCOUNT}'
            ]
          }
        ]
      }
    }
    EOM
    

    Add the service account and requested access roles to the IAM policy for the note you created:

    curl -X POST  \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        -H "x-goog-user-project: ${ATTESTOR_PROJECT_ID}" \
        --data-binary @/tmp/iam_request.json \
        "https://binaryauthorization.googleapis.com/v1beta1/projects/${ATTESTOR_PROJECT_ID}/attestors/${ATTESTOR_NAME}:setIamPolicy"
    

Verify that the attestor was created

To verify that the attestor was created, run the following command:

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

What's next