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
- Enable Binary Authorization.
- Create a cluster.
- Create a cryptographic key pair and an attestor, either through the command-line tool or the Google Cloud Console.
- 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
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.
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
orprod-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 exampleus-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.
- ATTESTOR_NAME: the name of the attestor, for example,
Create an attestation
Create an attestation using a locally stored key
To create an attestations signed with a local key, do the following:
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
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.
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)')
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:
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.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
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:
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
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
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 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 usesha256
,sha384
, orsha512
.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.
Store the signature in an environment variable:
PAYLOAD_SIGNATURE=PAYLOAD_SIGNATURE
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.Store the public key ID in an environment variable:
PUBLIC_KEY_ID=PUBLIC_KEY_ID
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
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.
What's next
- Deploy a container image to a cluster where Binary Authorization is enabled.