Chapter 37

Supply Chain Security

Scope. Binary Authorization already has two owners: §9.27 owns GKE enforcement and §25.10 owns the deploy-time gate. This chapter owns attestor design and attestation authoring only, plus SLSA in depth (§37.10). Chapter 22 owns source, Chapter 23 the build, Chapter 24 the artifact at rest, and Chapter 25 deployment; each is cited rather than restated. §23.14 already states Cloud Build's published SLSA position and this chapter does not contradict it. Prerequisites. Chapter 22, Chapter 23 (§23.13, §23.14), Chapter 24 (§24.5), Chapter 25 (§25.10, §25.12, §25.13), §9.27, Chapter 14 (Cloud KMS). Verified against. Google Cloud console and API surface as of 2026-09, Cloud SDK 583.0.0; every command resolved with --help; SLSA specification version confirmed; see sources at end.

A supply chain attack succeeds by compromising something you trust rather than something you defend. The artifact is signed, the pipeline is green, the deployment is authorized — and the code is not what anyone reviewed. Every control in this chapter exists to make one link in that chain verifiable by someone other than the party that produced it.

The chapter's centre of gravity is attestation, because it is the only mechanism here that produces a portable, cryptographically checkable claim. A scan result is a report; a provenance record is a claim by the builder about itself; an attestation is a signed statement by a named party that a specific artifact met a specific condition, verifiable by anyone with the public key.

Which makes attestor design the real subject. The machinery is well documented; the question that decides whether it means anything is who is allowed to attest, and what their signature asserts — a separation-of-duties problem, not a cryptography one.

One correction of scope up front. There is no generally available first-party image signing command; gcloud artifacts docker images sign does not exist. Signing on Google Cloud is the Binary Authorization attestation path described in §37.6, or an external tool.

37.1 Source Integrity §

Chapter 22 owns source hosting, branching, review, and commit signing. This section owns the supply-chain-specific part: protecting the thing that defines the build.

The build configuration is in the repository, which means a pull request can change the build. That is the concrete threat, and it is more direct than any dependency attack: a contributor who can modify cloudbuild.yaml can make the pipeline do anything the build identity can do, and the change looks like an ordinary code change.

Three controls, in increasing strength:

ControlWhat it prevents
Path-scoped review on the build configurationAn unreviewed change to the build
--require-approval on the triggerAny build running without a human decision
An inline build config on the triggerThe repository defining the build at all

--comment-control is the fork-originated-build control. A trigger on pull requests from forks executes attacker-supplied code by design; the flag gates whether that happens automatically or requires a comment from a trusted party. §22.x owns the credential rule — fork builds run with none and gate nothing.

Commit signing verification, not just signing. §22.x owns producing signatures; the supply chain control is a branch protection rule that rejects an unsigned or unverified commit. Signing that nothing checks is decoration.

The two-person rule is the strongest source control and the one SLSA's Source track measures (§37.10). It is also organizational rather than technical, which is why it is the one most often claimed and least often enforced.

Pitfall. Protecting the default branch and leaving tags unprotected routes around the whole model, because a tag-triggered pipeline builds whatever the tag points at. §22.x states this; the supply chain consequence is that the artifact's provenance will faithfully record a build of unreviewed code.

37.2 Dependency Integrity §

Most code in an artifact was written by someone else, and the controls here are about where it came from rather than what it contains.

Artifact Registry's repository modes are the first-party control, and the values are lowercase in the CLI:

ModePurpose
standard-repositoryYour own artifacts
remote-repositoryA caching proxy for an upstream public registry
virtual-repositoryOne endpoint fronting several repositories

A remote repository turns a transitive public dependency into a retained local artifact. The upstream can delete a version and your builds keep working — and the copy you hold is the copy that was scanned.

Virtual repositories introduce an ordering problem that is a real attack. A virtual repository resolves a name across its upstreams in a configured order. If a private package name can also be resolved from a public upstream, an attacker who publishes that name publicly may be served instead of your internal package. Configure the upstream priority so internal repositories are searched first, and prefer scoped or namespaced package names that cannot collide.

Lockfiles are the other half and they are not enforced by any GCP flag. There is no --require-lockfile; the control is a build step that fails when the lockfile is absent or does not match, which is Chapter 23's material.

Artifact Analysis (§24.x, §29.7) tells you what is in a dependency; this section is about controlling which one you got. Neither substitutes for the other.

Pitfall. A remote repository that falls through to the public upstream on a cache miss reintroduces the dependency on upstream availability and integrity at exactly the moment something unusual is happening. Know whether yours is a cache or a mirror, because they fail differently.

37.3 Build Isolation §

SLSA's build requirements are about isolation, and the word does not mean what it is usually taken to mean.

"Isolated" and "hermetic" are different properties, and the SLSA specification says so explicitly — isolated "is not to be confused with 'Hermetic'". Isolation means the build cannot be influenced by other builds or by the user; hermeticity means it declares all its inputs and fetches nothing at build time.

Hermetic builds are not required at any SLSA Build level. That is worth stating because teams regularly believe they have failed a level they have in fact met, and expend effort on hermeticity that would be better spent on §37.1's source controls.

What Cloud Build provides is the isolation half: each build runs on its own worker, isolated from other workloads. §23.x owns the platform's architecture; the hardening levers are §32.9's.

The gap is network access at build time, which is what makes a Cloud Build build non-hermetic by default. A private pool with --no-public-egress narrows it to a controlled path, and a remote repository (§37.2) makes what comes through that path a retained artifact rather than a live fetch.

Isolation is what makes provenance meaningful. If a build can be influenced by the user requesting it, the provenance is a statement the requester can shape — which is why SLSA's levels are about the platform and not about your pipeline (§23.14).

Pitfall. A build that pulls an image from a public registry and retags it produces provenance saying Cloud Build produced it — because Cloud Build did produce that push. §23.14's pitfall is the same observation: the assurance is about the pipeline's integrity, never about the artifact's origin.

37.4 Artifact Integrity §

An artifact's identity is its digest. Every control downstream depends on that being the reference actually used.

A tag is a mutable pointer and a digest is the content. §24.5's immutable tags close part of the gap and are Docker-format only, so for Maven, npm, and every other format the digest is the only durable identity.

Reference by digest everywhere it matters:

PlaceWhy
A deployment manifestThe policy evaluates what is presented (§25.10)
A provenance subjectThe claim is about a specific artifact
An attestationOtherwise it attests to whatever the tag points at now
A base image in a DockerfileOtherwise the build is not reproducible

Inspect what a registry actually holds:

gcloud artifacts docker images describe \
  us-central1-docker.pkg.dev/rc-saas-shared-art-01/prod-docker/billing@sha256:DIGEST \
  --show-provenance

Cleanup policies can delete an artifact something still needs (§32.10), and a Keep rule always beats a Delete rule. An artifact a deployment or a rollback needs should be covered by a Keep rule, not merely be recent.

Pitfall. Digest references are unreadable, so teams add a tag alongside "for clarity" and then someone uses the tag. If both appear in a manifest, only one of them is the control — and the tag is the one a human will edit.

37.5 Provenance §

§23.13 owns generating provenance and §25.12 owns verifying it. This section owns the format, because verification that does not understand the format checks a signature and not a claim.

Three nested standards, and each does one job:

LayerWhat it is
in-toto attestationThe envelope shape: _type, subject, predicateType, predicate
SLSA Provenance predicateThe build claim: how, by whom, from what
DSSEThe signing envelope: payloadType, payload, signatures

The subject binds the claim to an artifact by digest, which is why §37.4 matters: a provenance record whose subject is a tag asserts nothing durable.

The SLSA v1 predicate has two halves. buildDefinition records what was requested — the build type, the external parameters, and the resolved dependencies. runDetails records what happened — the builder's identity, metadata, and byproducts. The builder identity in runDetails is the claim's author, and verification means checking that it is a builder you trust, not merely that the document parses.

Cloud Build emits both slsa.dev/provenance/v1 and slsa.dev/provenance/v0.1. Not v0.2 — a verifier written against v0.2 will not match, and this is a specific and easy mistake. §23.14 records Cloud Build's published position and this chapter does not restate it.

Provenance is only generated for artifacts stored in Artifact Registry, which is the practical boundary on where this control applies.

Pitfall. Provenance is a claim by the builder about itself. It is trustworthy because the build platform is isolated and you trust the platform (§37.3), not because it is signed — a compromised builder produces perfectly valid provenance. That is precisely why §37.6's attestation, signed by a different party, adds something provenance cannot.

37.6 Signing §

Signing on Google Cloud means creating a Binary Authorization attestation. There is no first-party image signing command, and the sequence below is the generally available path.

The key must be an asymmetric signing key:

gcloud kms keys create k-attestor-prod \
  --location=us-central1 --keyring=kr-us-central1-attest \
  --purpose=asymmetric-signing \
  --default-algorithm=ec-sign-p256-sha256

Then the three-step signing sequence, which is the part most often gotten wrong:

gcloud container binauthz create-signature-payload \
  --artifact-url="$IMAGE_DIGEST_URL" > payload.json

gcloud kms asymmetric-sign \
  --location=us-central1 --keyring=kr-us-central1-attest \
  --key=k-attestor-prod --version=1 \
  --digest-algorithm=sha256 \
  --input-file=payload.json --signature-file=payload.sig

gcloud container binauthz attestations create \
  --artifact-url="$IMAGE_DIGEST_URL" \
  --attestor=attestor-prod-release \
  --payload-file=payload.json \
  --signature-file=payload.sig \
  --public-key-id="$PUBLIC_KEY_ID" \
  --validate

--public-key-id must be read back from the attestor, not constructed from the key's name. Binary Authorization assigns it — a ni:///sha-256;... identifier — when the key is added, and a guessed value fails.

--validate checks the attestation against the attestor before creating it, which turns a silent mismatch into an error at the point of signing rather than at the point of deployment.

attestations sign-and-create collapses the three steps into one and is beta-only. It is convenient and it is a Preview surface, so it must not be a production dependency.

Pitfall. --artifact-url must be a digest reference. Signing a tag produces an attestation that Binary Authorization will not match at admission, because admission evaluates the resolved digest — the signature is valid and useless.

37.7 Binary Authorization §

§9.27 owns GKE enforcement and §25.10 owns the policy object, its enums, and dry-run. This section owns attestor design, which is the question those two leave open: who signs, and what does the signature mean.

An attestor is a name plus a Container Analysis note plus one or more public keys:

gcloud container binauthz attestors create attestor-prod-release \
  --attestation-authority-note=note-prod-release \
  --attestation-authority-note-project=rc-saas-shared-sec-01

gcloud container binauthz attestors public-keys add \
  --attestor=attestor-prod-release \
  --keyversion-project=rc-saas-shared-sec-01 \
  --keyversion-location=us-central1 \
  --keyversion-keyring=kr-us-central1-attest \
  --keyversion-key=k-attestor-prod \
  --keyversion=1

The Container Analysis note has no gcloud command. There is no gcloud container analysis group; the note is created through the Container Analysis REST API. That is a genuine gap in the CLI surface and the first thing that stops an automation attempt.

Attestor design is separation of duties, and it is the whole point:

AttestorSigned byAsserts
attestor-buildThe build pipelineThis artifact was built by our pipeline
attestor-scanThe scanning stageThis artifact passed vulnerability policy
attestor-prod-releaseA release approverA human authorized this for production

Three attestors with three distinct signers feeding one admission gate. The build pipeline, running as sa-build-<app>, signs attestor-build. The scanning stage, running as sa-scan, signs attestor-scan. A human release approver signs attestor-prod-release. All three attestations flow into an admission gate that requires all three, so no single identity can produce a deployable image on its own.

If the build identity holds the signing key, the attestation proves only that a build ran — which the provenance already said (§37.5). The attestation adds value exactly to the extent that a different party signed it, which means roles/cloudkms.signerVerifier on the release key must not be held by sa-build-<app> (§25.13).

roles/containeranalysis.notes.attacher is the least-privilege role for an attesting identity — it can attach an occurrence to a note without being able to modify the note itself.

Require multiple attestors in the policy and the compromise of any one signing identity is insufficient to deploy.

Pitfall. An attestor with a key nobody controls carefully is a signature with no meaning. The security of the whole chain reduces to the IAM on the KMS key version, and if the pipeline can sign anything then the gate checks that the pipeline ran.

37.8 Admission Policies §

Binary Authorization is one admission controller among several, and a complete admission story uses more than one.

MechanismWhat it checksLicensing
Binary AuthorizationImage provenance and attestationsIncluded
Pod Security AdmissionPod-level security context, in-treeIncluded
ValidatingAdmissionPolicyArbitrary CEL rules, in-treeIncluded
Policy Controller / GatekeeperRich policy, constraint templatesGKE Enterprise

Binary Authorization answers "may this image run"; the others answer "may this workload be configured this way". They are not substitutes: an attested image running as root with a host mount is admitted by Binary Authorization and should be rejected by the others.

ValidatingAdmissionPolicy is the notable addition because it is in-tree and CEL-based, so a meaningful set of rules is available without a separately licensed product. Policy Controller is a GKE Enterprise feature and carries a licensing cost; presenting it as freely available is a real budgeting error.

Layer them in order of cheapness: Pod Security Admission at the namespace catches the largest class nearly free; a few ValidatingAdmissionPolicy rules catch the organization-specific cases; Binary Authorization catches the artifact question none of the others can see.

Pitfall. Every admission controller is bypassed by anything that does not go through admission — a workload started by a node-level process, a static pod, a resource created before the policy existed. Admission control governs new admissions, so deploying a policy leaves the existing fleet unevaluated (§37.9).

37.9 Runtime Verification §

Admission is a moment; runtime is a duration. Everything checked at admission can change afterward, and this section is the gap.

Three things change after a workload is admitted:

  1. The policy changes — a new rule would have rejected what is already running.
  2. The artifact's status changes — a vulnerability is disclosed in an image that was clean at admission (§29.7).
  3. The workload changes — a process is injected, a binary is replaced, an operator execs in.

Binary Authorization continuous validation addresses the first two by re-evaluating running workloads against the current policy. Two properties decide how you use it: it is Preview, so it must not be a production dependency, and it only writes to Cloud Logging — it does not block. Continuous validation is a detection control, and treating it as enforcement is a category error.

The third is Container Threat Detection's territory (§16.6), which instruments the guest kernel and sees process and script behavior inside containers. Notably it is unaffected by confidential nodes (§36.3), which makes it the runtime control that survives that trade.

GKE's --security-posture and --workload-vulnerability-scanning both take disabled, standard, and enterprise, and provide the continuous vulnerability view for running workloads rather than for stored artifacts.

Pitfall. The most common runtime gap is not exotic: a policy deployed today evaluates deployments made today, and the workloads admitted last year keep running whatever they were admitted with. Re-deploying the fleet after a policy change is the only thing that actually applies it, and continuous validation tells you which workloads still need it.

37.10 SLSA Architecture §

SLSA is a set of tracks and levels describing how resistant a supply chain is to tampering. §23.14 states Cloud Build's published position and this section does not contradict it — it supplies the framework that position sits inside.

The current specification is v1.2 and it has more than one track. The Build track is the one usually meant; a Source track is now published with Approved status, which is the fact most likely to be stale in an existing control mapping.

TrackLevelsWhat it measures
BuildL1–L3How resistant the build platform is to tampering
SourceL1–L4How resistant the source revision process is

The Build track's levels, in outline: L1 requires provenance to exist; L2 requires it to be signed by a hosted build platform; L3 requires the platform to prevent runs from influencing one another and to make the provenance non-forgeable. Hermeticity is not required at any Build level (§37.3).

The Source track's top level requires two trusted persons to review a revision — the two-person rule from §37.1, now with a level number attached. This is the track that turns "we do code review" into a claim with a defined meaning.

Map the requirements to the book:

RequirementOwned by
Provenance exists and is signed§23.13
Build platform is isolated§37.3, §32.9
Provenance is verified before deploy§25.12
Source is protected and reviewed§22.x, §37.1
Dependencies are controlled§37.2
Artifacts identified by digest§37.4

State the level honestly. §23.14's rule is the right one: the answer is "our build platform provides level 3 provenance for artifacts in Artifact Registry, and here is our source and verification posture" — not a bare number. A SLSA level is a property of a specific artifact produced by a specific pipeline, and a claim made about an organization is not a claim SLSA defines.

Pitfall. SLSA levels are used in procurement questionnaires, and the number is asked for without the track. "SLSA 3" now means something different depending on whether the questioner means the Build track or has read the Source track — so answer with the track and the version, or the answer is ambiguous in a document that will be relied on.

Chapter Summary §

  • An attestation is the only portable, independently checkable claim in this chapter; provenance is the builder's claim about itself and a scan is a report.
  • There is no generally available gcloud artifacts docker images sign. Signing is the Binary Authorization attestation path or an external tool.
  • The build configuration lives in the repository, so a pull request can change the build — path-scoped review, --require-approval, and an inline config are the escalating controls.
  • Commit signing that nothing verifies is decoration; the control is a protection rule that rejects unverified commits.
  • Artifact Registry --mode values are lowercase; a virtual repository's upstream ordering is a real dependency-confusion control.
  • "Isolated" is not "hermetic", and hermetic builds are not required at any SLSA Build level.
  • A build that retags a public image produces valid provenance saying Cloud Build produced it — the assurance is about the pipeline, never the artifact's origin.
  • Provenance nests three standards: in-toto for the envelope, the SLSA predicate for the claim, DSSE for the signature.
  • Cloud Build emits slsa.dev/provenance/v1 and slsa.dev/provenance/v0.1 — not v0.2. A verifier written against v0.2 will not match.
  • The attestation key must be --purpose=asymmetric-signing, and the sequence is create-signature-payload, kms asymmetric-sign, attestations create.
  • --public-key-id must be read back from the attestor, not constructed from the key name.
  • attestations sign-and-create is beta-only and must not be a production dependency.
  • The Container Analysis note has no gcloud command — it is created through the REST API.
  • If the build identity holds the signing key, the attestation proves only that a build ran. Value comes from a different party signing.
  • Binary Authorization answers "may this image run"; Pod Security Admission and ValidatingAdmissionPolicy answer "may it be configured this way". Policy Controller is a GKE Enterprise feature.
  • Binary Authorization continuous validation is Preview and only logs — it does not block. It is detection, not enforcement.
  • A policy deployed today does not evaluate workloads admitted yesterday; re-deployment is what applies it.
  • The current SLSA specification is v1.2 and the Source track is published, with its top level requiring two-person review.
  • State a SLSA level with its track and version, and as a property of a specific artifact from a specific pipeline.

Security Checklist §

ControlWhy it mattersHow to verify (CLI + Console)
Build configuration under path-scoped reviewA pull request can otherwise change the buildRepository code-owners rules
Fork-originated builds gated by --comment-controlThey execute attacker-supplied code by designgcloud builds triggers describe TRIGGER --region=REGION
Branch protection rejects unverified commitsSigning that nothing checks is decorationRepository protection rules
Tags protected as well as branchesA tag-triggered build routes around branch protectionRepository protection rules
Virtual repository upstream order puts internal firstDependency confusion is a resolution-order attackgcloud artifacts repositories describe REPO --location=REGION
Private pool restricts build egressIt is what narrows the non-hermetic surfacegcloud builds worker-pools describe POOL --region=REGION
Everything references artifacts by digestTags move; policies evaluate the resolved digestgrep -rE 'image:.*:' manifests/ returns nothing
Cleanup policy Keep rules cover deployed artifactsA policy can delete a rollback targetgcloud artifacts repositories describe REPO --location=REGION
Attestation key is asymmetric-signingA symmetric key cannot produce a verifiable signaturegcloud kms keys describe KEY --keyring=RING --location=REGION
--public-key-id read from the attestorA constructed value failsgcloud container binauthz attestors describe ATTESTOR
Signing identity differs from build identityOtherwise the attestation restates the provenancegcloud kms keys get-iam-policy KEY --keyring=RING --location=REGION
Policy requires more than one attestorOne compromised signing identity should not sufficegcloud container binauthz policy export
--validate used when creating attestationsTurns a silent mismatch into an error at signing timeThe signing pipeline's commands
Pod Security Admission set on every namespaceIt catches the largest class nearly freekubectl get ns -o yaml | grep pod-security
Policy Controller's licensing accounted forIt is a GKE Enterprise featureFleet entitlement
Continuous validation treated as detectionIt is Preview and only logsAlerting on its Cloud Logging output
Fleet re-deployed after a policy changeAdmission does not evaluate what is already runningWorkload creation timestamps versus policy date
SLSA claims state track, version, and artifactA bare number is ambiguous under v1.2The published attestation and the questionnaire response

Sources §