Skip to content

sec(iac/gcp): cleanup-function SA gets project-wide Secret Manager access to the encryption key #1614

Description

@cristim

Reviewed commit: be11bdcb5. Note: origin/main moved to 3e9660d06 during the review; re-verify against current main before changing code, since a finding may have been fixed or moved.

Blast radius: customer credential material. This is not a CI/deploy-infrastructure finding. The grant below reaches the AES-256-GCM key that decrypts stored customer cloud credentials, which is why it carries priority/p0 / impact/all-users rather than the priority/p1 / impact/internal used for our own deploy-identity findings.

Where

  • terraform/modules/compute/gcp/cleanup-function/main.tf:9-13 (the grant)
  • terraform/modules/compute/gcp/cleanup-function/main.tf:47 (the one secret the function actually needs)
  • terraform/modules/compute/gcp/cloud-run/main.tf:247-276 (the sibling module that already fixed this)
  • terraform/modules/secrets/gcp/main.tf:258-288 (the credential-encryption key that becomes readable)

What

google_project_iam_member.cleanup_secrets grants roles/secretmanager.secretAccessor at project scope to the cleanup function's service account, ungated (no count, no condition). Verified still present verbatim at 3e9660d06:

# Grant access to Secret Manager
resource "google_project_iam_member" "cleanup_secrets" {
  project = var.project_id
  role    = "roles/secretmanager.secretAccessor"
  member  = "serviceAccount:${google_service_account.cleanup.email}"
}

The function only ever needs one secret: var.db_password_secret_id, wired into DB_PASSWORD_SECRET at main.tf:47.

The sibling Cloud Run module explicitly documents removing this exact anti-pattern and replacing it with per-secret bindings (compute/gcp/cloud-run/main.tf:247-276):

Previously this granted the Cloud Run SA project-wide roles/secretmanager.secretAccessor, which let it read every secret in the project... Now we bind only the specific secrets

The cleanup function was never migrated.

Failure scenario

Anyone who compromises the cleanup function, or anyone who can impersonate its service account (for example a developer holding roles/iam.serviceAccountTokenCreator on the project), reads every secret in the project. That set includes:

  • <service>-credential-encryption-key (terraform/modules/secrets/gcp/main.tf:258-288), the AES-256-GCM key that decrypts stored customer cloud credentials
  • jwt-secret
  • session-secret
  • scheduled-task-secret
  • the SendGrid API key

Full tenant-credential compromise reachable from a session-cleanup job that has no business touching any of it.

Fix direction

Replace google_project_iam_member.cleanup_secrets with a google_secret_manager_secret_iam_member scoped to var.db_password_secret_id only, mirroring the pattern at compute/gcp/cloud-run/main.tf:262-267. Then add a repo-level guard (a grep-based lint or a policy test) that fails on any new project-scope roles/secretmanager.secretAccessor binding, so the next module does not repeat it.

Related

Activity

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