Skip to content

sec(cli): the GCP setup wizard grants roles/compute.admin at project scope #1946

Description

@cristim

Summary

Step 4 of cudly configure-gcp grants the CUDly service account roles/compute.admin at project scope, and the prompt treats empty input as Run. The next steps mint a long-lived JSON key for that identity and upload it to AWS Secrets Manager. That role confers full control of Compute Engine, including deleting VMs, disks, images, firewall rules and networks, where CUDly needs to read usage and buy committed use discounts. The project's own IAM policy names this role as the anti-pattern to avoid.

Location

cmd/configure_gcp.go:686 at 3c0f8ac

Failure scenario

Step 4 of cudly configure-gcp grants the CUDly service account roles/compute.admin on the operator's project, then Step 5 mints a long-lived JSON key for it and Step 6 uploads that key to AWS Secrets Manager. compute.admin confers full control of every Compute Engine resource: creating and deleting VMs, disks, images, firewall rules and networks. CUDly needs to read usage and purchase committed use discounts. Anyone who obtains the stored key can delete the project's production infrastructure. The project's own IAM policy in CLAUDE.md names this exact role as the anti-pattern ("Prefer custom roles ... over broad predefined roles like roles/compute.admin"), and the prompt defaults to Run on empty input.

Evidence

member := fmt.Sprintf("serviceAccount:%s", saEmail)
role := "roles/compute.admin"
...
fmt.Printf("[R]un, [S]kip? (grants %s to %s on project %s via SDK) ", role, saEmail, projectID)

Suggested fix

Grant the narrowest predefined pair the CUD flow needs (roles/compute.viewer plus the commitment-purchase permissions) or provision a google_project_iam_custom_role holding only compute.commitments.* and the usage reads, matching the runtime-permissions rule in CLAUDE.md.


Found by the 2026-09-02 codebase audit, finding A10-018, reported by one reviewer and independently confirmed by a second. Full report: docs/audits/codebase-audit-2026-09-02.md.

Activity

  1. cristim commented on Sep 29, 2026

    @cristim
    MemberAuthor

    Post-merge verification for #2122 completed on actual merge commit 76d1d4e72f13731ba071c7932f8e7e233cb4f6c6.

    • The merged tree is exactly identical to independently reviewed 1272e721b8d5185634840c9aa9fc7698dd7cb124: both tree IDs are 8736682a2fcc3ac7ff52b0187e696be35a82ddec.
    • Checked out the merge commit and ran GOWORK=off GOTOOLCHAIN=go1.26.6 go test -race ./cmd -run 'TestGCPStepGrantRole|TestAddMemberToPolicyBinding' -count=1: exit 0, 26.461 seconds.
    • This covers the actual wizard coordinator and Google SDK request path against local HTTP fixtures, including preserved conditional and existing broad grants, unsafe custom-role rejection, and failures before key minting. The independent pre-merge overlay also proved the unchanged regression fails on the original Compute Admin policy request.

    No real IAM mutation or purchase was performed. These fixtures establish request behaviour, not live IAM enforcement or propagation. Existing broad grants remain unchanged and need operator review. Follow-up #2123 tracks that inventory and authorized migration; #2124 tracks the pre-existing Recommender permission setup gap. Main-branch CI is tracked separately by the coordinating session.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions