Binary Authorization is a GCP security service that enforces deploy-time policy on which container images are allowed to run on GKE clusters. It acts as a Kubernetes admission controller: when a pod is submitted for scheduling, Binary Authorization checks whether the container image has been signed by a trusted attestor. If the attestation is missing or invalid, the pod is rejected before it ever starts. This closes a critical gap in the software supply chain—even if an attacker pushes a malicious image to Artifact Registry, it cannot run on a cluster protected by Binary Authorization unless the attacker also compromises the signing key.
Sigstore Cosign provides the signing mechanism. Cosign supports keyless signing using OIDC identity tokens, which means CI/CD pipelines can sign images using the identity of the GitHub Actions workflow or Cloud Build service account without managing long-lived signing keys. The signature is recorded as an OCI artifact alongside the image in Artifact Registry, and a transparency log (Rekor) provides a public, immutable record of every signing event. This combination of keyless signing and transparency logging eliminates the key management burden while providing stronger provenance guarantees than traditional GPG-based signing.
In a production cloud-native system, the infrastructure repository configures Binary Authorization policies that require all production container images to have a valid attestation from the CI/CD pipeline’s attestor. A SOC2 readiness report documents this as a supply chain security control that satisfies requirements around container provenance, build integrity, and deployment governance.
An attestation is a cryptographic statement that a specific container image (identified by its digest, not tag) has passed a required check. In the Binary Authorization model, an attestor signs the image digest using a private key, and the Binary Authorization admission controller verifies the signature against the attestor’s registered public key. Attestations are stored as Container Analysis notes and occurrences in GCP’s Artifact Analysis API, making them queryable and auditable.
Cosign’s keyless mode eliminates the need to manage signing keys. Instead, the signer authenticates via OIDC (e.g., GitHub Actions’ OIDC provider or GCP’s Workload Identity), and Sigstore’s Fulcio CA issues a short-lived signing certificate bound to that identity. The signature, certificate, and signing event are recorded in the Rekor transparency log. Verification checks the certificate chain back to Fulcio’s root, confirms the OIDC identity matches the expected CI/CD pipeline, and validates the Rekor inclusion proof.
A Binary Authorization policy is a GCP resource that defines the default admission rule and optional per-cluster overrides. The default rule specifies whether images require attestation, which attestors are trusted, and what enforcement mode to use (ENFORCED blocks non-compliant images; DRYRUN_AUDIT_LOG_ONLY logs violations without blocking). A production setup sets ENFORCED for production clusters and DRYRUN_AUDIT_LOG_ONLY for development, allowing dev teams to iterate without being blocked by signing requirements.
GCP’s Container Analysis (also called Artifact Analysis) stores metadata about container images as notes (the definition of what an attestation means) and occurrences (individual attestation events). When Cosign signs an image for Binary Authorization, it creates an occurrence linking the image digest to the attestor’s note. The Binary Authorization admission controller queries these occurrences at deploy time to verify that the required attestation exists.
Binary Authorization operates on image digests (e.g., sha256:abc123...), not tags (e.g., :latest, :v1.2.3). This is critical because tags are mutable—an attacker who gains push access to a registry can retag a malicious image with a trusted tag. Digest pinning ensures that the exact bytes of the image are what was attested. A production CI/CD pipeline always deploys by digest, and the Binary Authorization policy rejects any image reference that uses a tag instead of a digest.
The end-to-end flow involves three phases: (1) image building and signing in CI/CD, (2) attestation creation, and (3) admission enforcement at deploy time.
After building and pushing a container image, the CI/CD pipeline signs it with Cosign using keyless mode. The --fulcio-url and --rekor-url flags point to Sigstore’s public infrastructure.
#!/bin/bash
# CI/CD pipeline step: build, push, and sign container image
IMAGE="us-docker.pkg.dev/myapp-prod/services/search-api"
TAG="$(git rev-parse --short HEAD)"
# Build and push
docker build -t "${IMAGE}:${TAG}" .
docker push "${IMAGE}:${TAG}"
# Get the image digest (immutable identifier)
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' "${IMAGE}:${TAG}")
echo "Image digest: ${DIGEST}"
# Sign with Cosign keyless (uses OIDC identity from CI environment)
cosign sign --yes \
--fulcio-url=https://fulcio.sigstore.dev \
--rekor-url=https://rekor.sigstore.dev \
"${DIGEST}"
# Verify the signature
cosign verify \
--certificate-identity-regexp=".*@myapp\\.iam\\.gserviceaccount\\.com" \
--certificate-oidc-issuer="https://accounts.google.com" \
"${DIGEST}"
echo "Image signed and verified: ${DIGEST}"
The infrastructure project provisions the Binary Authorization policy, attestor, and Container Analysis note as Pulumi resources. The attestor references a KMS key for verification (even with keyless signing, the attestation record is stored in Container Analysis and linked to the attestor).
package infra
import (
"fmt"
"github.com/pulumi/pulumi-gcp/sdk/v7/go/gcp/binaryauthorization"
"github.com/pulumi/pulumi-gcp/sdk/v7/go/gcp/containeranalysis"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func NewBinaryAuthPolicy(ctx *pulumi.Context, env, project string) error {
// Create the Container Analysis note that defines the attestation type
note, err := containeranalysis.NewNote(ctx, env+"-build-note", &containeranalysis.NoteArgs{
Name: pulumi.String(fmt.Sprintf("%s-build-attestation", env)),
Project: pulumi.String(project),
AttestationAuthority: &containeranalysis.NoteAttestationAuthorityArgs{
Hint: &containeranalysis.NoteAttestationAuthorityHintArgs{
HumanReadableName: pulumi.String(
fmt.Sprintf("CI/CD Build Attestor for %s", env),
),
},
},
})
if err != nil {
return err
}
// Create the attestor that references the note
attestor, err := binaryauthorization.NewAttestor(ctx, env+"-build-attestor",
&binaryauthorization.AttestorArgs{
Name: pulumi.String(fmt.Sprintf("%s-build-attestor", env)),
Project: pulumi.String(project),
AttestationAuthorityNote: &binaryauthorization.AttestorAttestationAuthorityNoteArgs{
NoteReference: note.Name.ApplyT(func(name string) string {
return fmt.Sprintf("projects/%s/notes/%s", project, name)
}).(pulumi.StringOutput),
},
},
)
if err != nil {
return err
}
// Configure the Binary Authorization policy
enforcementMode := "ENFORCED"
if env == "dev" {
enforcementMode = "DRYRUN_AUDIT_LOG_ONLY"
}
_, err = binaryauthorization.NewPolicy(ctx, env+"-binauth-policy",
&binaryauthorization.PolicyArgs{
Project: pulumi.String(project),
DefaultAdmissionRule: &binaryauthorization.PolicyDefaultAdmissionRuleArgs{
EvaluationMode: pulumi.String("REQUIRE_ATTESTATION"),
EnforcementMode: pulumi.String(enforcementMode),
RequireAttestationsBies: pulumi.StringArray{
attestor.Name.ApplyT(func(name string) string {
return fmt.Sprintf("projects/%s/attestors/%s", project, name)
}).(pulumi.StringOutput),
},
},
GlobalPolicyEvaluationMode: pulumi.String("ENABLE"),
},
)
return err
}
After signing with Cosign, the CI/CD pipeline creates an attestation occurrence in Container Analysis. This is what Binary Authorization checks at deploy time.
#!/bin/bash
# Create Binary Authorization attestation after Cosign signing
PROJECT="myapp-prod"
ATTESTOR="prod-build-attestor"
DIGEST="sha256:abc123def456..."
# Create the attestation using gcloud
gcloud beta container binauthz attestations sign-and-create \
--project="${PROJECT}" \
--artifact-url="us-docker.pkg.dev/${PROJECT}/services/search-api@${DIGEST}" \
--attestor="${ATTESTOR}" \
--attestor-project="${PROJECT}" \
--keyversion="projects/${PROJECT}/locations/us/keyRings/binauth/cryptoKeys/attestor-key/cryptoKeyVersions/1"
# Verify attestation exists
gcloud beta container binauthz attestations list \
--project="${PROJECT}" \
--attestor="${ATTESTOR}" \
--artifact-url="us-docker.pkg.dev/${PROJECT}/services/search-api@${DIGEST}"
For high-security environments, the infrastructure project supports requiring multiple attestations before an image can deploy. The build attestor signs after a successful build, a vulnerability scanner attestor signs after the image passes CVE scanning, and a manual approval attestor signs after a human reviews the deployment. The Binary Authorization policy can require all three, creating a chain of trust that ensures images are built correctly, scanned for vulnerabilities, and approved by a human before reaching production.
Binary Authorization supports a break-glass mechanism that allows deploying unsigned images in emergencies. When a pod annotation includes alpha.image-policy.k8s.io/break-glass: "true", Binary Authorization allows the deployment but logs a break-glass event to Cloud Audit Logs. A production setup monitors these events with a Cloud Monitoring alert that pages the security team whenever break-glass is used, ensuring accountability even during emergencies.
A production rollout deploys Binary Authorization in phases: first in DRYRUN_AUDIT_LOG_ONLY mode for 2 weeks, monitoring Cloud Audit Logs for violations that would have been blocked. The team identifies and fixes any unsigned images (often third-party sidecar containers or init containers), adds attestations for all legitimate images, and then switches to ENFORCED mode. This prevents outages caused by legitimate images being unexpectedly blocked.
Binary Authorization and container signing secure a production deployment pipeline in the following ways:
globalPolicyEvaluationMode: ENABLE setting, which trusts Google-provided system images.:latest) instead of digest, an attacker can retag a malicious image and it may pass attestation checks if the original tag was attested. Always deploy by digest in production.