Create attestations with Kritis Signer

This tutorial explains how to build Kritis Signer and use it to check container images for vulnerabilities before creating Binary Authorization attestations.

Overview

Kritis Signer is an open source command-line tool that can create Binary Authorization attestations based on a policy that you configure. You can also use Kritis Signer to create attestations after checking an image for vulnerabilities identified by Artifact Analysis.

Additionally, Cloud Build can run Kritis Signer as a custom builder in a build pipeline.

In this tutorial you perform a one-time build of the Kritis Signer custom builder, and then you run sample build pipelines. Each sample pipeline contains the following build steps:

  1. Build a sample container image.
  2. Push the image to Container Registry.
  3. Check and sign the image: Use Kritis Signer to create an attestation based on the policy.

In the check-and-sign build step of each pipeline, Kritis Signer does the following:

  1. Scans the newly built image with Artifact Analysis and retrieves a list of vulnerabilities.
  2. Checks the list of vulnerabilities against vulnerability signing rules in the policy and then:
    1. If all of the identified vulnerabilities satisfy the vulnerability signing rules, Kritis Signer creates the attestation.
    2. If any of the identified vulnerabilities violate the vulnerability signing rules, Kritis Signer does not create the attestation.

At deploy time, the Binary Authorization enforcer checks for a verifiable attestation. Without one, the enforcer disallows the image from deploying.

This tutorial also explains how to run Kritis Signer in check-only mode in a Cloud Build pipeline. In this mode, Kritis Signer does not create an attestation, it only checks whether the vulnerability results satisfy the vulnerability signing rules in the policy. If they do, the Kritis Signer build step succeeds and the pipeline continues to run, otherwise the step fails and the pipeline exits.

Objectives

In this tutorial you do the following:

  1. Set up Kritis Signer as a Cloud Build custom builder.
  2. View a policy that contains vulnerability signing rules.
  3. Run Kritis Signer in create attestations based on vulnerability scanning results.
  4. Run Kritis Signer in check-only mode.

Costs

This tutorial uses the following Google Cloud products.

  • Container Registry
  • Artifact Analysis
  • Cloud Build
  • Cloud Key Management Service

Use the Pricing Calculator to generate a cost estimate based on your projected usage.

Before you begin

In this section, you perform a one-time setup of the system.

Set up your environment

  1. Store your Google Cloud project in an environment variable.

    export PROJECT_ID=PROJECT_ID
    

    Replace PROJECT_ID with your Google Cloud project.

  2. Set the default project ID to your Google Cloud project:

    gcloud config set project $PROJECT_ID
    
  3. Store the project number in an environment variable for future steps:

    export PROJECT_NUMBER=$(gcloud projects list --filter="${PROJECT_ID}" \
     --format="value(PROJECT_NUMBER)")
    
  4. Enable APIs:

    To ensure the services required for this guide are enabled, execute the following command:

    gcloud services enable \
      cloudbuild.googleapis.com \
      containerregistry.googleapis.com \
      containerscanning.googleapis.com \
      cloudkms.googleapis.com
    

Set up IAM roles

Run the following commands to configure the Cloud Build service account with the following roles:

  • containeranalysis.notes.editor: adds the Artifact Analysis Notes Editor role to manage the attestor.
  • containeranalysis.notes.occurrences.viewer: adds the Artifact Analysis Occurrences for Notes role to manage both the vulnerability and attestation occurrences.
  • roles/containeranalysis.occurrences.editor: adds the Artifact Analysis Occurrences Editor role to create attestation occurrences in Artifact Analysis.
  • cloudkms.signer: adds the Cloud KMS CryptoKey Signer role that allows the service account to access the Cloud KMS signing service.

    gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:[email protected] --role roles/containeranalysis.notes.editor
    gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:[email protected] --role roles/containeranalysis.notes.occurrences.viewer
    gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:[email protected] --role roles/containeranalysis.occurrences.editor
    gcloud projects add-iam-policy-binding $PROJECT_ID --member serviceAccount:[email protected] --role roles/cloudkms.signer
    

Set up the Kritis Signer custom builder

In this section, you perform a one-time setup of the Kritis Signer custom builder. Once you obtain, build, and push Kritis Signer, it can then be used in any Cloud Build pipeline.

This section shows you how to:

  • Clone the Kritis repository.
  • Build the Kritis Signer Cloud Build custom builder.
  • Push Kritis Signer to Container Registry to make it available for use as a Cloud Build build step.

Run the following commands to get the code and configuration files used in this guide:

  1. Clone the Kritis repository:

    git clone https://github.com/grafeas/kritis.git
    

    This repository contains the following:

    • Source code for Kritis that also includes Kritis Signer.
    • A Cloud Build configuration file that is used by Cloud Build to build the Kritis Signer custom builder.
    • An example policy that contains vulnerability signing rules.
    • Example Cloud Build configuration files. Each configuration file uses Kritis Signer in a vulnerability scanning pipeline.
  2. Navigate to the kritis/ directory:

    cd kritis
    
  3. Build and register Kritis Signer custom builder.

    This one-time setup step builds the Kritis Signer custom builder and registers it with Cloud Build. Once registered, the custom builder is available for use in any Cloud Build pipeline.

    gcloud builds submit . --config deploy/kritis-signer/cloudbuild.yaml
    

View an existing policy

This section shows an example Kritis Signer policy.

This policy configures Kritis Signer to request Artifact Analysis to scan the image for vulnerabilities. After the scan completes, Kritis Signer checks returned vulnerability results against the vulnerability signing rules in the policy.

You can edit the vulnerability signing rules in this policy to create an attestation based on the following:

  • Severity levels of identified vulnerabilities.
  • Specific vulnerabilities.

You can also set the policy to unconditionally create (ALLOW_ALL) or not create an (BLOCK_ALL) an attestation.

To view the Kritis Signer policy, execute the following command:

cat samples/signer/policy-strict.yaml

The policy resembles the following:

apiVersion: kritis.grafeas.io/v1beta1
kind: VulnzSigningPolicy
metadata:
  name: my-vsp
spec:
  imageVulnerabilityRequirements:
    maximumFixableSeverity: MEDIUM
    maximumUnfixableSeverity: MEDIUM
    allowlistCVEs:
    - projects/goog-vulnz/notes/CVE-2021-20305

Where:

  • maximumUnfixableSeverity and maximumFixableSeverity define Common Vulnerabilities and Exposures (CVE) severity thresholds at which Kritis Signer creates attestations. maximumUnfixableSeverity defines the threshold for severity for which a fix isn't currently available. maximumFixableSeverity defines the threshold for severity for which a fix is currently available. maximumUnfixableSeverity and maximumFixableSeverity can each be set to one of the following severity levels:

    • CRITICAL
    • HIGH
    • MEDIUM
    • LOW

    For more information on severity levels, see Severity levels.

    Alternatively, you can set maximumUnfixableSeverity and maximumFixableSeverity to the following:

    • BLOCK_ALL: The attestation is not created if any vulnerability is identified.
    • ALLOW_ALL: The attestation is always created.
  • allowlistCVEs is a list of specific CVEs to allowlist. Kritis Signer ignores CVEs in this list when evaluating whether to create an attestation. Each entry in the allowlist must exactly match the Artifact Analysis note name for the CVE. Learn more about Artifact Analysis Vulnerability sources. For more information about notes, see Metadata Storage.

Create a Cloud KMS signing key

Cloud Key Management Service keys are used to create the attestation.

  1. Create a new Cloud KMS key ring with the name KEY_RING:

    gcloud kms keyrings create KEY_RING \
       --location global
    
  2. Create a new Cloud KMS key called KEY_NAME within the key ring:

    gcloud kms keys create KEY_NAME \
        --keyring KEY_RING \
        --location global \
        --purpose "asymmetric-signing" \
        --default-algorithm "rsa-sign-pkcs1-2048-sha256"
    
  3. Store the digest algorithm and Cloud KMS in an environment variable for future steps:

    export KMS_DIGEST_ALG=SHA256
    export KMS_KEY_NAME=projects/$PROJECT_ID/locations/global/keyRings/KEY_RING/cryptoKeys/KEY_NAME/cryptoKeyVersions/1
    

Define a note name

All attestations reference a Artifact Analysis note. Kritis Signer automatically creates a note for a given name. You can also reuse existing note names.

export NOTE_ID=my-signer-note
export NOTE_NAME=projects/${PROJECT_ID}/notes/${NOTE_ID}

Create attestations with Kritis Signer in a Cloud Build pipeline

This section demonstrates how to use the Kritis Signer custom cloud builder to create Binary Authorization attestations based on vulnerability scanning results.

The following steps demonstrate how Kritis Signer works using the example build config files in the Kritis Signer repository. Each example config file contains the following build steps:

  1. A docker build step that builds a Docker container image.
  2. A docker push step that pushes the newly built container image to Container Registry.
  3. A vulnsign step that checks and signs the container image by:

    1. Waiting for Artifact Analysis to return vulnerability findings on the newly built container image.
    2. Checking the findings against the vulnerability signing rules in the policy.
    3. Creating the attestation if the findings satisfy the vulnerability rules.

You submit each of the example builds to Cloud Build. Each build produces a vulnerability result:

  • Failure case: the vulnerability result violates the vulnerability signing rules. This build fails and no attestation is created.
  • Success case: the vulnerability result satisfies the vulnerability signing rules. This build succeeds and an attestation is created.

Submit the failure-case sample build

In this section, you build a container image and scan it for vulnerabilities. The build fails because the container image is based on a specific snapshot of Debian 10, which contains a number of vulnerabilities with severity level HIGH. These vulnerabilities violate the vulnerability signing rule. As a result, the builder doesn't produce an attestation.

  1. (Optional) View the vulnerability signing policy file for the failure case.

    cat samples/signer/policy-strict.yaml
    
  2. Submit the build:

    gcloud builds submit \
      --substitutions=_KMS_KEY_NAME=$KMS_KEY_NAME,_KMS_DIGEST_ALG=$KMS_DIGEST_ALG,_NOTE_NAME=$NOTE_NAME \
      --config=samples/signer/cloudbuild-bad.yaml samples/signer
    

    You see output like the following:

    "ERROR: (gcloud.builds.submit) build BUILD_ID completed with status "FAILURE"
    
  3. Save the build ID from the last build:

    export BUILD_ID=$(gcloud builds list --limit=1 --format="value('ID')")
    
  4. Verify the result:

     gcloud storage cat gs://${PROJECT_NUMBER}.cloudbuild-logs.googleusercontent.com/log-${BUILD_ID}.txt | grep "does not pass VulnzSigningPolicy"
    

Submit the success-case sample build

In this section you build a container image that contains vulnerabilities that don't violate the vulnerability signing rules. In this case, the Kritis Signer custom builder creates an attestation.

To submit the success-case sample build to Cloud Build, do the following:

  1. (Optional) View the vulnerability signing policy file for the success case.

    cat samples/signer/policy-loose.yaml
    
  2. Submit the build:

    gcloud builds submit \
      --substitutions=_KMS_KEY_NAME=$KMS_KEY_NAME,_KMS_DIGEST_ALG=$KMS_DIGEST_ALG,_NOTE_NAME=$NOTE_NAME \
      --config=samples/signer/cloudbuild-good.yaml samples/signer
    
  3. Save the build ID from the last build:

    export BUILD_ID=$(gcloud builds list --limit=1 --format="value('ID')")
    
  4. Verify the result:

    gcloud builds describe $BUILD_ID | grep status
    

Use Kritis Signer in check-only mode

This section shows you how to use Kritis Signer in check-only mode. In this mode, Kritis Signer does not create an attestation. It only checks the image for vulnerabilities before either succeeding or failing the build step based on the vulnerability signing rules.

Submit the failure-case sample build

  1. (Optional) View the vulnerability signing policy file for the failure case.

    cat samples/policy-check/policy-strict.yaml
    

    In the Kritis Signer build step, note that the mode flag is set to check-only.

  2. Submit the build:

    gcloud builds submit \
      --config=samples/policy-check/cloudbuild-bad.yaml samples/policy-check
    

    Note that the build fails.

  3. Save the build ID from the last build:

    export BUILD_ID=$(gcloud builds list --limit=1 --format="value('ID')")
    
  4. Verify the result:

     gcloud storage cat gs://${PROJECT_NUMBER}.cloudbuild-logs.googleusercontent.com/log-${BUILD_ID}.txt | grep "does not pass VulnzSigningPolicy"
    

Submit the success-case sample build

  1. (Optional) View the vulnerability signing policy file for the success case.

    cat samples/policy-check/policy-loose.yaml
    
  2. Submit the build:

    gcloud builds submit \
     --config=samples/policy-check/cloudbuild-good.yaml samples/policy-check
    
  3. Save the build ID from the last build:

    export BUILD_ID=$(gcloud builds list --limit=1 --format="value('ID')")
    
  4. Verify the result:

    gcloud builds describe $BUILD_ID | grep status
    

Create an attestor

To create a policy that requires the attestations that you create using the method described in this guide, you must first create an attestor.

To create an attestor, do the following:

  1. Retrieve the public key material from the Cloud KMS key that you created earlier in this guide:

    gcloud kms keys versions get-public-key 1 \
    --key KEY_NAME \
    --keyring KEY_RING \
    --location global \
    --output-file OUTPUT_PATH
    
    • KEY_NAME: the key name
    • KEY_RING: the key ring name
    • OUTPUT_PATH: a file path—for example, my-key.pem
  2. Create an attestor by using the public key material in the file and the note that you created earlier in this guide. You can create an attestor through the Google Cloud console or the gcloud CLI.

  3. Create a policy that requires attestations and supply the attestor that you created in this section. You can create a policy through the Google Cloud console or the gcloud CLI

Create an attestation

To create an attestation using your attestor, see Create an attestation using Cloud KMS.

Clean up

To clean up resources used in this document, you can delete the project:

gcloud projects delete ${PROJECT_ID}

What's next