Skip to content

fix(gcp): reject destroy trust tuples off protected dev/main - #2376

Merged
Brad-Edwards merged 3 commits into
devfrom
fix/gcp-destroy-protected-ref
Sep 23, 2026
Merged

Brad-Edwards merged 3 commits into
devfrom
fix/gcp-destroy-protected-ref

Conversation

@Brad-Edwards

Copy link
Copy Markdown
Owner

Problem

On 2026-09-23, dispatching gcp-dev-destroy.yml from dev with environment=balrog failed with: Branch "dev" is not allowed to deploy to balrog-destroy due to environment protection rules.

The workflow's pre-auth guard Reject non-protected dispatch refs allows only refs/heads/dev|refs/heads/main. That guard is a security control and this PR does not change it. The balrog foundation (cicd-oidc) was applied with its destroy purpose tuple bound to refs/heads/balrog, and balrog-destroy allowed only branch balrog. No dispatch could satisfy both the guard and that binding. The foundation input contract accepted the tuple anyway. scripts/bootstrap/README.md also said "Deploy and destroy use the selected tenant branch", which contradicts the guard.

Fix

  • platform/terraform/gcp/global/cicd-oidc/inventory.tf and modules/cicd-oidc-identity/inventory.tf: new purpose_contexts validation. Every destroy tuple must have ref in refs/heads/dev / refs/heads/main, and its workflow_ref must end with @<ref>.
  • scripts/bootstrap/gcp_foundation.py: adds a DESTROY_PROTECTED_REFS constant and checks it right after the required-keys check, before any gcloud/gh/terraform call. Without this check, API enablement and state-bucket creation would run before Terraform variable validation.
  • Drift test: test_destroy_refs_match_workflow_guard_and_terraform_contract checks the workflow guard's case pattern, both Terraform validations, and the example inputs against DESTROY_PROTECTED_REFS.
  • Docs: the bootstrap README now says deploy uses the tenant branch. Destroy must bind protected dev/main, and the <environment>-destroy Environment branch policy must allow that ref. docs/dev/deploy-secrets.md already said this; it now also says bootstrap rejects other refs.

Tests

  • terraform test in platform/terraform/gcp/global/cicd-oidc (mock provider): 7 passed. New runs: destroy on refs/heads/dev with a tenant-branch deploy is accepted; destroy on the tenant branch is rejected.
  • pytest scripts/bootstrap/tests: all passed. New cases: dev and main are accepted; a tenant-branch ref and a mismatched workflow_ref are rejected before any command runs; plus the drift test.
  • pytest scripts/check_tf_gcp_wif_trust: 29 passed. The check_tf_gcp_wif_trust hook is clean.
  • pytest shifter/packer/tests/test_packer_gcp.py -k destroy: 2 passed.

Operator follow-up (not in this PR)

The live balrog foundation still has the refs/heads/balrog destroy tuple, and balrog-destroy still allows only balrog. Fixing it takes a re-apply of the balrog cicd-oidc root with a refs/heads/dev destroy tuple, plus a change to the balrog-destroy Environment branch policy to allow dev. Re-applying with the current inputs will now fail validation until the tuple is corrected.

gcp-dev-destroy.yml rejects every dispatch ref except refs/heads/dev and
refs/heads/main before auth, but the cicd-oidc input contract accepted a
destroy purpose tuple on any branch. A tenant bootstrapped with its destroy
tuple on the tenant branch could never be torn down.

Validate destroy tuples in both the cicd-oidc root and the identity module,
and in gcp_foundation bootstrap before any command runs. A test pins the
workflow guard, both Terraform validations, and the example inputs to one
Python constant so they cannot drift. Correct the bootstrap README, which
said destroy uses the tenant branch.
@Brad-Edwards
Brad-Edwards merged commit 93a67b8 into dev Sep 23, 2026
7 checks passed
@Brad-Edwards
Brad-Edwards deleted the fix/gcp-destroy-protected-ref branch September 23, 2026 22:42
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant