KB-1565

S176 IAM RCA: cursor-ci-builder Missing Permissions Pattern

6 min read Revision 1
rcaiams176gcp

S176-IAM-RCA: Root Cause Analysis — cursor-ci-builder Missing Secret Manager Access

Ngày điều tra: 2026-04-10 Mission: S176 (Lark Production Infrastructure) Trạng thái: FIXED — IAM binding added, GSM access verified


1. Bối cảnh

Khi bắt đầu S176, phát hiện SA cursor-ci-builder trên VPS không có quyền đọc GSM secrets (LARK_APP_ID, LARK_APP_SECRET). Đây là lần thứ 2 trong 3 phiên gặp pattern "IAM silent missing":

  • S174: cursor-ci-builder thiếu storage.buckets.list → GCS upload fail (WARN), backup hỏng 28 ngày
  • S176: cursor-ci-builder thiếu secretmanager.versions.access → không đọc được Lark credentials

2. RCA — 5 câu hỏi

Q1: Ai tạo SA cursor-ci-builder, khi nào?

  • Display name: "GitHub Actions Build / Push Artifact Registry"
  • Email: cursor-ci-builder@github-chatgpt-ggcloud.iam.gserviceaccount.com
  • Project: github-chatgpt-ggcloud
  • Mục đích ban đầu: CI/CD cho GitHub Actions — build Docker image, push lên Artifact Registry
  • Ngày tạo: Không xác định chính xác (GCP describe không trả creation date). Xuất hiện trong Gemini chat logs từ 2025-10-26, nên tạo khoảng Q4 2025.
  • Không có trong Terraform IaC — SA và IAM bindings được tạo thủ công qua gcloud CLI

Q2: SA có những role gì?

Role Mục đích
roles/artifactregistry.writer Push Docker images (mục đích gốc)
roles/cloudsql.client Cloud SQL access
roles/datastore.user Firestore/Datastore
roles/secretmanager.secretAccessor MỚI THÊM hôm nay — đọc GSM secrets

Nhận xét: SA bắt đầu đời chỉ với artifactregistry.writer (CI/CD), rồi được "mượn" làm SA chính trên VPS khi setup gcloud. Mỗi lần cần tính năng mới (Cloud SQL, Firestore, GSM), phải add role thủ công — và quên thì silent fail.

Q3: SA cần đọc secret nào khác? Còn thiếu quyền gì?

Secrets trong GSM (22 total):

  • AGENT_DATA_API_KEY, DIRECTUS_ADMIN_TOKEN, DIRECTUS_KEY, DIRECTUS_SECRET
  • GCS_BUCKET_BACKUP
  • LARK_APP_ID, LARK_APP_SECRET
  • MYSQL_ROOT_PASSWORD (legacy)
  • OPENAI_API_KEY
  • PG_DATABASE, PG_HOST, PG_PASSWORD, PG_PORT, PG_USER
  • POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_USER
  • QDRANT_LOCAL_API_KEY
  • gh_pat_sync_secrets
  • smtp-password-nmhuyen
  • vps_contabo_id, vps_contabo_secret

Codebase references trên VPS: Chỉ 1 script đọc GSM trực tiếp:

  • gh_pat_sync_secrets — used by git-push scripts

Hiện tại secretAccessor cho phép đọc TẤT CẢ secrets (project-level binding, không có condition). Đủ cho cả gh_pat_sync_secretsLARK_APP_*.

Missing permissions đã biết (từ S174):

  • storage.buckets.list — vẫn thiếu (S174 ghi nhận, chưa fix, coi là out-of-scope)

Q4: Có IaC (Terraform) quản lý IAM không?

KHÔNG. cursor-ci-builder không xuất hiện trong bất kỳ file .tf nào:

  • agent-data-test/terraform/iam.tf chỉ quản lý compute default SA
  • Không có .tf file nào reference cursor-ci-builder

Hệ quả: Mọi IAM change cho SA này đều thủ công (gcloud CLI). Không có drift detection, không có version control, không có review process.

Q5: Liên quan S174 47h silent không?

CÓ — cùng root cause pattern. Đối chiếu:

S174 S176
SA cursor-ci-builder cursor-ci-builder
Missing role storage.buckets.list secretmanager.secretAccessor
Symptom GCS backup upload silent fail GSM secret access denied
Silent duration 28 ngày Phát hiện ngay (nhờ P0 snapshot)
Root cause SA thiếu role, không IaC, không audit Cùng

Pattern: SA được repurpose từ CI-only → multi-purpose VPS SA, nhưng permissions chỉ được thêm ad-hoc khi gặp lỗi, không có checklist "SA cần gì cho use case X".


3. Gốc rễ thật (1 câu)

SA cursor-ci-builder được tạo cho mục đích hẹp (CI Artifact Registry) rồi được repurpose làm SA chính trên VPS, nhưng IAM bindings quản lý thủ công (không IaC), không có audit/review process, nên mỗi lần thêm use case mới thì thiếu role → silent fail.


4. Fix đã thực hiện

# 2026-04-10, từ Desktop (nmhuyen@gmail.com)
gcloud projects add-iam-policy-binding github-chatgpt-ggcloud \
  --member="serviceAccount:cursor-ci-builder@github-chatgpt-ggcloud.iam.gserviceaccount.com" \
  --role="roles/secretmanager.secretAccessor" \
  --condition=None

# Verify trên VPS
ssh root@38.242.240.89 'gcloud secrets versions access latest \
  --secret=LARK_APP_ID --project=github-chatgpt-ggcloud | head -c 15'
# Output: cli_a785d634437  (khớp app "For Gem")

5. Đề xuất hệ thống (chờ Desktop duyệt — scope ngoài S176)

# Đề xuất Priority
1 Terraform-ize cursor-ci-builder IAM — Add SA + all bindings vào iam.tf. Drift = terraform plan sẽ phát hiện. HIGH
2 IAM audit cron — Weekly script: dump gcloud projects get-iam-policy, diff với baseline, alert nếu có thay đổi. MEDIUM
3 SA purpose documentation — Ghi rõ mỗi SA dùng cho gì, cần role gì, trong KB. MEDIUM
4 Pre-mission IAM checklist — Khi mission cần GSM/GCS/API mới, check SA permissions TRƯỚC, không phải lúc gặp lỗi. HIGH

6. Ghi chú kỹ thuật

  • secretAccessor là project-level (tất cả secrets). Nếu cần restrict → dùng secret-level IAM binding.
  • gcloud projects add-iam-policy-binding yêu cầu --condition=None khi policy đã có conditional bindings.
  • SA describe không trả createTime — cần Audit Log hoặc Activity Log để xác định.

RCA bởi Claude Code, Mission S176 Phase 0, 2026-04-10