The assumption that changes the design
A perimeter model asks 'is this request inside the network?' and grants trust on the answer. That question stopped being useful the moment workloads started calling APIs across the internet and engineers started working from home. The replacement assumption is that every layer has already been breached: the WAF has been bypassed, a credential has leaked, an image has been tampered with, someone is inside the VPC. Design each control to be useful given that, and the blast radius of any single failure collapses.
Kill the long-lived credential first
The overwhelming majority of cloud breaches start with a static key — committed to a repo, sitting in a CI variable, pasted into a Slack thread in 2022. Workload Identity Federation removes the object entirely: a workload proves what it is to the identity provider and receives a short-lived token. There is no secret to leak, because there is no secret. This is the single highest-leverage change on the list and it is mostly unglamorous plumbing.
gcloud iam service-accounts keys list --iam-account=SA_EMAIL --managed-by=userAny key returned here is a user-managed static credential with no expiry. The goal is an empty list across every service account in the project — that output is the actual metric.
Assume the credential leaked anyway — VPC Service Controls
Suppose a token does get out. With plain IAM, a valid credential works from anywhere on the internet, which turns credential theft directly into data exfiltration. VPC Service Controls draw a perimeter around the data services themselves: a request carrying a perfectly valid token, arriving from outside the perimeter, is refused. It converts a total compromise into a contained one.
gcloud access-context-manager perimeters describe PERIMETER --policy=POLICY_ID --format='value(status.restrictedServices)'Confirm storage, BigQuery and Secret Manager are inside. A perimeter that omits the service holding the data is decorative.
Only run what you signed
Supply-chain attacks do not need your registry credentials; they need a mutable tag. Binary Authorization enforces at admission that every image carries a valid signature from your build system, so a tampered or unsigned image cannot be scheduled — even by someone who has legitimate deploy access. Combined with signing at build time, the cluster stops trusting the registry and starts trusting the attestation.
cosign verify --key gcpkms://projects/P/locations/L/keyRings/R/cryptoKeys/K IMAGE@sha256:DIGESTVerify by digest, never by tag. A tag is a mutable pointer; the digest is the thing you actually signed.
The network is not a trust boundary
Inside a cluster, flat pod-to-pod networking means one compromised container can reach everything. Istio in STRICT mTLS gives every workload a cryptographic identity and refuses unauthenticated traffic, so lateral movement requires forging a certificate rather than finding an IP. The migration is the delicate part — permissive mode silently accepts plaintext, so you have to verify what is actually encrypted before enforcing.
istioctl authn tls-check $(kubectl get pod -l app=api -o jsonpath='{.items[0].metadata.name}').default | grep -v OKAnything that isn't OK is a plaintext path that STRICT will sever. Find these before enforcing, not from the incident channel afterwards.
Delete the bastion
A jump host is a single point of both failure and compromise, protected by a key that gets shared and never rotated. Short-lived certificate-based access with MFA replaces it: every session is issued on demand, expires on its own, and is recorded. Revoking access becomes a policy change rather than an exercise in remembering which machines hold which key.
tsh status && tsh kube lsThe certificate has an expiry measured in hours. A stolen laptop is a time-boxed problem instead of an open-ended one.
Detection, because prevention will fail
Every control above will eventually be misconfigured by someone in a hurry. Security Command Centre reports posture drift — a bucket made public, a firewall opened, a service account over-granted — and audit logs record who did what and when. Neither prevents anything; both are how you find out, and how you reconstruct events afterwards. The controls that fail silently are the dangerous ones.
gcloud logging read 'protoPayload.methodName="SetIamPolicy"' --freshness=7d --format='table(timestamp,protoPayload.authenticationInfo.principalEmail,resource.labels)'IAM policy changes are the highest-signal audit event there is. Almost every privilege-escalation path passes through one.