Skip to content

Service Accounts — Key Management, Impersonation và Default SA Dangers

Tại sao Service Accounts là attack surface lớn nhất trong GCP

Trong phần lớn các GCP security incidents được public, vector tấn công ban đầu thường là một trong hai: leaked service account key, hoặc compromised workload với overly-broad SA permissions. Service accounts là nơi IAM risks tập trung vì:

  1. Số lượng: Mỗi project production có thể có hàng chục đến hàng trăm SAs
  2. Automated usage: SA được dùng trong CI/CD, automation scripts, không có human oversight trực tiếp
  3. Persistence: Keys không rotate → single exposure = long-term compromise
  4. Broad permissions: Convenience-driven permission grants theo thời gian tích lũy thành overly-broad access

Hiểu sâu về SA model — không chỉ "cách dùng" mà "tại sao thiết kế như vậy và cái gì thực sự nguy hiểm" — là điều kiện tiên quyết để thiết kế workload identity đúng.


Ba loại Service Accounts: Phân biệt quan trọng

1. User-managed Service Accounts

Được tổ chức của bạn tạo ra và quản lý. Email format: NAME@PROJECT_ID.iam.gserviceaccount.com

Đây là loại SA bạn tương tác hàng ngày khi viết Terraform, cấu hình workloads, v.v. Bạn có full control: tạo, delete, grant roles, tạo keys, impersonate.

Vòng đời của user-managed SA:

Create → (optional) Create Keys → Assign to Workload → Grant IAM Roles

  Use (metadata server, workload identity, key-based auth)

Delete (soft-delete 30 ngày, undelete được) → Hard delete (permanent, quota released)

2. Default Service Accounts

Được GCP tự động tạo khi enable một số services. Mỗi project khi bật Compute Engine hoặc App Engine nhận một default SA:

  • Compute Engine default SA: PROJECT_NUMBER-compute@developer.gserviceaccount.com
  • App Engine default SA: PROJECT_ID@appspot.gserviceaccount.com

Default SAs được tạo với Editor role (roles/editor) theo mặc định — đây là vấn đề nghiêm trọng sẽ được phân tích riêng bên dưới.

3. Google-managed Service Agents

Được Google tạo và vận hành để thực hiện các operations nội bộ của GCP. Ví dụ:

  • service-PROJECT_NUMBER@compute-system.iam.gserviceaccount.com — Compute Engine service agent
  • service-PROJECT_NUMBER@container-engine-robot.iam.gserviceaccount.com — GKE service agent
  • service-PROJECT_NUMBER@gcp-sa-cloudbuild.iam.gserviceaccount.com — Cloud Build service agent

Không bao giờ modify permissions của service agents trừ khi có hướng dẫn cụ thể từ GCP docs. Removing hoặc modifying roles của service agents có thể khiến GCP services không hoạt động được trong project của bạn.


Service Account Keys: Cơ Chế và Tại Sao Là Rủi Ro Lớn

Cơ chế của SA keys

SA key là một RSA key pair (2048-bit hoặc 4096-bit):

  • Private key: Được download khi tạo, không được lưu ở Google — nếu mất, phải tạo key mới
  • Public key: Được GCP lưu, dùng để verify signatures của JWT tokens

Khi workload cần authenticate với Google APIs sử dụng SA key:

  1. Workload tạo một JWT (JSON Web Token) signed bằng private key
  2. JWT được gửi đến Google OAuth endpoint để đổi lấy access token
  3. Access token (thường có TTL 1 giờ) được dùng để call GCP APIs

Tại sao SA keys là anti-pattern

1. Long-lived credentials:

SA keys không có expiration date theo mặc định. Một key được tạo năm 2020 vẫn valid nếu không bị explicitly revoked. Trong thực tế:

  • Keys được hardcode vào scripts, forgotten
  • Keys commit vào Git repositories (accident), được crawl bởi scanners
  • Keys được share qua Slack/email "tạm thời", không bao giờ bị revoke

2. Google không thể revoke khi cần:

Nếu phát hiện key bị leak, GCP không biết key đó đang được dùng ở đâu. Revoke key (delete trên GCP) ngay lập tức phá vỡ tất cả legitimate workloads đang dùng key đó. Đây là trade-off không thoải mái.

3. "Key sprawl":

Mỗi SA có thể có tối đa 10 user-managed keys tồn tại đồng thời. Trong một large project, có thể có hàng nghìn keys active — không ai biết key nào đang được dùng thực sự.

4. Audit gap:

Khi workload dùng SA key để authenticate, audit log chỉ thấy "SA này đã gọi API" — không thấy "key nào" được dùng, và không thấy "từ machine nào". Không có traceability đến individual key.

Theo GCP documentation: "Service account keys are a security risk if not managed correctly... Choose a more secure alternative to service account keys whenever possible."

Alternatives tốt hơn SA keys

Workload locationRecommended approach
GKE/GCE/Cloud RunWorkload Identity / Metadata Server (không cần key)
GitHub ActionsWorkload Identity Federation
Jenkins on GCPWorkload Identity Federation
Local developmentgcloud auth application-default login
External system (không GCP)Workload Identity Federation với external OIDC/SAML

SA keys chỉ nên dùng khi không có alternative — tức là workload chạy trong môi trường hoàn toàn không hỗ trợ nào trong bảng trên.


Service Account Impersonation: Cơ Chế Token Exchange

Impersonation cho phép một principal (user hoặc SA) lấy short-lived credentials của một SA khác mà không cần key của SA đó.

Cơ chế bên trong

Khi principal A impersonate SA B:

1. Principal A (đã authenticate) call:
   POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/B@.../generateAccessToken
   {
     "scope": ["https://www.googleapis.com/auth/cloud-platform"]
   }

2. IAM kiểm tra:
   - Principal A có iam.serviceAccounts.getAccessToken permission trên SA B không?
   - (Permission này được bao gồm trong roles/iam.serviceAccountTokenCreator)

3. Nếu authorized:
   - IAM Service Account Credentials API tạo access token của SA B
   - Token có TTL tùy chỉnh (default 1 giờ, max 12 giờ với allow_extended_expiry)
   - Token trả về cho Principal A

4. Principal A dùng token này để call GCP APIs với identity của SA B

Tại sao impersonation tốt hơn keys

  • Short-lived: Token mặc định TTL 1 giờ, không thể persist như keys
  • No credential storage: Principal A không có private key của SA B — token tạo mới mỗi lần
  • Traceable: Audit log chứa cả "who impersonated" và "which SA was impersonated"
  • Revocable: Remove roles/iam.serviceAccountTokenCreator từ Principal A ngay lập tức block future impersonation

Impersonation chains

GCP cho phép delegation chains — A impersonate B, B impersonate C:

bash
# Tạo token của C thông qua chain A → B → C
gcloud auth print-access-token \
  --impersonate-service-account="B@project.iam.gserviceaccount.com,C@project.iam.gserviceaccount.com"

Tuy nhiên, chains dài làm tăng attack surface và khó audit. Production recommendation: không dùng chains dài hơn 2 levels.

Dùng impersonation trong local development

Thay vì download SA key để develop locally, dùng impersonation:

bash
# Login với user account của bạn
gcloud auth login

# Impersonate SA khi chạy local tools
gcloud config set auth/impersonate_service_account my-sa@project.iam.gserviceaccount.com

# Hoặc per-command
gcloud storage ls gs://my-bucket/ --impersonate-service-account=my-sa@project.iam.gserviceaccount.com

# Application Default Credentials với impersonation
export GOOGLE_IMPERSONATE_SERVICE_ACCOUNT=my-sa@project.iam.gserviceaccount.com
# Sau đó Google client libraries sẽ tự impersonate SA này

Default Service Accounts: Tại Sao Là Thảm Họa Bảo Mật

Default Compute Engine SA và Editor role

Khi một project lần đầu sử dụng Compute Engine, GCP tự động tạo:

  • SA email: PROJECT_NUMBER-compute@developer.gserviceaccount.com
  • Tự động gán: roles/editor cho SA này

Khi một VM được tạo mà không specify service account, VM đó tự động chạy với default Compute SA — tức là với roles/editor trên toàn project.

Điều này có nghĩa là: một attacker compromise được bất kỳ process nào trên VM (web app, sidecar container, cron job) đó sẽ có roles/editor — có thể đọc mọi Cloud Storage bucket, gọi mọi API, tạo VM mới, v.v.

Tại sao GCP làm vậy

Đây là di sản từ thời GCP mới ra đời, khi developer experience ưu tiên "just works" hơn security. Editor role để VM có thể gọi các GCP services khác mà không cần cấu hình IAM gì thêm.

Theo GCP documentation: "Google highly recommends that you disable the automatic role grant for default service accounts."

Tại sao không tự động disabled

Vì có rất nhiều existing production workloads dựa vào default SA với Editor role. Breaking change này sẽ ảnh hưởng đến hàng triệu VMs. Google không thể rollback mà không gây outages quy mô lớn.

New projects có thể (và nên) disable automatic Editor grant thông qua Organization Policy:

bash
# Ngăn default SA nhận Editor role tự động
gcloud resource-manager org-policies set-policy \
  --organization=ORG_ID \
  policy.yaml
yaml
# policy.yaml
name: organizations/ORG_ID/policies/iam.automaticIamGrantsForDefaultServiceAccounts
spec:
  rules:
  - enforce: true

Hậu quả và cách mitigate

Nếu đang có VMs đang chạy với default SA:

Kiểm tra VM nào đang dùng default SA:

bash
gcloud compute instances list \
  --format="table(name, zone, serviceAccounts.email)" \
  --filter="serviceAccounts.email:compute@developer.gserviceaccount.com"

Migration plan:

  1. Tạo dedicated SA với chỉ permissions cần thiết cho từng workload
  2. Update VM để dùng SA mới (cần stop VM trên một số machine types)
  3. Sau khi verify, revoke Editor role từ default SA

Không thể delete default SA — GCP creates và quản lý nó. Nhưng có thể revoke Editor role và không assign nó cho any workload.


Service Account as Principal vs Service Account as Resource

Đây là điểm dễ nhầm lẫn: SA có hai vai trò khác nhau trong IAM model.

SA là Principal (Subject)

SA được grant roles → SA có permissions để làm actions:

grant roles/storage.admin to:
  serviceAccount:my-sa@project.iam.gserviceaccount.com

Ở đây, SA là "who" trong "who can do what".

SA là Resource (Object)

SA có IAM policy riêng điều khiển ai có thể làm gì với SA:

IAM policy của my-sa@project.iam.gserviceaccount.com:
  user:alice@example.com → roles/iam.serviceAccountUser
  # (alice có thể deploy workloads với identity của SA này)
  
  user:bob@example.com → roles/iam.serviceAccountTokenCreator
  # (bob có thể tạo tokens của SA này — impersonation)
  
  user:carol@example.com → roles/iam.serviceAccountAdmin
  # (carol có thể modify SA — tạo keys, update, delete)

Ở đây, SA là "what" trong "who can do what to this SA".

Tại sao sự phân biệt này quan trọng:

roles/iam.serviceAccountUser không grant bất kỳ permissions nào mà SA có. Nó chỉ grant quyền attach SA vào resource (ví dụ: tạo VM với SA này, hoặc deploy Cloud Function với SA này). Sau khi workload chạy với identity của SA, workload có permissions của SA — không phải permissions của user who deployed.

Nhiều privilege escalation vectors xảy ra khi user có iam.serviceAccounts.actAs permission (bao gồm trong roles/iam.serviceAccountUser) nhưng không có roles của SA — user có thể deploy một workload với SA có broad permissions, sau đó exploit workload để access resources.


Production Hardening cho Service Accounts

1. Principle of Least Privilege per Workload

Mỗi workload cần SA riêng, với chỉ permissions cần thiết:

bash
# Tạo SA dedicated cho một service
gcloud iam service-accounts create payments-processor \
  --display-name="Payment Processor Service"

# Grant chỉ permissions cần thiết
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:payments-processor@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/pubsub.subscriber"

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:payments-processor@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/bigquery.dataViewer"

2. Workload Identity thay vì Keys

Cho GKE workloads, dùng Workload Identity Federation (chi tiết ở Chapter 13):

yaml
# kubernetes service account annotation
apiVersion: v1
kind: ServiceAccount
metadata:
  name: payments-processor
  namespace: payments
  annotations:
    iam.gke.io/gcp-service-account: payments-processor@PROJECT_ID.iam.gserviceaccount.com

3. SA Key rotation policy

Nếu buộc phải dùng SA keys:

  • Rotate ít nhất mỗi 90 ngày
  • Không bao giờ commit key files vào Git
  • Dùng Secret Manager để store keys, không phải environment variables
  • Audit iam.serviceAccounts.createKey events trong Cloud Audit Logs
  • Dùng Organization Policy để enforce key creation limitations:
bash
# Disable SA key creation cho tất cả SAs (ngoài exceptions)
gcloud resource-manager org-policies set-policy \
  --organization=ORG_ID \
  iam.disableServiceAccountKeyCreation.yaml

4. Phát hiện unused SA keys

bash
# List tất cả SA keys và khi cuối cùng được sử dụng
gcloud iam service-accounts keys list \
  --iam-account=my-sa@project.iam.gserviceaccount.com \
  --format="table(name, keyType, validAfterTime, validBeforeTime)"

# Keys không được dùng trong 90 ngày nên được xem xét revoke
# (IAM Recommender tự động đề xuất điều này)

References