Skip to content

Forms DB Access — Paths & Break-Glass Procedure

Classification: CONFIDENTIAL — Internal Use Only Document: infrastructure/runbooks/forms-db-access.md · v1.0 · 2026-06-12 · GPUS-IT

Access paths to Cloud SQL instance gpus-forms-db (PostgreSQL 15, private IP 10.34.0.3 on gpus-vpc, IAM database authentication, no password auth). Covers the canonical service paths and the break-glass human path used when an operator must reach the database directly from a workstation.

Canonical access paths (standing)

Principal IAM DB user Path Purpose
gpus-forms-backend@gpus-infra.iam.gserviceaccount.com gpus-forms-backend@gpus-infra.iam Cloud Run → VPC connector gpus-vpc-connector → private IP Application runtime (holds forms_app role)
clamav-worker@gpus-infra.iam.gserviceaccount.com clamav-worker@gpus-infra.iam Cloud Run → VPC connector → private IP α ClamAV verdict + audit writes (scanner RLS policies)
maple-agent@gpus-infra.iam.gserviceaccount.com maple-agent@gpus-infra.iam MAPLE VM → VPC peering → private IP Daily backup export; 2.5(c) routing worker (Gate 4)
rajesh.chhetry@greenpeace.us rajesh.chhetry@greenpeace.us (CLOUD_IAM_USER) Break-glass only (below) Operator

There is no public IP and no password-authenticated user in active use. Humans have no standing connection path: workstations are not on gpus-vpc, and that is deliberate.

Break-glass path: laptop tunnel via MAPLE + SA impersonation

Not a standing convenience

This is a grant → use → revoke procedure, time-boxed to a single work session. The roles/iam.serviceAccountTokenCreator grant exists only while the session is open and is revoked — and the revocation verified — before the session is considered closed. Leaving the grant in place defeats the access model (CIS 5.2 / NIST AC-6 least privilege). Used most recently for the forms 2.5(c) Gate-3 end-to-end test (2026-06-10/11); revocation verified 2026-06-12.

Use this path when work requires direct psql against gpus-forms-db with an application role's privileges (e.g. forms_app row-level security context) — schema verification, e2e checks, incident forensics. For plain read access under your own identity, the same tunnel works with your own CLOUD_IAM_USER and no grant is needed (skip steps 1 and 4; your DB user carries only its own privileges, not forms_app).

Prerequisites: SSH reachability to MAPLE (172.16.0.12) over the established VPN path (IAP is not currently a working fallback — see priority T3, IAP-to-MAPLE 4003 host-level), and roles/cloudsql.instanceUser on your account (already held by rajesh.chhetry@greenpeace.us).

1 — Grant (time-boxed)

Grant yourself token-creator on the service account whose database privileges the work requires — normally gpus-forms-backend@gpus-infra.iam.gserviceaccount.com (holds forms_app):

gcloud iam service-accounts add-iam-policy-binding \
  gpus-forms-backend@gpus-infra.iam.gserviceaccount.com \
  --member="user:rajesh.chhetry@greenpeace.us" \
  --role="roles/iam.serviceAccountTokenCreator" \
  --project=gpus-infra

Record the grant (timestamp + reason) in the work log for the change that needed it. The grant itself is captured in Cloud Audit Logs (SetIamPolicy on the SA).

2 — Use

Terminal 1 — tunnel a local port through MAPLE to the Cloud SQL private IP:

ssh -L 15432:10.34.0.3:5432 cloudadmin@172.16.0.12

Terminal 2 — IAM-auth psql as the impersonated service account (access tokens expire after ~60 minutes; re-run the PGPASSWORD mint as needed):

PGPASSWORD="$(gcloud auth print-access-token \
  --impersonate-service-account=gpus-forms-backend@gpus-infra.iam.gserviceaccount.com)" \
psql "host=127.0.0.1 port=15432 \
      user=gpus-forms-backend@gpus-infra.iam \
      dbname=gpus_forms sslmode=require"

For your own-identity variant: user=rajesh.chhetry@greenpeace.us and drop the --impersonate-service-account flag.

3 — Revoke (same session, not "later")

gcloud iam service-accounts remove-iam-policy-binding \
  gpus-forms-backend@gpus-infra.iam.gserviceaccount.com \
  --member="user:rajesh.chhetry@greenpeace.us" \
  --role="roles/iam.serviceAccountTokenCreator" \
  --project=gpus-infra

4 — Verify the revocation (evidence, not assertion)

gcloud iam service-accounts get-iam-policy \
  gpus-forms-backend@gpus-infra.iam.gserviceaccount.com \
  --project=gpus-infra --format=json

Expected: no user: member under any role. The only standing bindings across the forms-portal service accounts are roles/iam.serviceAccountUser for gpus-cloudbuild@gpus-infra.iam.gserviceaccount.com (deploy-time actor) and — on clamav-worker only — roles/iam.serviceAccountTokenCreator for the Pub/Sub service agent service-1056766133984@gcp-sa-pubsub.iam.gserviceaccount.com (mints OIDC tokens for the push subscription; do not remove it).

Close the tunnel (Ctrl-C in Terminal 1) and note the revocation in the same work log entry as the grant.

GRANT-class migrations — who can apply them (documented fact, 2026-06-12)

Discovered applying migration 013: IAM DB users (including rajesh.chhetry@greenpeace.us) hold only cloudsqliamuserno CREATEROLE, no admin option on forms_app — so GRANT <role> TO … migrations fail with must have admin option on role. ALTER TYPE migrations (011/012 style) work under your own IAM user because the type owner check passes; role-membership grants do not.

The only identity that can apply GRANT-class migrations is the postgres built-in (member of cloudsqlsuperuser, which holds CREATEROLE; it is also the effective grantor of record for both forms_app memberships). Its password is deliberately not stored anywhere — Step 5 never set one and no secret exists. Procedure (transient-credential analog of the break-glass grant→use→revoke):

# 1. Set a one-time random password (requires project owner):
PW="$(openssl rand -base64 24)"
gcloud sql users set-password postgres --instance=gpus-forms-db \
  --project=gpus-infra --password="$PW"

# 2. Apply over the MAPLE tunnel (§ above) as postgres:
PGPASSWORD="$PW" psql "host=127.0.0.1 port=15432 user=postgres \
  dbname=gpus_forms sslmode=require" -f <migration.sql>

# 3. Immediately rotate to a NEW random that is discarded — never
#    leave a known root credential behind:
gcloud sql users set-password postgres --instance=gpus-forms-db \
  --project=gpus-infra --password="$(openssl rand -base64 24)"

Record which identity applied each migration in its commit message.

  • Backup & Restoremaple-agent Cloud SQL export auth path
  • Forms Portal — architecture and controls overview
  • forms-backend/RUNBOOK-CLAUDE-CODE.md Step 6 — original schema-apply procedure (Cloud SQL Auth Proxy variant; the proxy needs direct network reach to the instance, so from a workstation the SSH-tunnel form above is the working path)