Workload Identity Federation — Cắt Đứt Con Đường Tới GCP API
Vì sao chủ đề này là trung tâm của bảo mật GKE
Hầu hết workload trên GKE cần gọi GCP API: đọc/ghi Cloud Storage, publish Pub/Sub, query BigQuery, đọc secret từ Secret Manager. Câu hỏi "workload chứng minh danh tính với Google bằng cách nào" là một trong những quyết định bảo mật quan trọng nhất của toàn bộ cluster — vì câu trả lời sai tạo ra chính xác con đường mà kẻ tấn công cần để leo thang từ một Pod bị chiếm lên quyền trên toàn project.
Câu trả lời cũ và sai: tạo một IAM service account, export một JSON key dài hạn, nhét vào Pod như một Kubernetes Secret, và để code đọc key đó. Câu trả lời đúng và hiện đại: Workload Identity Federation for GKE — workload chứng minh danh tính qua một token short-lived được trao đổi tự động, không có key tĩnh nào tồn tại ở bất kỳ đâu.
File này tập trung vào góc bảo mật: tại sao key tĩnh nguy hiểm, Workload Identity cắt đứt vector tấn công nào, và cơ chế của nó ở mức đủ để ra quyết định kiến trúc. Deep-dive đầy đủ về token exchange path, troubleshooting, và các mô hình federation phức tạp được trình bày ở Chương 13 — chương dành riêng cho Workload Identity.
Internal model: vì sao long-lived key là credential tệ nhất
Một service account JSON key có những đặc tính khiến nó trở thành vector tấn công lý tưởng:
- Không hết hạn. Key sống cho đến khi bị xóa thủ công. Một key lộ năm 2023 vẫn dùng được năm 2026 nếu không ai để ý.
- Tự chứa toàn bộ credential. Key chứa private key đủ để mạo danh service account. Không cần gì thêm — ai có file là có quyền của SA.
- Bị sao chép khắp nơi. Key đi vào Git (vô tình commit), image Docker (build vào layer), etcd (như Kubernetes Secret, base64 không mã hóa thật), CI variable, máy developer, Slack message. Mỗi bản sao là một điểm lộ.
- Khó audit và khó rotate. Không có cách dễ để biết key đang ở đâu, ai dùng, có nên xoay vòng không. Rotate đòi hỏi cập nhật mọi nơi đang dùng — thường không ai dám động vào.
Theo thống kê sự cố thực tế của Google Cloud, service account key bị lộ là một trong những nguyên nhân hàng đầu của compromise. Đây là lý do tài liệu hardening của GKE nêu thẳng: Workload Identity Federation là "the recommended way to authenticate to Google Cloud APIs" — phương thức được khuyến nghị, và việc dùng key tĩnh nên được coi là anti-pattern cần loại bỏ.
Vector tấn công mà Workload Identity loại bỏ
Hãy hình dung con đường tấn công kinh điển khi không có Workload Identity:
1. Kẻ tấn công khai thác RCE trong app → có shell trong container
2. Đọc Kubernetes Secret mount vào Pod → lấy SA JSON key
3. Dùng key xác thực với GCP → mạo danh service account
4. SA có quyền rộng (vì "cho tiện") → đọc mọi bucket, mọi secret
5. Key không hết hạn → kẻ tấn công giữ quyền vĩnh viễn, kể cả sau khi Pod bị xóaWorkload Identity cắt đứt bước 2–5: không có key để đọc (bước 2 thất bại), và token được cấp là short-lived gắn với KSA cụ thể (kể cả nếu lấy được token, nó hết hạn nhanh và chỉ có quyền của KSA đó).
Workload Identity Federation for GKE: cơ chế
Ý tưởng cốt lõi: thay vì cấp cho workload một credential của Google, ta dạy Google tin tưởng danh tính Kubernetes ServiceAccount (KSA) của workload, và trao đổi danh tính đó lấy credential Google short-lived khi cần.
Workload Identity Pool
Khi bật Workload Identity, GKE tạo một workload identity pool cố định cho project, theo định dạng:
PROJECT_ID.svc.id.googTheo tài liệu Workload Identity Federation for GKE, pool này "provides a naming format that allows IAM to understand and trust Kubernetes credentials" — cung cấp định dạng tên để IAM hiểu và tin tưởng credential Kubernetes. Pool là cây cầu khái niệm: nó cho phép IAM tham chiếu một KSA như một principal IAM hợp lệ.
Ánh xạ KSA → IAM principal
Mỗi KSA được biểu diễn trong IAM bằng một principal identifier theo cú pháp:
principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAMEĐịnh danh này nhúng đầy đủ: project, pool, namespace, và tên KSA. Nhờ vậy, một IAM policy binding có thể cấp quyền GCP trực tiếp cho một KSA cụ thể trong một namespace cụ thể — granularity đúng bằng workload, không rộng hơn.
Token exchange ba bước qua GKE metadata server
Khi code trong Pod gọi GCP API qua Application Default Credentials (ADC), điều sau xảy ra tự động, trong suốt với ứng dụng:
1. Thư viện client (ADC) hỏi token từ metadata endpoint
http://metadata.google.internal (giống như trên Compute Engine)
2. GKE metadata server (chạy trên node) chặn request, lấy một
KSA JWT từ Kubernetes API server cho KSA của Pod này
3. Metadata server đổi KSA JWT qua Security Token Service (STS)
lấy một "short-lived federated access token" của Google
4. Token được trả về cho workload, dùng để gọi GCP API
với quyền của IAM principal/SA mà KSA được phép mạo danhĐiểm bảo mật then chốt: không có credential dài hạn nào tham gia. KSA JWT là short-lived và bound; federated access token là short-lived. Toàn bộ chuỗi tự refresh. Ứng dụng không thấy, không lưu, không quản lý credential nào — nó chỉ gọi ADC như bình thường.
Để cơ chế này hoạt động, node pool phải bật GKE_METADATA mode (metadata server của Workload Identity), thay cho metadata server Compute Engine thông thường. Đây là cấu hình --workload-metadata=GKE_METADATA ở node pool.
Hai cách cấp quyền: principal trực tiếp vs annotation legacy
Có hai mô hình cấu hình, và tài liệu hiện khuyến nghị mô hình mới:
Mô hình mới (khuyến nghị) — IAM binding trực tiếp cho KSA principal:
Cấp role GCP trực tiếp cho principal của KSA, không cần IAM service account trung gian, không cần annotation:
gcloud projects add-iam-policy-binding PROJECT_ID \
--role="roles/storage.objectViewer" \
--member="principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/PROJECT_ID.svc.id.goog/subject/ns/NAMESPACE/sa/KSA_NAME" \
--condition=NoneKSA được cấp quyền GCP một cách trực tiếp. Đơn giản hơn, ít thành phần hơn, ít chỗ sai hơn.
Mô hình legacy — impersonate một IAM service account qua annotation:
KSA được annotate trỏ tới một IAM SA, và IAM SA cấp roles/iam.workloadIdentityUser cho KSA:
apiVersion: v1
kind: ServiceAccount
metadata:
name: KSA_NAME
namespace: NAMESPACE
annotations:
iam.gke.io/gcp-service-account: GSA_NAME@PROJECT_ID.iam.gserviceaccount.com# IAM SA tin tưởng KSA mạo danh nó
gcloud iam service-accounts add-iam-policy-binding \
GSA_NAME@PROJECT_ID.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="serviceAccount:PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]"Theo tài liệu, mô hình mới (IAM binding trực tiếp cho principal) được khuyến nghị hơn vì "eliminates the need for manual ServiceAccount annotation configuration" — loại bỏ bước annotate thủ công và lớp IAM SA trung gian. Tuy nhiên mô hình annotation vẫn phổ biến vì nhiều tổ chức đã chuẩn hóa quyền theo IAM service account; chọn mô hình nào tùy vào việc bạn quản lý quyền theo KSA hay theo IAM SA.
Workload Identity Federation cho external IdP
Cùng một cơ chế federation mở rộng ra ngoài GKE: Workload Identity Federation (không gắn GKE) cho phép workload chạy ở AWS, Azure, on-prem, hoặc bất kỳ OIDC provider nào đổi token của IdP đó lấy Google credential short-lived — vẫn không cần SA key.
Ứng dụng bảo mật điển hình:
- CI/CD runner (GitHub Actions, GitLab CI) đổi OIDC token của runner lấy quyền deploy lên GCP, thay vì lưu SA key trong CI secret. Đây là cách loại bỏ SA key khỏi pipeline — một trong những nơi key bị lộ nhiều nhất.
- Multi-cloud workload chạy trên EKS/AKS gọi GCP API bằng cách federate identity của cloud kia.
Mô hình chung: tạo một workload identity pool + provider mô tả IdP bên ngoài và điều kiện tin cậy (attribute mapping, attribute condition), rồi cấp quyền GCP cho principal khớp điều kiện. Federation cho external IdP là chủ đề rộng; deep-dive thuộc về Chương 13, nhưng nguyên tắc bảo mật giống hệt GKE: niềm tin dựa trên token short-lived được verify, không dựa trên key tĩnh.
Production architecture patterns
Pattern: zero long-lived key trên toàn tổ chức
Mục tiêu kiến trúc nên là không một service account key nào tồn tại: workload trong GKE dùng Workload Identity for GKE; CI/CD dùng Workload Identity Federation từ runner; multi-cloud dùng federation từ IdP cloud kia. Để enforce, dùng Org Policy constraint iam.disableServiceAccountKeyCreation chặn việc tạo SA key ở cấp tổ chức. Khi tạo key bị cấm ở tầng policy, đội ngũ buộc phải dùng federation — chính sách biến least-privilege thành mặc định không thể lách.
Pattern: KSA-per-workload ánh xạ quyền tối thiểu
Mỗi workload có KSA riêng, và mỗi KSA chỉ được cấp đúng quyền GCP nó cần. Service xử lý ảnh chỉ có storage.objectAdmin trên đúng một bucket; service đọc cấu hình chỉ có secretmanager.secretAccessor trên đúng các secret của nó. Vì IAM binding gắn trực tiếp vào KSA principal, granularity đạt mức từng workload. Nếu một workload bị chiếm, quyền GCP lộ ra đúng bằng nhu cầu của workload đó — không hơn.
Common mistakes / anti-patterns
1. Vẫn dùng SA key "vì đã quen". Tạo SA, export key, mount Secret. Vì sao xảy ra: quán tính, tutorial cũ, "Workload Identity phức tạp". Hệ quả: vector lộ credential số một vẫn mở. Phòng tránh: bật Workload Identity, dùng Org Policy chặn tạo key, migrate workload sang federation.
2. Bật Workload Identity nhưng không bật GKE_METADATA trên node pool. Cluster bật Workload Identity nhưng một node pool cũ vẫn dùng metadata server Compute Engine. Hệ quả: Pod trên node pool đó vẫn lấy được token của node service account qua metadata — đúng vector mà Workload Identity định cắt. Phòng tránh: đảm bảo mọi node pool đặt --workload-metadata=GKE_METADATA; kiểm tra không còn pool nào ở chế độ cũ.
3. Cấp quyền GCP quá rộng cho KSA. Workload Identity loại bỏ key, nhưng nếu KSA được cấp roles/editor ở project thì một Pod bị chiếm vẫn có quyền khổng lồ. Hệ quả: Workload Identity bảo vệ credential nhưng không bảo vệ khỏi quyền dư thừa. Phòng tránh: KSA-per-workload + IAM role tối thiểu trên đúng resource cần.
4. Không bật metadata concealment trên cluster cũ. Trên node pool không dùng GKE_METADATA, Pod truy cập được metadata endpoint của node và lấy token node SA. Hệ quả: bypass toàn bộ Workload Identity. Phòng tránh: GKE_METADATA mode (đi kèm Workload Identity) chính là cơ chế chặn Pod truy cập metadata node; xác nhận nó bật trên mọi pool (xem thêm file 05 về node service account).
5. Dùng default KSA cho mọi Pod với Workload Identity. Gán quyền GCP cho KSA default của một namespace = mọi Pod trong namespace đó đều có quyền đó. Hệ quả: granularity biến mất, mọi workload chung quyền. Phòng tránh: KSA riêng có tên cho mỗi workload, không dùng default.
GCP-native implementation guidance
# Bật Workload Identity khi tạo cluster
gcloud container clusters create CLUSTER \
--region REGION \
--workload-pool=PROJECT_ID.svc.id.goog
# Đảm bảo node pool dùng GKE_METADATA (chặn truy cập metadata node)
gcloud container node-pools update POOL \
--cluster CLUSTER --region REGION \
--workload-metadata=GKE_METADATA
# Kiểm tra mọi node pool có ở chế độ GKE_METADATA không (cờ đỏ nếu thấy GCE_METADATA)
gcloud container node-pools list --cluster CLUSTER --region REGION \
--format="table(name, config.workloadMetadataConfig.mode)"
# Chặn tạo SA key ở cấp tổ chức (Org Policy)
gcloud resource-manager org-policies enable-enforce \
iam.disableServiceAccountKeyCreation \
--organization ORG_IDKiểm chứng từ bên trong Pod rằng Workload Identity hoạt động và metadata node bị che: từ một Pod, gọi metadata server và xác nhận nó trả về danh tính của KSA-mapped principal chứ không phải node SA, đồng thời các path nhạy cảm của metadata node bị chặn (GKE_METADATA chỉ expose những gì an toàn).
Operational implications
Workload Identity thay đổi mô hình vận hành credential từ "quản lý vòng đời key" sang "quản lý IAM binding". Không còn rotation, không còn lo key trong Git, không còn audit "key này ở đâu". Đổi lại, trách nhiệm chuyển sang giữ IAM binding của KSA tối thiểu và đúng — một bài toán authorization thuần túy, dễ audit hơn nhiều (chỉ cần đọc IAM policy) so với bài toán "truy vết mọi bản sao của một file key".
Một hệ quả tinh tế cho debugging: khi một workload gặp lỗi permission gọi GCP API, điểm điều tra giờ là IAM binding của KSA principal, không phải "key có đúng không". Lỗi thường gặp là binding sai namespace/tên KSA trong principal string, hoặc node pool chưa bật GKE_METADATA. Vì toàn bộ chuỗi token là tự động và short-lived, không có trạng thái credential nào để "kẹt" — chỉ có cấu hình binding đúng hoặc sai. Đây là một sự đơn giản hóa lớn về mặt vận hành so với mô hình key, và là lý do Workload Identity nên là mặc định cho mọi cluster mới. Chi tiết token path, attribute mapping, và các mô hình federation nâng cao tiếp tục ở Chương 13.