Found while fixing #1544 (PR #1651). These are in the served GCP onboarding script and the ARM script's OIDC branch, and are out of that PR's scope.
Scope note: this issue originally also covered the Terraform module's pool-wide grant. That turned out to be a live bypass rather than a hardening gap and has been split into its own p0, #1667. This issue is now only the two items below.
1. Served onboarding script swallows provider-create failures
internal/iacfiles/templates/gcp-wif-cli.sh.tmpl:50-57 - the script the federation API hands to customers.
gcloud iam workload-identity-pools providers create-oidc "${PROVIDER_ID}" \
... --attribute-condition="assertion.sub == '${CUDLY_FEDERATED_SUBJECT}'" \
|| echo " (provider may already exist)"
The template is not vulnerable to either #1544 defect: it always passes --attribute-condition and binds principal://.../subject/..., and its AWS-STS path was deliberately removed (asserted by templates_test.go's mustNot: "create-aws"). But || echo swallows every failure of a security-critical create under set -euo pipefail. If a provider of that name already exists with a weaker condition, or none at all, the script continues, grants the binding, and prints "=== Done ===". Same shape on the pool creation at :43-47.
This is the exact gap closed on the ARM script in PR #1651 (commit 21af9cc): on create failure, describe the existing provider and compare its attributeCondition to the expected string, dying on mismatch rather than reusing it.
Lower severity than #1544 because the issuer is CUDly's own OIDC deployment rather than a public shared issuer, and the subject is the fixed cudly-controller.
2. CUDLY_FEDERATED_SUBJECT is interpolated into CEL with no validation
internal/iacfiles/templates/gcp-wif-cli.sh.tmpl:31:
CUDLY_FEDERATED_SUBJECT="${CUDLY_FEDERATED_SUBJECT:-cudly-controller}"
It is environment-overridable and lands inside a CEL string literal at :55. A value containing ' closes the literal: CUDLY_FEDERATED_SUBJECT="x' || true || '" renders assertion.sub == 'x' || true || '', an always-true condition admitting every subject the CUDly issuer will sign. It also flows into the principal://.../subject/ identifier at :75.
Self-inflicted (the operator sets it on their own machine), so severity is low, but it is the same break-out validate_principal_value() was added to stop in PR #1651, and the same class as #1667(c). Apply the same character rejection: ', ", \, backtick, *, $.
3. create-cred-config called with no credential source in OIDC mode
arm/CUDly-CrossSubscription/setup-gcp-wif.sh:275-278 (pre-existing; unchanged by #1651).
Google's reference states exactly one of --aws, --azure, --credential-source-file, --credential-source-url, --executable-command, --credential-cert-path must be given. The AWS branch passes --aws; the OIDC branch passes none, so gcloud should reject the invocation and set -e aborts after every mutation has been made - the customer ends up with a configured pool and no credential config.
This suggests OIDC mode has never been run end to end. Not verified against a live gcloud (no GCP credentials in the fix session), so confirm before acting: either forward a credential source for OIDC, or drop OIDC mode from that script if the served template supersedes it.
Found while fixing #1544 (PR #1651). These are in the served GCP onboarding script and the ARM script's OIDC branch, and are out of that PR's scope.
1. Served onboarding script swallows provider-create failures
internal/iacfiles/templates/gcp-wif-cli.sh.tmpl:50-57- the script the federation API hands to customers.The template is not vulnerable to either #1544 defect: it always passes
--attribute-conditionand bindsprincipal://.../subject/..., and its AWS-STS path was deliberately removed (asserted bytemplates_test.go'smustNot: "create-aws"). But|| echoswallows every failure of a security-critical create underset -euo pipefail. If a provider of that name already exists with a weaker condition, or none at all, the script continues, grants the binding, and prints "=== Done ===". Same shape on the pool creation at:43-47.This is the exact gap closed on the ARM script in PR #1651 (commit 21af9cc): on create failure, describe the existing provider and compare its
attributeConditionto the expected string, dying on mismatch rather than reusing it.Lower severity than #1544 because the issuer is CUDly's own OIDC deployment rather than a public shared issuer, and the subject is the fixed
cudly-controller.2.
CUDLY_FEDERATED_SUBJECTis interpolated into CEL with no validationinternal/iacfiles/templates/gcp-wif-cli.sh.tmpl:31:CUDLY_FEDERATED_SUBJECT="${CUDLY_FEDERATED_SUBJECT:-cudly-controller}"It is environment-overridable and lands inside a CEL string literal at
:55. A value containing'closes the literal:CUDLY_FEDERATED_SUBJECT="x' || true || '"rendersassertion.sub == 'x' || true || '', an always-true condition admitting every subject the CUDly issuer will sign. It also flows into theprincipal://.../subject/identifier at:75.Self-inflicted (the operator sets it on their own machine), so severity is low, but it is the same break-out
validate_principal_value()was added to stop in PR #1651, and the same class as #1667(c). Apply the same character rejection:',",\, backtick,*,$.3.
create-cred-configcalled with no credential source in OIDC modearm/CUDly-CrossSubscription/setup-gcp-wif.sh:275-278(pre-existing; unchanged by #1651).Google's reference states exactly one of
--aws,--azure,--credential-source-file,--credential-source-url,--executable-command,--credential-cert-pathmust be given. The AWS branch passes--aws; the OIDC branch passes none, so gcloud should reject the invocation andset -eaborts after every mutation has been made - the customer ends up with a configured pool and no credential config.This suggests OIDC mode has never been run end to end. Not verified against a live gcloud (no GCP credentials in the fix session), so confirm before acting: either forward a credential source for OIDC, or drop OIDC mode from that script if the served template supersedes it.