How to Automate the Kritis Signer on Google Cloud

Hiring

We are Binx. We make every organization cloud-native.

How to Automate the Kritis Signer on Google Cloud

Google Binary Authorization allows you to control the images allowed on your Kubernetes cluster. You name the images or allow only images required to be signed off by trusted parties. Google documents a manual process of creating Kritis signer attestions. In this blog, I show you how to automate the Kritis signer, using Terraform and Google Cloud Run.

What is the Kritis signer

The Kritis Signer is a command-line utility
to check whether an image violates the policy on security vulnerabilities. After the container scanning service completes a scan, you need to type:

signer \ 
  -mode check-and-sign \
  -policy policy.yaml \ 
  -note_name projects/demo/notes/passed-vulnerability-policy \ 
  -kms_key_name projects/demo/locations/eur4/keyRings/vulnerability_policy_attestors/cryptoKeys/vulnerability-attestor-XX0/cryptoKeyVersions/1 \
  -kms_digest_alg SHA384 \ 
  -image $IMAGE_TO_SCAN

This command checks if the image vulnerability scan satisfies the following policy:

apiVersion: Kritis.grafeas.io/v1beta1
kind: VulnzSigningPolicy
metadata:
  name: my-vsp
spec:
  imageVulnerabilityRequirements:
    maximumFixableSeverity: MEDIUM
    maximumUnfixableSeverity: MEDIUM
    allowlistCVEs:
    - projects/goog-vulnz/notes/CVE-2020-10543
    - projects/goog-vulnz/notes/CVE-2020-10878
    - projects/goog-vulnz/notes/CVE-2020-14155

If the image passes, it will create an attestation using the note passed-vulnerability-policy signed using the KMS key.

As this command needs to be include in a build pipeline, you run the risk that the developers relax the security policy. This will make the Kritis attestation less valuable.

Automate the Kritis Signer

To give the attestor the status of an independent entity, I created the Kritis signer service. It listens to container scanning events. When a vulnerability scan completes, it performs the Kritis check-and-sign operation.

There are two ways to automate the signer. You can create a little wrapper program which invokes the Kritis signer. Alternatively, you can extend the current Kritis signer with this functionality.

I chose to extend the signer. To get it included in the standard release, I created a pull request for Kritis which was merged on Dec 22nd, 2020.

How Do You Deploy The Signer Service?

To deploy the Kritis signer service, you take the following steps:

  1. define the note
  2. create a kms key
  3. create the attestor
  4. deploy the Kritis signer
  5. subscribe it to the Google Container Analysis notifications

These are the big steps. We are leaving out a few small details like IAM policy bindings, which are included in this full Terraform template.

Defining the Note

A note (it’s not what you think) identifies a particular fact about an image. In this case, the note indicates that an image does not violate the policy on vulnerabilities. The note is defined using this Terraform snippet:

resource "google_container_analysis_note" "passed_vulnerability_policy" {
  name = "passed-vulnerability-policy"

  short_description = "image passed vulnerability policy"
  long_description  = <<EOF
attached to an image which passed the vulnerability 
scan without violating the security policy.
EOF

  attestation_authority {
    hint {
      human_readable_name = "vulnerability policy attestors"
    }
  }
}

Defining the KMS key

An attestor is someone who witnessed a fact. An attestor attests by signing a written statement, known as the attestation. This signature uses a KMS key, as defined using the following Terraform snippet:

resource "google_kms_key_ring" "vulnerability_policy_attestors" {
  name       = "vulnerability_policy_attestors"
  location   = "eur4"
  depends_on = [google_project_service.cloudkms]
}

resource "google_kms_crypto_key" "vulnerability_policy_attestor" {
  name     = "vulnerability-attestor"
  key_ring = google_kms_key_ring.vulnerability_policy_attestors.id
  purpose  = "ASYMMETRIC_SIGN"

  version_template {
    algorithm = "EC_SIGN_P384_SHA384"
  }
}

Creating the attestor

To create an attestor, you link the signing key with the note as shown in this Terraform snippet:

## Define the attestor to be and attestor of vulnerability policies
resource "google_binary_authorization_attestor" "vulnerability_policy" {
  name = "vulnerability-policy"
  attestation_authority_note {
    note_reference = google_container_analysis_note.passed_vulnerability_policy.name
    public_keys {
      id = data.google_kms_crypto_key_version.vulnerability_policy_attestor.id
      pkix_public_key {
        public_key_pem      = data.google_kms_crypto_key_version.vulnerability_policy_attestor.public_key[0].pem
        signature_algorithm = local.algorithms[data.google_kms_crypto_key_version.vulnerability_policy_attestor.public_key[0].algorithm]
      }
    }
  }
}

locals {
  algorithms = { ## avoid continuous update of google_binary_authorization_attestor
    "EC_SIGN_P384_SHA384" = "ECDSA_P384_SHA384"
  }
}

Deploying the Kritis Signer Service

Now you can deploy the Kritis signer service on Google Cloud Run, using this snippet:

resource "google_cloud_run_service" "vulnerability_policy_attestor" {
  name     = "vulnerability-policy-attestor"
  location = "europe-west1"

  template {
    spec {
      service_account_name = google_service_account.vulnerability_policy_attestor.email
      containers {
        image = "gcr.io/${var.project}/gcr-kritis-signer:latest"
        env {
          name  = "ATTESTATION_PROJECT"
          value = var.project
        }
        env {
          name  = "ATTESTATION_NOTE_NAME"
          value = google_container_analysis_note.passed_vulnerability_policy.id
        }
        env {
          name  = "ATTESTATION_KMS_KEY"
          value = replace(data.google_kms_crypto_key_version.vulnerability_policy_attestor.id, "///cloudkms.googleapis.com/[^/]*//", "")
        }
        env {
          name  = "ATTESTATION_DIGEST_ALGORITHM"
          value = "SHA384"
        }
        env {
          name  = "ATTESTATION_OVERWRITE"
          value = "true"
        }
        env {
          name  = "ATTESTATION_POLICY"
          value = file("policy.yaml")
        }
      }
    }
  }
  depends_on = [google_project_service.run]
}

The service provides the endpoints /check /check-and-sign and /event. The latter is the endpoint for container analysis notifications. See the open API specification for more details.

Subscribe to the Container Analysis Notifications

Finally, you subscribe the service to the container analysis occurrence notifications, as shown in this snippet:

## subscribe the attestor to the container analysis occurrences events
resource "google_pubsub_subscription" "vulnerability_policy_attestor" {
  name  = "vulnerability-policy-attestor"
  topic = "projects/${var.project}/topics/container-analysis-occurrences-v1"

  ack_deadline_seconds = 30

  push_config {
    push_endpoint = "${google_cloud_run_service.vulnerability_policy_attestor.status[0].url}/event"
    oidc_token {
      service_account_email = google_service_account.vulnerability_policy_attestor_invoker.email
    }
  }
  depends_on = [google_project_service.pubsub]
}

Now the Kritis signer will automatically sign every image pushed to the registry.

Demo

For the demo build the kritis signer service by typing:

git clone https://github.com/grafeas/kritis
cd kritis
gcloud builds submit --config deploy/gcr-kritis-signer/cloudbuild.yaml .

This will build and deploy the Kritis signer service into your project’s container registry.

Deploy the complete demo from the github repository, by typing:

git clone https://github.com/binxio/gcr-kritis-signer.git
cd gcr-kritis-signer/Terraform
terraform init
terraform apply -auto-approve

Push two images into the registry, by typing:

PROJECT=$(gcloud config get-value project)
docker pull alpine:3.2
docker tag alpine:3.2 gcr.io/$PROJECT/alpine:3.2
docker push !$
docker pull alpine:3.12
docker tag alpine:3.12 gcr.io/$PROJECT/alpine:3.12
$ docker push !$

Wait until the images are scanned, by typing:

gcloud beta container images list-tags gcr.io/$PROJECT/alpine --show-occurrences
DIGEST        TAGS  TIMESTAMP            VULNERABILITIES  VULNERABILITY_SCAN_STATUS
d7342993700f  3.12  2020-10-22T04:19:24                   FINISHED_SUCCESS
ddac200f3ebc  3.2   2019-01-30T23:20:12  HIGH=1           FINISHED_SUCCESS

Now you can see the attestations created by the service, by typing:

gcloud beta container binauthz attestations list --attestor=vulnerability-policy
---
attestation:
  serializedPayload: eyJjcml0aWNhbCI6eyJpZGVudGl0eSI6eyJkb2NrZXItcmVmZXJlbmNlIjoiZ2NyLmlvL3NwZWVsdHVpbi1tdmFuaG9sc3RlaWpuLTIvYWxwaW5lIn0sImltYWdlIjp7ImRvY2tlci1tYW5pZmVzdC1kaWdlc3QiOiJzaGEyNTY6ZDczNDI5OTM3MDBmOGNkN2FiYTg0OTZjMmQwZTU3YmUwNjY2ZTgwYjRjNDQxOTI1ZmM2ZjkzNjFmYTgxZDEwZSJ9LCJ0eXBlIjoiYXRvbWljIGNvbnRhaW5lciBzaWduYXR1cmUifX0=
  signatures:
  - publicKeyId: //cloudkms.googleapis.com/v1/projects/binx-io-public/locations/eur4/keyRings/vulnerability_policy_attestors/cryptoKeys/vulnerability-attestor-XX0/cryptoKeyVersions/1
    signature: MGYCMQDX-xe0hlYFZjDJNzNhI7Kdd-yMLVjWfT_DNUBP9mjnU6KaezIyxeng436sVYbSpQwCMQDDi5HQWnBfHCShGB1gbSbhOG6HWT6Fn6SAJzSGf_3sV0_2WuV3PwvKhBj7SSl0Xb4=
createTime: '2020-11-29T12:53:19.619920Z'
kind: ATTESTATION
name: projects/binx-io-public/occurrences/3ebccaff-67a6-4221-82ba-7e7515091342
noteName: projects/binx-io-public/notes/passed-vulnerability-policy
resourceUri: https://gcr.io/binx-io-public/alpine@sha256:d7342993700f8cd7aba8496c2d0e57be0666e80b4c441925fc6f9361fa81d10e
updateTime: '2020-11-29T12:53:19.619920Z'

Note that only one alpine:3.12 image has an attestation. Version 3.2 has a high vulnerability severity and does not pass the policy.

Summary

The Kritis vulnerability policy signer is easily automated using Terraform and Cloud Run. The service is independent and triggered by container scanning events. You can trust that the image does violate your policy on security vulnerabilities.

Image by Free-Photos from Pixabay

Mark van Holsteijn is a senior software systems architect, and CTO of binx.io. He is passionate about removing waste in the software delivery process and keeping things clear and simple.
Share this article: Tweet this post / Post on LinkedIn