diff --git a/terraform/environments/gcp/ci-cd-permissions/README.md b/terraform/environments/gcp/ci-cd-permissions/README.md index 8e21bb74d..641c9dbea 100644 --- a/terraform/environments/gcp/ci-cd-permissions/README.md +++ b/terraform/environments/gcp/ci-cd-permissions/README.md @@ -119,6 +119,33 @@ Workload Identity Federation endpoint for a short-lived SA access token, and set ### Attribute condition -The WIF provider is configured with `attribute_condition = "assertion.repository == 'LeanerCloud/CUDly'"`. -Only workflows from that exact repository can impersonate the service account. To change the allowed -repo, update `github_repo` in `terraform.tfvars` and re-apply. +The WIF provider enforces **two** conditions simultaneously: + +```text +assertion.repository == '' && assertion.ref == '' +``` + +Only workflows running from the exact repository **and** the exact ref (branch) configured in +`terraform.tfvars` can obtain a GCP token. Any other branch, PR, fork, tag, or +`workflow_dispatch` from a different ref is rejected with: + +> `unauthorized_client: The given credential is rejected by the attribute condition.` + +**Keeping `deploy_ref` in sync with the active deploy branch is critical.** When the base +deploy branch changes (e.g. a long-running feature branch is merged into `main`), the operator +must update `deploy_ref` in `terraform.tfvars` and re-apply this module before the next deploy. + +The default value of `deploy_ref` is `refs/heads/main`. A plain `terraform apply` with no +override in `terraform.tfvars` restores the correct condition for deploys from `main`. + +To verify the currently-applied condition without applying: + +```bash +gcloud iam workload-identity-pools providers describe github-actions \ + --workload-identity-pool=github-actions \ + --location=global \ + --project= \ + --format="value(attributeCondition)" +``` + +To change the allowed repo, update `github_repo` in `terraform.tfvars` and re-apply. diff --git a/terraform/environments/gcp/ci-cd-permissions/github_oidc.tf b/terraform/environments/gcp/ci-cd-permissions/github_oidc.tf index 02778a1a1..94aecf876 100644 --- a/terraform/environments/gcp/ci-cd-permissions/github_oidc.tf +++ b/terraform/environments/gcp/ci-cd-permissions/github_oidc.tf @@ -29,6 +29,11 @@ resource "google_iam_workload_identity_pool_provider" "github" { # Restrict to the specific repo AND only allow the designated deploy branch. # PRs, tags, forks, and workflow_dispatch from other refs are blocked. + # + # deploy_ref defaults to refs/heads/main. Override in terraform.tfvars only + # when temporarily deploying from a feature branch; flip back to main once + # that branch merges. Forgetting to flip locks out main-branch deploys with + # "unauthorized_client: The given credential is rejected by the attribute condition." attribute_condition = "assertion.repository == '${var.github_repo}' && assertion.ref == '${var.deploy_ref}'" } diff --git a/terraform/environments/gcp/ci-cd-permissions/terraform.tfvars.example b/terraform/environments/gcp/ci-cd-permissions/terraform.tfvars.example index fc4cfbca8..78bbfa713 100644 --- a/terraform/environments/gcp/ci-cd-permissions/terraform.tfvars.example +++ b/terraform/environments/gcp/ci-cd-permissions/terraform.tfvars.example @@ -36,4 +36,18 @@ github_repo = "LeanerCloud/CUDly" # Only exact-match pushes to this branch can obtain a GCP token. # PRs, tags, forks, and workflow_dispatch from other refs are blocked. # +# DEFAULT: refs/heads/main +# The default in variables.tf is refs/heads/main, which is correct for normal +# operation. Override here ONLY when temporarily deploying from a feature branch. +# +# IMPORTANT: When the active deploy branch changes (e.g. a long-running feature +# branch merges into main), update this value and re-apply the module. Forgetting +# to flip back to refs/heads/main will lock out all future main-branch deploys +# with: "The given credential is rejected by the attribute condition." +# +# To verify the currently-applied condition in GCP (read-only): +# gcloud iam workload-identity-pools providers describe github-actions \ +# --workload-identity-pool=github-actions --location=global \ +# --project= --format="value(attributeCondition)" +# deploy_ref = "refs/heads/main"