This is a demonstration repository, but it manages a real (demo-scale) GCP organization. If you find a vulnerability in the Terraform, the pipeline configuration, or the demo application, please open a GitHub issue — there is no sensitive production data behind any of it, so public disclosure is acceptable and fastest. If you believe you've found something sensitive despite that (e.g., a leaked identifier that shouldn't be here), use GitHub's private vulnerability reporting on this repository instead.
Scope: this document models the organization this repo builds — a demo-scale GCP org with a GitOps control plane and one zero-trust workload. It is deliberately honest about scale; controls that only make sense with more workloads, people, or data are listed as accepted gaps, not silently omitted.
┌────────────────────────────── GitHub ──────────────────────────────┐
│ PR authors ──> branch ──> [B1: review + plan + lint gates] ──> main │
└───────────────────────────────────│─────────────────────────────────┘
OIDC token (short-lived)
│
┌────────────────────── [B2: WIF trust condition] ────────────────────┐
│ deployer SA (least-privilege, keyless, no org-IAM write) │
└───────────────────────────────────│─────────────────────────────────┘
│ terraform apply
┌──────────────────────── GCP organization root ──────────────────────┐
│ org policies (inherited, unremovable from below) │
│ ├── /workloads [B3: folder boundary] │
│ │ └── vended project ── Cloud Run │
│ └── /sandbox ▲ │
│ [B4: LB + IAP — identity-checked ingress] │
└───────────────────────────────────│─────────────────────────────────┘
internet
- The organization control plane — org policies and org IAM. Compromise here defeats everything below.
- The deployment identity (WIF pool + deployer SA) — the only identity that changes infrastructure.
- Terraform state (versioned GCS bucket) — an infrastructure map and a tamper target.
- The workload and its ingress — lowest value by design; it holds no data.
| Threat | Boundary | Control |
|---|---|---|
| Stolen long-lived credential used to modify infrastructure | B2 | No such credential exists: iam.disableServiceAccountKeyCreation org-wide; deployment is OIDC→WIF with minutes-lived tokens |
| A fork or unrelated repo assuming the deployer identity | B2 | WIF attribute condition pins the exact owner/repo claim |
| Compromised pipeline escalating its own privilege | B2 | Deployer holds no setIamPolicy on the org; its own role bindings are human-granted and imported — self-escalation is structurally absent |
| Malicious or mistaken change reaching the org | B1 | plan-on-PR renders every change for review; apply only from main; ruleset requires green checks (enforced on public repos) |
| Workload misconfiguration (public bucket, public IP, default network, over-privileged default SA) | org root | The seven policy constraints — violations are unrepresentable, not reviewed |
| Anonymous or unauthorized access to the workload | B4 | No public ingress path exists: Cloud Run accepts LB traffic only, IAP authenticates every request, invoker is granted solely to IAP's service agent, access is allow-listed per identity |
| Cross-project blast radius from a misbehaving workload | B3 | Factory vends into folders, never the org root; a workload's maximum scope is its own project |
| Silent tampering / repudiation | org root | Org-level audit sink (include_children = true) captures every admin action, including the pipeline's own applies; state bucket versioning preserves every state transition |
Two classes of information appear in this repository's evidence and pipeline output, with two rules:
- Numeric resource identifiers (organization, billing account, project, folder) are treated as non-secret: they are names, not credentials; a live plan-on-PR pipeline necessarily displays them in public comments and workflow logs, and no identifier grants access absent IAM.
- Personal-linking artifacts (user emails, personal domains) are redacted from evidence by policy and supplied to the pipeline as masked secrets, never committed.
- Single human super-admin, no group-based IAM or breakglass. One-person directory; the folder layout leaves room for both.
- No VPC Service Controls / data-exfiltration perimeter. There is no data to exfiltrate; the perimeter is the next layer in a real deployment and nothing here conflicts with it.
- No Security Command Center. Preventive controls only; detective posture management is production scope (see the sister repo, CloudCanary, for the detection side of this argument).
- State bucket uses Google-managed encryption, no CMEK; registry likewise. Key-management lifecycle adds operational surface with no threat-model benefit at demo scale — each is marked in-code with a
#checkov:skipand rationale. - Pipeline self-manages its identity plumbing (pool, provider, SA) — a documented tradeoff versus leaving those resources as unmanaged drift; the compensating control is that org-IAM write is withheld.
- Single environment, no promotion gates. One org, one lane; multi-env is roadmap.
The pattern behind every row above: a control was omitted because the asset it protects doesn't exist here yet — not because the control is unknown. Where that changes, the roadmap names the control.