Container registries are easy to poison and hard to police. A compromised CI runner, a typo-squatted base image, or a developer laptop with stale credentials can all result in an unsigned, unverified image reaching production before anyone notices. Binary Authorization GKE integration closes that gap by refusing to run any image that doesn't carry a cryptographic attestation from a trusted authority — turning "we hope nothing bad got deployed" into "nothing bad can get deployed."
This guide walks through standing up Binary Authorization on a GKE cluster end to end: enabling the service, creating attestors backed by Container Analysis, writing a policy that blocks unattested images, wiring signing into your CI pipeline, and validating enforcement actually works. By the end you'll have a working attestation based deployment GCP pipeline where every workload running in your cluster can be traced back to a signed, verified build.
1. Understand the Binary Authorization GKE Enforcement Model
Before touching configuration, it helps to know what's actually enforced and where. Binary Authorization sits between the GKE API server and the kubelet's image pull. Every time a Pod is created or updated, the admission controller intercepts the request, extracts the image digest, and checks it against a policy. The policy references one or more attestors — entities that vouch for an image by attaching a signed note to it in Container Analysis (Google's metadata store for vulnerability and provenance data).
Three pieces work together:
- Attestors — represent a trusted party (a CI system, a security team, a QA gate) and hold the public key(s) used to verify signatures.
- Attestations — signed statements, tied to an image digest, created after that party approves the image (e.g., "this digest passed the vulnerability scan").
- Policy — the rule set evaluated at deploy time, specifying which attestors are required for which projects, clusters, or namespaces.
Critically, enforcement is keyed to the immutable image digest, not the tag — so retagging a bad image to look like a good one doesn't bypass the check.
2. Enable Binary Authorization on Your GKE Cluster
Start by enabling the required APIs and turning on Binary Authorization when creating (or updating) your cluster.
gcloud services enable \
binaryauthorization.googleapis.com \
containeranalysis.googleapis.com \
cloudkms.googleapis.com
# New cluster
gcloud container clusters create prod-cluster \
--enable-binauthz \
--region us-central1
# Existing cluster
gcloud container clusters update prod-cluster \
--binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE \
--region us-central1
For GKE Autopilot clusters, Binary Authorization is enabled the same way via --binauthz-evaluation-mode on clusters update. Give the change a few minutes to propagate before testing — the admission webhook needs to register before it will start blocking anything.
3. Create an Attestor Backed by a KMS Key
Attestors need an asymmetric key pair to verify signatures. Using Cloud KMS keeps private key material out of your CI logs and laptops entirely.
# Create a key ring and signing key
gcloud kms keyrings create binauthz-keys --location us-central1
gcloud kms keys create build-attestor-key \
--keyring binauthz-keys \
--location us-central1 \
--purpose asymmetric-signing \
--default-algorithm rsa-sign-pkcs1-4096-sha512
# Create a Container Analysis note and the attestor that references it
gcloud container binauthz attestors create build-attestor \
--attestation-authority-note=build-attestor-note \
--attestation-authority-note-project=$PROJECT_ID
gcloud container binauthz attestors public-keys add \
--attestor=build-attestor \
--keyversion-project=$PROJECT_ID \
--keyversion-location=us-central1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=1
Repeat this pattern for each gate you want represented — a common setup is a build-attestor for "came from our CI" and a security-attestor for "passed vulnerability scanning with no critical CVEs."
4. Configure a Binary Authorization Policy
The policy is what actually turns attestation checking into enforcement. Export the default, edit it, and re-import it.
gcloud container binauthz policy export > policy.yaml
Edit policy.yaml to require your attestor(s) by default, while allowing a documented exception for system images:
defaultAdmissionRule:
evaluationMode: REQUIRE_ATTESTATION
enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
requireAttestationsBy:
- projects/PROJECT_ID/attestors/build-attestor
- projects/PROJECT_ID/attestors/security-attestor
admissionWhitelistPatterns:
- namePattern: gcr.io/gke-release/*
globalPolicyEvaluationMode: ENABLE
Apply it:
gcloud container binauthz policy import policy.yaml
This is the core of enforce signed images GKE workflows: REQUIRE_ATTESTATION with ENFORCED_BLOCK_AND_AUDIT_LOG means non-compliant Pods are rejected outright and the rejection is logged to Cloud Audit Logs for later review. If you're rolling this out against a live cluster, start with DRYRUN_AUDIT_LOG_ONLY for a week to see what would have been blocked before flipping to enforcement.
5. Sign Images and Generate Attestations in CI
Enforcement only works if something is actually creating attestations. Add a signing step to your CI pipeline immediately after build and scan, keyed to the resolved image digest.
DIGEST=$(gcloud container images describe \
us-central1-docker.pkg.dev/$PROJECT_ID/app/myservice:$TAG \
--format='get(image_summary.digest)')
gcloud container binauthz attestations sign-and-create \
--project=$PROJECT_ID \
--artifact-url="us-central1-docker.pkg.dev/$PROJECT_ID/app/myservice@$DIGEST" \
--attestor=build-attestor \
--attestor-project=$PROJECT_ID \
--keyversion-project=$PROJECT_ID \
--keyversion-location=us-central1 \
--keyversion-keyring=binauthz-keys \
--keyversion-key=build-attestor-key \
--keyversion=1
Gate this step behind whatever checks matter to your organization — unit tests, SCA/SAST results, container vulnerability scanning — so the attestor's signature actually means something. This is the essence of attestation based deployment GCP pipelines: the signature isn't a rubber stamp, it's evidence that specific quality gates passed for that exact digest.
6. Enforce Signed Images Across Namespaces and Environments
A single global policy is a reasonable starting point, but most teams need finer control — for instance, allowing unattested images in a dev namespace while enforcing strictly in prod. Binary Authorization supports per-cluster policy via platform policies on GKE, or you can achieve namespace-level separation by pairing Binary Authorization with Kubernetes admission controls (like an OPA/Gatekeeper constraint) that only apply the strict evaluation mode to production namespaces.
For multi-cluster fleets, use gcloud container binauthz policy import per project and consider a Cloud Build trigger that lints and applies policy changes through a pull request, so policy edits get the same review rigor as code.
Verification and Troubleshooting
Confirm enforcement is live by attempting to deploy an unsigned image:
kubectl run test-unsigned --image=us-central1-docker.pkg.dev/$PROJECT_ID/app/unsigned-test:latest
You should see an admission error referencing Binary Authorization, and a corresponding entry in Cloud Audit Logs under binaryauthorization.googleapis.com. If the Pod is not blocked, check the following, roughly in order of likelihood:
- Evaluation mode mismatch — confirm the cluster was created or updated with
--binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE; a cluster still onDISABLEDsilently ignores policy. - Stale policy propagation — policy changes can take a minute or two to reach the admission webhook; retry before assuming it's broken.
- Whitelist patterns too broad — an overly permissive
admissionWhitelistPatternsentry (e.g., a wildcard registry path) will quietly exempt more than intended. - Wrong attestor on the signature — verify with
gcloud container binauthz attestations list --artifact-url=...that the attestation was created against the correct attestor and digest, not just the tag. - IAM on the KMS key — the CI service account needs
roles/cloudkms.signerVerifieron the specific key version; missing permissions fail the sign step silently in some CI runners, leaving images undeployed with a confusing error further downstream.
For ongoing operations, route Binary Authorization audit log entries into your SIEM so blocked deployment attempts are visible to security, not just buried in cluster logs that only platform engineers read.
How Safeguard Helps
Binary Authorization gives GKE a hard enforcement point, but it only reflects the quality of what feeds it — attestors are only as trustworthy as the process that creates them, and policies drift as clusters, namespaces, and teams multiply. Safeguard closes that operational gap for software supply chain security teams running Binary Authorization at scale:
- Attestor and key inventory — Safeguard continuously discovers every attestor, KMS key, and policy binding across your GCP projects, flagging attestors with overly broad IAM grants or keys that haven't rotated.
- Policy drift detection — when a cluster's Binary Authorization policy diverges from your organization's baseline (a whitelist pattern loosened, evaluation mode silently reverted to dry-run), Safeguard raises it before it becomes an incident.
- End-to-end provenance linking — Safeguard ties each attestation back to the specific build, commit, and scan results that produced it, so "this image is signed" also means "here is exactly why it was signed" for audit and incident response.
- Enforcement gap analysis — Safeguard identifies namespaces, clusters, or CI pipelines that bypass attestation entirely, closing the loop between the Binary Authorization policy you think you're running and the one actually enforced everywhere.
If you're building out attestation based deployment GCP practices and want visibility into how well they're actually holding up in production, Safeguard gives supply chain and platform security teams the continuous assurance that manual policy reviews can't.