Fleet Workload Identity — Định Danh Thống Nhất Xuyên Cluster
Tại Sao Quan Trọng Trong Production
Workload Identity Federation (WIF) là cơ chế cho phép Kubernetes workloads authenticate đến Google Cloud services mà không cần service account keys. Ở quy mô một cluster, WIF đã giải quyết tốt bài toán này. Nhưng khi bạn có 20+ clusters chạy cùng workload (payments service chạy trên 5 regions), bạn gặp vấn đề mới: identity fragmentation.
Với per-cluster WIF, mỗi cluster có OIDC pool riêng theo format PROJECT_ID.svc.id.goog. Workload chạy trên cluster A có principal serviceAccount:PROJECT.svc.id.goog[NAMESPACE/KSA], workload trên cluster B có principal serviceAccount:PROJECT.svc.id.goog[NAMESPACE/KSA]. Nếu hai cluster cùng project, chúng có cùng OIDC pool — IAM không thể phân biệt workload đang chạy trên cluster nào. Nếu hai cluster khác project, bạn cần maintain IAM bindings riêng cho từng principal.
Kết quả: với 10 clusters trên 5 projects chạy 30 services, bạn cần quản lý hàng trăm IAM bindings riêng biệt. Bất kỳ thay đổi quyền truy cập nào cũng phải được apply trên mọi cluster — sai một cái là security gap.
Fleet Workload Identity giải quyết vấn đề này bằng cách tạo một identity pool ở fleet level, độc lập với cluster nào đang chạy workload.
Per-Cluster WIF và Vấn Đề Cơ Bản
Để hiểu Fleet WI, cần hiểu trước giới hạn của per-cluster WIF.
Cơ Chế Per-Cluster WIF
Khi per-cluster Workload Identity được enable trên GKE cluster, mỗi cluster được cấu hình với một Workload Identity Pool theo format:
{PROJECT_ID}.svc.id.googKhi Pod với KSA được annotate chạy, GKE metadata server intercept calls đến metadata.google.internal và:
- Phát hiện Pod đang request token
- Trao đổi KSA token (được signed bởi cluster's API server) với Google's STS (Security Token Service)
- STS verify KSA token dựa trên cluster's OIDC issuer configuration
- STS phát hành Google-signed token cho principal
serviceAccount:{PROJECT_ID}.svc.id.goog[{NAMESPACE}/{KSA_NAME}]
IAM binding cho workload này trông như:
members:
- serviceAccount:my-project.svc.id.goog[payments/payment-processor]
role: roles/storage.objectViewerVấn Đề Khi Scale
Vấn đề 1: Same project, nhiều clusters. Nếu cluster-A và cluster-B cùng project my-project, cả hai đều dùng pool my-project.svc.id.goog. IAM binding cho serviceAccount:my-project.svc.id.goog[payments/payment-processor] sẽ grant quyền cho workload đó trên cả hai clusters. Bạn không thể distinguish: "chỉ cluster-A được phép, cluster-B không được".
Điều này đặc biệt nguy hiểm trong scenarios như: cluster-prod được phép đọc production secrets, cluster-dev chỉ được đọc dev secrets. Nếu chúng cùng project pool, cùng workload name/namespace trên dev cluster có thể access production secrets.
Vấn đề 2: Nhiều projects, nhiều bindings. Nếu 10 clusters trên 10 projects khác nhau đều chạy payments/payment-processor và service này cần quyền như nhau, bạn cần 10 IAM bindings riêng biệt — một cho mỗi pool. Mỗi khi thêm cluster, thêm binding. Automation có thể giảm bớt nhưng không loại bỏ complexity.
Vấn đề 3: Cross-project cluster là common pattern. Fleet thường có clusters từ nhiều projects (dev project, staging project, prod project). Per-cluster WIF không có cơ chế thống nhất identity xuyên project boundaries.
Fleet Workload Identity — Cơ Chế Nội Tại
Fleet Workload Identity Pool
Fleet Workload Identity tạo một Workload Identity Pool tại fleet level, không gắn với bất kỳ cluster cụ thể nào:
{FLEET_PROJECT_NUMBER}.global.id.googChú ý sự khác biệt quan trọng:
- Per-cluster pool:
{PROJECT_ID}.svc.id.goog— string project ID, suffix.svc - Fleet pool:
{FLEET_PROJECT_NUMBER}.global.id.goog— project number (không phải ID), suffix.global
Dùng project number thay vì project ID là thiết kế có chủ ý: project number là immutable (không thể thay đổi sau khi tạo), trong khi project ID có thể conflict. Fleet pool URL là stable identifier.
OIDC Token Structure và Principal Format
Khi Fleet WI được enable, mỗi cluster trong fleet được cấu hình với OIDC issuer URL trỏ vào fleet-level endpoint. Thay vì cluster-specific OIDC issuer, token được issued với:
- Issuer:
https://gkehub.googleapis.com/projects/{FLEET_PROJECT_NUMBER}/locations/global/memberships/{MEMBERSHIP_ID} - Subject (sub):
system:serviceaccount:{NAMESPACE}:{KSA_NAME} - Audience:
{FLEET_PROJECT_NUMBER}.global.id.goog
STS (Security Token Service) verify token này và phát hành Google token với principal:
serviceAccount:{FLEET_PROJECT_NUMBER}.global.id.goog[{MEMBERSHIP_NAMESPACE}/{MEMBERSHIP_ID}/{NAMESPACE}/{KSA_NAME}]Ví dụ cụ thể:
- Fleet project number:
123456789012 - Membership ID:
cluster-prod-us-east1 - Namespace:
payments - KSA:
payment-processor
Principal sẽ là:
serviceAccount:123456789012.global.id.goog[cluster-prod-us-east1/payments/payment-processor]IAM Binding Với Fleet WI
Với principal format trên, IAM binding để grant quyền cho cùng workload xuyên tất cả clusters:
{
"bindings": [
{
"role": "roles/secretmanager.secretAccessor",
"members": [
"serviceAccount:123456789012.global.id.goog[*/payments/payment-processor]"
]
}
]
}Wildcard * match bất kỳ membership ID nào, tức là workload payment-processor trong namespace payments trên bất kỳ cluster nào trong fleet đều được grant quyền này.
Nếu bạn chỉ muốn grant cho specific cluster:
serviceAccount:123456789012.global.id.goog[cluster-prod-us-east1/payments/payment-processor]Đây là granularity mà per-cluster WIF không cung cấp được — khả năng viết IAM binding mà trong đó cluster membership ID là một phần của principal identifier.
KSA Annotation và Fleet Context
Để workload sử dụng Fleet WI, KSA không cần annotation đặc biệt khác với per-cluster WI. Sự khác biệt nằm ở cluster configuration:
# Enable fleet workload identity khi đăng ký cluster
gcloud container fleet memberships register MEMBERSHIP_ID \
--gke-cluster=LOCATION/CLUSTER_NAME \
--enable-workload-identity \
--project=FLEET_PROJECT
# Hoặc enable khi tạo GKE cluster với fleet
gcloud container clusters create CLUSTER_NAME \
--fleet-project=FLEET_PROJECT \
--workload-pool=FLEET_PROJECT_NUMBER.global.id.googFlag --workload-pool=FLEET_PROJECT_NUMBER.global.id.goog là thứ quyết định cluster dùng fleet-level pool thay vì per-cluster pool.
Khi cluster được cấu hình với fleet WI pool, GKE metadata server trên node sẽ trao đổi KSA token với OIDC token theo fleet pool format, không phải per-cluster format.
Fleet WI vs Per-Cluster WI: Khi Nào Dùng Cái Nào
Sử Dụng Fleet Workload Identity Khi
1. Workload deployment xuyên nhiều clusters là pattern thường xuyên: Nếu payments service chạy trên 5 clusters ở 5 regions và cần cùng quyền truy cập GCS bucket, Fleet WI cho phép một IAM binding thay vì 5 bindings.
2. Muốn per-cluster granularity trong IAM: Paradoxically, Fleet WI cung cấp nhiều granularity hơn per-cluster WI cho multi-cluster scenarios. Bạn có thể grant workload trên cluster-prod quyền khác với cùng workload trên cluster-dev bằng cách bind khác nhau trên membership ID.
3. Cross-project clusters: Khi clusters từ nhiều projects cần cùng workload identity, Fleet WI là giải pháp duy nhất thực tế.
Giữ Per-Cluster Workload Identity Khi
1. Single-cluster setup hoặc cluster số lượng rất ít: Overhead của fleet management không worth it cho 1–2 clusters.
2. Clusters không join fleet: Hiển nhiên — Fleet WI yêu cầu cluster là fleet member.
3. Cần backward compatibility với existing IAM bindings: Đổi từ per-cluster sang fleet WI là breaking change — principal format thay đổi hoàn toàn. Tất cả IAM bindings hiện tại phải được cập nhật.
Migration Từ Per-Cluster Sang Fleet WI
Đây là operation có downtime nếu không cẩn thận. Quy trình an toàn:
Phase 1: Enable Fleet WI cạnh Per-Cluster WI
- Đăng ký cluster vào fleet với fleet WI pool
- Cluster lúc này có CẢ HAI: per-cluster pool và fleet pool
Phase 2: Update IAM bindings
- Tạo IAM bindings mới với fleet principal format
- Giữ nguyên bindings cũ (per-cluster format)
- Test workloads đang dùng fleet token
Phase 3: Remove per-cluster WI annotations (nếu cần)
- Update workloads để chỉ dùng fleet WI
- Xóa IAM bindings cũ (per-cluster format) sau khi verifyThực tế, per-cluster và fleet WI có thể coexist trong giai đoạn migration. Metadata server quyết định pool nào được dùng dựa trên cluster configuration.
Constraints Và Failure Modes
Constraint 1: Project number vs Project ID Fleet WI dùng project number (numeric), không phải project ID (string). Project number không thay đổi suốt vòng đời project nhưng ít "human readable" hơn. Tự động hóa IAM management phải dùng project number, không phải project ID, trong fleet WI context.
Constraint 2: Global location Fleet WI pool là global, không phải regional. Điều này có nghĩa là STS verification xảy ra ở Google global infrastructure, không phải regional. Latency thêm vào tùy thuộc vào vị trí cluster nhưng thông thường không đáng kể.
Constraint 3: Membership ID là một phần principal Principal bao gồm membership ID. Nếu cluster được unregister và re-register vào fleet với membership ID khác, principal sẽ thay đổi và IAM bindings phải cập nhật. Đây là lý do nên chọn membership ID ổn định (thường match cluster name).
Constraint 4: Enable workload identity khi registration Fleet WI phải được enable tại thời điểm đăng ký cluster (hoặc cluster creation). Retroactively enable Fleet WI trên existing cluster yêu cầu re-registration.
Failure mode: Token exchange timeout Nếu cluster không thể reach Google STS endpoint (ví dụ private cluster không có proper egress), token exchange sẽ fail. Workloads sẽ thấy 401 Unauthorized khi gọi Google APIs. Cần đảm bảo sts.googleapis.com và iamcredentials.googleapis.com accessible từ cluster (qua Cloud NAT hoặc Private Google Access).
Failure mode: Membership ID mismatch Nếu cluster được configure với fleet WI pool nhưng Membership resource chưa tạo (race condition trong automation), token exchange sẽ fail vì STS không thể validate OIDC token mà không có Membership configuration.
Ví Dụ Production Pattern
Một fintech company với payments service chạy trên 6 clusters (2 per region, active-active):
# IAM policy để grant payments service access Secret Manager
bindings:
- members:
# Tất cả clusters trong fleet (wildcard)
- serviceAccount:123456789012.global.id.goog[*/payments/payment-processor]
role: roles/secretmanager.secretAccessor
condition:
title: only_payments_secrets
expression: resource.name.startsWith("projects/my-project/secrets/payments-")
- members:
# Chỉ production clusters được phép access production DB credentials
- serviceAccount:123456789012.global.id.goog[cluster-prod-us-east1/payments/payment-processor]
- serviceAccount:123456789012.global.id.goog[cluster-prod-eu-west1/payments/payment-processor]
- serviceAccount:123456789012.global.id.goog[cluster-prod-asia-east1/payments/payment-processor]
role: roles/secretmanager.secretAccessor
condition:
title: production_db_creds
expression: resource.name.startsWith("projects/my-project/secrets/db-prod-")Pattern này:
- Grant tất cả clusters quyền đọc payments-prefixed secrets (shared config)
- Chỉ production clusters mới có quyền đọc production DB credentials
- Dev/staging clusters không thể access production secrets dù cùng workload name
Điều này không thể thực hiện với per-cluster WIF nếu clusters cùng project (IAM không distinguish clusters).