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:
| Control | What it prevents |
|---|---|
| Path-scoped review on the build configuration | An unreviewed change to the build |
--require-approval on the trigger | Any build running without a human decision |
| An inline build config on the trigger | The 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:
| Mode | Purpose |
|---|---|
standard-repository | Your own artifacts |
remote-repository | A caching proxy for an upstream public registry |
virtual-repository | One 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:
| Place | Why |
|---|---|
| A deployment manifest | The policy evaluates what is presented (§25.10) |
| A provenance subject | The claim is about a specific artifact |
| An attestation | Otherwise it attests to whatever the tag points at now |
| A base image in a Dockerfile | Otherwise 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:
| Layer | What it is |
|---|---|
| in-toto attestation | The envelope shape: _type, subject, predicateType, predicate |
| SLSA Provenance predicate | The build claim: how, by whom, from what |
| DSSE | The 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:
| Attestor | Signed by | Asserts |
|---|---|---|
attestor-build | The build pipeline | This artifact was built by our pipeline |
attestor-scan | The scanning stage | This artifact passed vulnerability policy |
attestor-prod-release | A release approver | A human authorized this for production |

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.
| Mechanism | What it checks | Licensing |
|---|---|---|
| Binary Authorization | Image provenance and attestations | Included |
| Pod Security Admission | Pod-level security context, in-tree | Included |
ValidatingAdmissionPolicy | Arbitrary CEL rules, in-tree | Included |
| Policy Controller / Gatekeeper | Rich policy, constraint templates | GKE 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:
- The policy changes — a new rule would have rejected what is already running.
- The artifact's status changes — a vulnerability is disclosed in an image that was clean at admission (§29.7).
- 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.
| Track | Levels | What it measures |
|---|---|---|
| Build | L1–L3 | How resistant the build platform is to tampering |
| Source | L1–L4 | How 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:
| Requirement | Owned 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
--modevalues 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/v1andslsa.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 iscreate-signature-payload,kms asymmetric-sign,attestations create. --public-key-idmust be read back from the attestor, not constructed from the key name.attestations sign-and-createis beta-only and must not be a production dependency.- The Container Analysis note has no
gcloudcommand — 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
ValidatingAdmissionPolicyanswer "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 §
| Control | Why it matters | How to verify (CLI + Console) |
|---|---|---|
| Build configuration under path-scoped review | A pull request can otherwise change the build | Repository code-owners rules |
Fork-originated builds gated by --comment-control | They execute attacker-supplied code by design | gcloud builds triggers describe TRIGGER --region=REGION |
| Branch protection rejects unverified commits | Signing that nothing checks is decoration | Repository protection rules |
| Tags protected as well as branches | A tag-triggered build routes around branch protection | Repository protection rules |
| Virtual repository upstream order puts internal first | Dependency confusion is a resolution-order attack | gcloud artifacts repositories describe REPO --location=REGION |
| Private pool restricts build egress | It is what narrows the non-hermetic surface | gcloud builds worker-pools describe POOL --region=REGION |
| Everything references artifacts by digest | Tags move; policies evaluate the resolved digest | grep -rE 'image:.*:' manifests/ returns nothing |
| Cleanup policy Keep rules cover deployed artifacts | A policy can delete a rollback target | gcloud artifacts repositories describe REPO --location=REGION |
Attestation key is asymmetric-signing | A symmetric key cannot produce a verifiable signature | gcloud kms keys describe KEY --keyring=RING --location=REGION |
--public-key-id read from the attestor | A constructed value fails | gcloud container binauthz attestors describe ATTESTOR |
| Signing identity differs from build identity | Otherwise the attestation restates the provenance | gcloud kms keys get-iam-policy KEY --keyring=RING --location=REGION |
| Policy requires more than one attestor | One compromised signing identity should not suffice | gcloud container binauthz policy export |
--validate used when creating attestations | Turns a silent mismatch into an error at signing time | The signing pipeline's commands |
| Pod Security Admission set on every namespace | It catches the largest class nearly free | kubectl get ns -o yaml | grep pod-security |
| Policy Controller's licensing accounted for | It is a GKE Enterprise feature | Fleet entitlement |
| Continuous validation treated as detection | It is Preview and only logs | Alerting on its Cloud Logging output |
| Fleet re-deployed after a policy change | Admission does not evaluate what is already running | Workload creation timestamps versus policy date |
| SLSA claims state track, version, and artifact | A bare number is ambiguous under v1.2 | The published attestation and the questionnaire response |
Sources §
- https://slsa.dev/spec/v1.2/build-requirements — the Build track levels and their requirements (last validated 2026-09-04)
- https://slsa.dev/spec/v1.2/source-requirements — the Source track and its four levels (last validated 2026-09-04)
- https://slsa.dev/spec/v1.2/about — the current specification version and its track structure (last validated 2026-09-04)
- https://slsa.dev/spec/v1.2/threats — the published supply chain threat model (last validated 2026-09-04)
- https://slsa.dev/provenance/v1 — the SLSA Provenance predicate structure (last validated 2026-09-04)
- https://in-toto.io/Statement/v1 — the in-toto attestation statement envelope and its fields (last validated 2026-09-04)
- https://cloud.google.com/binary-authorization/docs/creating-attestors-cli — attestor creation, notes, and public key management (last validated 2026-09-04)
- https://cloud.google.com/binary-authorization/docs/making-attestations — the signing payload and attestation creation sequence (last validated 2026-09-04)
- https://cloud.google.com/binary-authorization/docs/overview-cv — continuous validation, its launch stage, and that it logs rather than blocks (last validated 2026-09-04)
- https://cloud.google.com/build/docs/securing-builds/view-build-provenance — the provenance formats Cloud Build emits (last validated 2026-09-04)
- https://cloud.google.com/build/docs/private-pools/private-pools-overview — build worker isolation and egress control (last validated 2026-09-04)
- https://cloud.google.com/artifact-registry/docs/repositories/virtual-repo — virtual repositories and upstream resolution order (last validated 2026-09-04)
- https://cloud.google.com/kubernetes-engine/enterprise/policy-controller/docs/overview — Policy Controller and its GKE Enterprise requirement (last validated 2026-09-04)
- https://cloud.google.com/iam/docs/roles-permissions/containeranalysis — the Container Analysis roles, including the attacher role (last validated 2026-09-04)