Reviewed commit: be11bdcb5. Note: origin/main moved to 3e9660d06 during the review; re-verify against current main before changing code.
Severity: CRITICAL. This template is deployed by customers into their own cloud accounts, so the blast radius is their infrastructure, not ours.
Two defects in one script, filed together because they share a fix session and a root cause: the shell path lacks the guard its Terraform sibling enforces.
Defect A: AWS mode creates the WIF provider with no attribute condition, then grants pool-wide impersonation
Where: arm/CUDly-CrossSubscription/setup-gcp-wif.sh:99-103 (provider create) and :133-139 (IAM binding)
What: the --provider-type aws branch calls gcloud iam workload-identity-pools providers create-aws with only --account-id, and never passes --attribute-condition. The script then binds roles/iam.workloadIdentityUser to principalSet://iam.googleapis.com/projects/<n>/locations/global/workloadIdentityPools/<pool>/*, i.e. every identity in the pool.
The Terraform sibling iac/federation/gcp-target/terraform/main.tf:137-147 carries an explicit precondition forbidding exactly this state, with the error text: "Without it the attribute_condition is null and any IAM role in the AWS account can federate as the GCP service account."
Failure scenario: a customer runs the documented AWS-mode command. Any IAM principal in the referenced AWS account (a developer's sandbox role, a CI runner, an EC2 instance profile on a compromised host, anything that can call sts:GetCallerIdentity) exchanges its AWS credentials at sts.googleapis.com and impersonates the customer's GCP service account, which holds compute.commitments.create/update (spend authority) or, in the impersonation bundle, commerceorgpolicy.commitmentAdmin. No CUDly involvement is required.
Fix: require a --aws-role-name argument in AWS mode and emit --attribute-condition="attribute.aws_role.contains('assumed-role/<role>/')", and narrow the binding to principalSet://.../attribute.aws_role/..., matching the Terraform module's mandatory precondition.
Defect B: OIDC mode warns instead of failing, and the documented usage omits the guard
Where: arm/CUDly-CrossSubscription/setup-gcp-wif.sh:110-126; documented usage example at :20-27
What: in OIDC mode, when --subject-condition is empty the script prints a WARNING to stderr and proceeds to create the provider with no --attribute-condition (:123-125 only appends the flag when the variable is non-empty). The header's copy-paste example uses --issuer-uri https://token.actions.githubusercontent.com and does not include --subject-condition.
Failure scenario: a customer copy-pastes the documented OIDC command verbatim. Because token.actions.githubusercontent.com is a globally shared public issuer, any GitHub Actions workflow in any repository on GitHub can request an OIDC token, exchange it through the customer's pool, and impersonate the customer's CUDly service account with commitment-purchase rights. A stderr warning on a set -euo pipefail script that still succeeds is not a control.
Fix: make --subject-condition mandatory for --provider-type oidc (exit 1 like the other required-arg checks at :59-74) and fix the header example to include it.
Related
Part of the onboarding-path drift class filed separately: the Terraform path is hardened and the shell/CFN/ARM path for the identical onboarding job is not.
Reviewed commit:
be11bdcb5. Note:origin/mainmoved to3e9660d06during the review; re-verify against currentmainbefore changing code.Severity: CRITICAL. This template is deployed by customers into their own cloud accounts, so the blast radius is their infrastructure, not ours.
Two defects in one script, filed together because they share a fix session and a root cause: the shell path lacks the guard its Terraform sibling enforces.
Defect A: AWS mode creates the WIF provider with no attribute condition, then grants pool-wide impersonation
Where:
arm/CUDly-CrossSubscription/setup-gcp-wif.sh:99-103(provider create) and:133-139(IAM binding)What: the
--provider-type awsbranch callsgcloud iam workload-identity-pools providers create-awswith only--account-id, and never passes--attribute-condition. The script then bindsroles/iam.workloadIdentityUsertoprincipalSet://iam.googleapis.com/projects/<n>/locations/global/workloadIdentityPools/<pool>/*, i.e. every identity in the pool.The Terraform sibling
iac/federation/gcp-target/terraform/main.tf:137-147carries an explicitpreconditionforbidding exactly this state, with the error text: "Without it the attribute_condition is null and any IAM role in the AWS account can federate as the GCP service account."Failure scenario: a customer runs the documented AWS-mode command. Any IAM principal in the referenced AWS account (a developer's sandbox role, a CI runner, an EC2 instance profile on a compromised host, anything that can call
sts:GetCallerIdentity) exchanges its AWS credentials atsts.googleapis.comand impersonates the customer's GCP service account, which holdscompute.commitments.create/update(spend authority) or, in the impersonation bundle,commerceorgpolicy.commitmentAdmin. No CUDly involvement is required.Fix: require a
--aws-role-nameargument in AWS mode and emit--attribute-condition="attribute.aws_role.contains('assumed-role/<role>/')", and narrow the binding toprincipalSet://.../attribute.aws_role/..., matching the Terraform module's mandatory precondition.Defect B: OIDC mode warns instead of failing, and the documented usage omits the guard
Where:
arm/CUDly-CrossSubscription/setup-gcp-wif.sh:110-126; documented usage example at:20-27What: in OIDC mode, when
--subject-conditionis empty the script prints a WARNING to stderr and proceeds to create the provider with no--attribute-condition(:123-125only appends the flag when the variable is non-empty). The header's copy-paste example uses--issuer-uri https://token.actions.githubusercontent.comand does not include--subject-condition.Failure scenario: a customer copy-pastes the documented OIDC command verbatim. Because
token.actions.githubusercontent.comis a globally shared public issuer, any GitHub Actions workflow in any repository on GitHub can request an OIDC token, exchange it through the customer's pool, and impersonate the customer's CUDly service account with commitment-purchase rights. A stderr warning on aset -euo pipefailscript that still succeeds is not a control.Fix: make
--subject-conditionmandatory for--provider-type oidc(exit 1 like the other required-arg checks at:59-74) and fix the header example to include it.Related
Part of the onboarding-path drift class filed separately: the Terraform path is hardened and the shell/CFN/ARM path for the identical onboarding job is not.