Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implement GCP Binary Authorization to enforce deploy-time security controls that ensure only trusted, attested container images are deployed to Google Kubernetes Engine and Cloud Run.
.claude/skills/implementing-gcp-binary-authorization/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-17 | ✗→✓ | ▲ Improved | — | — |
| case-08 | ✗→✓ | ▲ Improved | — | — |
| case-02 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
Binary Authorization is a Google Cloud deploy-time security control that ensures only trusted container images are deployed on GKE or Cloud Run. It works through a policy-based model where images must have cryptographic attestations confirming they passed predefined requirements such as vulnerability scans, code reviews, or build pipeline verification. Continuous validation (CV) monitors running pods against policies and logs violations.
bash# Enable required APIs gcloud services enable binaryauthorization.googleapis.com gcloud services enable containeranalysis.googleapis.com gcloud services enable container.googleapis.com # Enable Binary Authorization on GKE cluster gcloud container clusters update CLUSTER_NAME \ --enable-binauthz \ --zone us-central1-a
bash# Create keyring gcloud kms keyrings create binauthz-keyring \ --location global # Create signing key gcloud kms keys create attestor-key \ --keyring binauthz-keyring \ --location global \ --algorithm ec-sign-p256-sha256 \ --purpose asymmetric-signing
bashcat > /tmp/note.json << 'EOF' { "attestation": { "hint": { "humanReadableName": "Production Build Attestor" } } } EOF curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ "https://containeranalysis.googleapis.com/v1/projects/PROJECT_ID/notes/?noteId=prod-build-note" \ -d @/tmp/note.json
bashgcloud container binauthz attestors create prod-build-attestor \ --attestation-authority-note=prod-build-note \ --attestation-authority-note-project=PROJECT_ID # Add KMS key to attestor gcloud container binauthz attestors public-keys add \ --attestor=prod-build-attestor \ --keyversion-project=PROJECT_ID \ --keyversion-location=global \ --keyversion-keyring=binauthz-keyring \ --keyversion-key=attestor-key \ --keyversion=1
yaml# binauthz-policy.yaml admissionWhitelistPatterns: - namePattern: "gcr.io/google_containers/*" - namePattern: "gcr.io/google-containers/*" - namePattern: "k8s.gcr.io/**" - namePattern: "gke.gcr.io/**" - namePattern: "gcr.io/stackdriver-agents/*" defaultAdmissionRule: evaluationMode: REQUIRE_ATTESTATION enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG requireAttestationsBy: - projects/PROJECT_ID/attestors/prod-build-attestor globalPolicyEvaluationMode: ENABLE
bashgcloud container binauthz policy import binauthz-policy.yaml
yamladmissionWhitelistPatterns: - namePattern: "gcr.io/google_containers/*" clusterAdmissionRules: us-central1-a.production-cluster: evaluationMode: REQUIRE_ATTESTATION enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG requireAttestationsBy: - projects/PROJECT_ID/attestors/prod-build-attestor us-central1-a.staging-cluster: evaluationMode: ALWAYS_ALLOW enforcementMode: DRYRUN_AUDIT_LOG_ONLY defaultAdmissionRule: evaluationMode: ALWAYS_DENY enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
bash# Get image digest IMAGE_DIGEST=$(gcloud container images describe \ gcr.io/PROJECT_ID/my-app:latest \ --format='get(image_summary.digest)') # Create attestation gcloud container binauthz attestations sign-and-create \ --artifact-url="gcr.io/PROJECT_ID/my-app@${IMAGE_DIGEST}" \ --attestor="prod-build-attestor" \ --attestor-project="PROJECT_ID" \ --keyversion-project="PROJECT_ID" \ --keyversion-location="global" \ --keyversion-keyring="binauthz-keyring" \ --keyversion-key="attestor-key" \ --keyversion="1"
yaml# cloudbuild.yaml steps: - name: 'gcr.io/cloud-builders/docker' args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-app:$SHORT_SHA', '.'] - name: 'gcr.io/cloud-builders/docker' args: ['push', 'gcr.io/$PROJECT_ID/my-app:$SHORT_SHA'] # Vulnerability scanning - name: 'gcr.io/cloud-builders/gcloud' entrypoint: 'bash' args: - '-c' - | gcloud artifacts docker images scan \ gcr.io/$PROJECT_ID/my-app:$SHORT_SHA \ --format='value(response.scan)' # Create attestation after successful scan - name: 'gcr.io/cloud-builders/gcloud' entrypoint: 'bash' args: - '-c' - | IMAGE_DIGEST=$(gcloud container images describe \ gcr.io/$PROJECT_ID/my-app:$SHORT_SHA \ --format='get(image_summary.digest)') gcloud container binauthz attestations sign-and-create \ --artifact-url="gcr.io/$PROJECT_ID/my-app@$${IMAGE_DIGEST}" \ --attestor="prod-build-attestor" \ --attestor-project="$PROJECT_ID" \ --keyversion-project="$PROJECT_ID" \ --keyversion-location="global" \ --keyversion-keyring="binauthz-keyring" \ --keyversion-key="attestor-key" \ --keyversion="1"
bash# Enable CV on a GKE cluster gcloud container clusters update CLUSTER_NAME \ --enable-binauthz-monitoring \ --zone us-central1-a
resource.type="k8s_cluster"
logName="projects/PROJECT_ID/logs/binaryauthorization.googleapis.com%2Fcontinuous_validation"bash# This should be blocked kubectl run test-unapproved \ --image=docker.io/library/nginx:latest # Verify the pod was denied kubectl get events --field-selector reason=FailedCreate
bashgcloud container binauthz attestations list \ --attestor=prod-build-attestor \ --attestor-project=PROJECT_ID
For emergency deployments bypassing Binary Authorization:
yamlapiVersion: v1 kind: Pod metadata: name: emergency-pod labels: image-policy.k8s.io/break-glass: "true" annotations: alpha.image-policy.k8s.io/break-glass: "Emergency deployment - ticket INC-12345" spec: containers: - name: emergency image: gcr.io/PROJECT_ID/emergency-fix:latest
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted, and 22 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +35 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.