Cloud Security

GCP Binary Authorization: Enforcing Container Trust at Deploy Time

A practical walkthrough of Binary Authorization on GKE, from attestor setup to break-glass procedures and CI/CD integration.

Nayan Dey
DevSecOps Lead
7 min read

Binary Authorization is one of GCP's most powerful and least understood security features. It answers a simple question: should this container image be allowed to run on this cluster? The answer is based on cryptographic attestations -- verifiable proof that the image passed your security checks.

Most teams know Binary Authorization exists. Far fewer have it running in enforce mode in production. The gap is usually not technical -- it is organizational. Setting up attestors, integrating with CI/CD, and handling edge cases requires planning that teams skip in favor of faster feature delivery.

This post walks through a production-ready Binary Authorization setup, including the edge cases that trip people up.

How Binary Authorization Works

The concept is straightforward. You define a policy that says which attestations an image must have before it can run on a GKE cluster. Attestations are cryptographic signatures created by systems you trust -- your build pipeline, your vulnerability scanner, your manual review process.

When Kubernetes tries to create a pod, Binary Authorization intercepts the request and checks whether the image has the required attestations. If it does, the pod is created. If it does not, the request is denied.

The components:

Attestors. An attestor represents a verification step. You might have attestors for "built by our CI/CD pipeline," "passed vulnerability scanning," and "approved by security review." Each attestor has a public key (stored in Binary Authorization) and a corresponding private key (stored in Cloud KMS or managed externally).

Attestations. An attestation is a cryptographic signature over an image digest, created by an attestor's private key. It says "this specific image (by digest) was verified by this specific attestor."

Policies. A policy defines which attestors are required for images to be deployed. You can set different policies per cluster, per namespace, or per image path.

Setting Up Attestors

Start with two attestors: one for your build pipeline and one for your vulnerability scanner.

Build attestor. This attestor verifies that an image was built by your approved CI/CD pipeline. The attestation is created at the end of a successful Cloud Build or your preferred CI system.

Create the attestor:

gcloud container binauthz attestors create build-attestor \
  --attestation-authority-note=build-note \
  --attestation-authority-note-project=your-project

Create a KMS key for signing:

gcloud kms keys create build-signer \
  --keyring=binauthz-keys \
  --location=global \
  --purpose=asymmetric-signing \
  --default-algorithm=ec-sign-p256-sha256

Associate the key with the attestor:

gcloud container binauthz attestors public-keys add \
  --attestor=build-attestor \
  --keyversion-project=your-project \
  --keyversion-location=global \
  --keyversion-keyring=binauthz-keys \
  --keyversion-key=build-signer \
  --keyversion=1

Vulnerability scan attestor. This attestor verifies that an image passed vulnerability scanning. The attestation is created after scanning completes with results below your threshold.

Set this up the same way with a separate KMS key. Using separate keys for each attestor means that compromising one key does not allow an attacker to bypass all verification steps.

Creating Attestations in CI/CD

Your CI/CD pipeline creates attestations after each verification step succeeds.

In Cloud Build, add steps that create attestations after build and scan:

steps:
  # Build the image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['build', '-t', 'us-docker.pkg.dev/$PROJECT_ID/repo/app:$SHORT_SHA', '.']

  # Push the image
  - name: 'gcr.io/cloud-builders/docker'
    args: ['push', 'us-docker.pkg.dev/$PROJECT_ID/repo/app:$SHORT_SHA']

  # Create build attestation
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: 'gcloud'
    args:
      - 'container'
      - 'binauthz'
      - 'attestations'
      - 'sign-and-create'
      - '--artifact-url=us-docker.pkg.dev/$PROJECT_ID/repo/app:$SHORT_SHA'
      - '--attestor=build-attestor'
      - '--attestor-project=$PROJECT_ID'
      - '--keyversion-project=$PROJECT_ID'
      - '--keyversion-location=global'
      - '--keyversion-keyring=binauthz-keys'
      - '--keyversion-key=build-signer'
      - '--keyversion=1'

  # Scan and create scan attestation (after vulnerability check passes)
  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        # Run vulnerability scan and check results
        # Only create attestation if scan passes threshold
        gcloud container binauthz attestations sign-and-create \
          --artifact-url=us-docker.pkg.dev/$PROJECT_ID/repo/app:$SHORT_SHA \
          --attestor=scan-attestor \
          --attestor-project=$PROJECT_ID \
          --keyversion-project=$PROJECT_ID \
          --keyversion-location=global \
          --keyversion-keyring=binauthz-keys \
          --keyversion-key=scan-signer \
          --keyversion=1

Configuring the Policy

The policy defines what attestations are required. Start with a simple policy and evolve it.

Require both attestors for production clusters:

defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: REQUIRE_ATTESTATION
  requireAttestationsBy:
    - projects/your-project/attestors/build-attestor
    - projects/your-project/attestors/scan-attestor

Allow specific system images. GKE system components need to run without attestations. Add admission whitelist patterns for GKE system images:

admissionWhitelistPatterns:
  - namePattern: 'gke.gcr.io/*'
  - namePattern: 'gcr.io/gke-release/*'
  - namePattern: 'gcr.io/config-management-release/*'

Use cluster-specific policies. Development clusters can have a more permissive policy (audit-only or require only the build attestation). Production clusters should require all attestations.

Dry-Run Mode

Do not go straight to enforcement. Start with dry-run mode.

In dry-run mode, Binary Authorization evaluates the policy and logs the result but does not block non-compliant deployments. This lets you verify that your attestation pipeline works correctly and that your whitelist patterns cover all necessary system images.

Run in dry-run for at least two weeks. Monitor the audit logs for policy violations. Each violation represents a deployment that would be blocked in enforce mode. Investigate each one:

  • Is it a system component that needs to be whitelisted?
  • Is it an image from a pipeline that does not yet create attestations?
  • Is it a third-party image that needs a separate attestation workflow?

Only switch to enforce mode when you have zero unexpected violations over a sustained period.

Break-Glass Procedures

Sometimes you need to deploy an image that does not have the required attestations. A production outage, a zero-day patch, or a vendor-provided hotfix might require bypassing Binary Authorization.

Use breakglass annotations. Binary Authorization supports a breakglass annotation on pod specs that overrides the policy. When a pod has this annotation, it is allowed to run regardless of attestation status, and the bypass is logged.

metadata:
  annotations:
    alpha.image-policy.k8s.io/break-glass: "true"

Restrict who can use breakglass. Use RBAC and admission webhooks to control who can add the breakglass annotation. Not every developer should be able to bypass your security policy.

Alert on breakglass usage. Every breakglass deployment should trigger an alert to your security team. Review the deployment after the incident to understand why the normal attestation pipeline was bypassed.

Create attestations retroactively. After a breakglass deployment, go back and create the necessary attestations once the urgency has passed. Then remove the breakglass annotation.

Common Pitfalls

Using tags instead of digests. Binary Authorization works on image digests, not tags. If your deployments reference images by tag (e.g., app:latest), Binary Authorization cannot verify them reliably. Always use digests in your deployment manifests.

Forgetting about init containers and sidecars. Binary Authorization checks all container images in a pod, including init containers and sidecars. Ensure that sidecar images (Istio proxies, logging agents) are either attested or whitelisted.

KMS key rotation. When you rotate KMS keys, update the attestor's public key list. Old attestations signed with the previous key version remain valid as long as the old public key is still associated with the attestor.

How Safeguard.sh Helps

Safeguard.sh integrates with GCP Binary Authorization to add supply chain intelligence to your attestation workflow. It can serve as an attestation source -- automatically creating attestations when images pass Safeguard.sh's vulnerability and policy checks. This means your Binary Authorization policy can require that images not only were built by your pipeline but also passed Safeguard.sh's comprehensive supply chain analysis.

The platform provides visibility into your attestation coverage: which images have complete attestations, which are missing attestations, and which were deployed via breakglass. This operational view helps you maintain the integrity of your Binary Authorization deployment over time.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.