GKE Workload Identity Federation with Direct Principal Grants without Intermediate Service Accounts
GKE Workload Identity Federation with Direct Principal Grants without Intermediate Service Accounts

La gestione dell'identità applicativa su Kubernetes rappresenta uno dei pilastri critici della postura di sicurezza enterprise. Negli ambienti cloud ibridi e regolamentati, come il settore bancario e dei pagamenti digitali, garantire che un microservizio in esecuzione su un cluster Google Kubernetes Engine (GKE) possa invocare API Google Cloud (Cloud Storage, Secret Manager, Cloud KMS, BigQuery) senza ricorrere a segreti statici è un requisito di conformità non negoziabile.
Per anni, lo standard di riferimento su Google Cloud è stato GKE Workload Identity, implementato mediante un modello di impersonation: il Kubernetes Service Account (KSA) riceveva il ruolo IAM roles/iam.workloadIdentityUser su una Google Service Account (GSA) dedicata, permettendo al GKE Metadata Server di emettere un token OAuth2 a valle di una catena di scambi crittografici. Questo approccio ha rappresentato una pietra miliare nella sicurezza cloud-native, eliminando l'uso delle chiavi di servizio JSON statiche nei Secret del cluster. Tuttavia, su scala enterprise con decine di cluster e centinaia di microservizi, richiede la gestione e il censimento di una Service Account GCP per ogni workload applicativo.
L'evoluzione nativa di IAM su Google Cloud consente oggi di affiancare e semplificare questo pattern attraverso i Direct Principal Grants di Workload Identity Federation: assegnare i permessi IAM direttamente all'identificatore canonico del KSA, eliminando la Google Service Account intermedia e il doppio passaggio di impersonation.
L'Anatomia del Doppio Hop: Il Flusso dell'Impersonation Classica
Nel pattern convenzionale di Workload Identity, l'identità del pod e l'identità IAM cloud vivono in due perimetri distinti che richiedono una mappatura esplicita tra Kubernetes e Google Cloud:
- KSA locale: Creato nel namespace Kubernetes dell'applicazione (es.
payment-processornel namespacepayments). - GSA remota: Creata nel progetto Google Cloud (es.
gsa-payment-processor@prj-core-banking-prod.iam.gserviceaccount.com).
Il collegamento operativo richiede due passaggi di configurazione distinti. In primo luogo, l'assegnazione del ruolo roles/iam.workloadIdentityUser sulla GSA all'entità federata del KSA:
resource "google_service_account_iam_member" "workload_identity_user" {
service_account_id = google_service_account.app_gsa.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:prj-core-banking-prod.svc.id.goog[payments/payment-processor]"
}
In secondo luogo, il KSA deve essere decorato con l'annotazione specifica per istruire il webhook di mutazione di GKE su quale GSA impersonare a runtime:
apiVersion: v1
kind: ServiceAccount
metadata:
name: payment-processor
namespace: payments
annotations:
iam.gke.io/gcp-service-account: gsa-payment-processor@prj-core-banking-prod.iam.gserviceaccount.com
A runtime, l'emissione del token attraversa una catena a doppio hop: uno scambio iniziale con Google Security Token Service (STS) per validare l'identità OIDC del KSA, seguito dalla chiamata di impersonation alla IAM Credentials API:
Caratteristiche e Considerazioni sul Modello a Impersonation
Il pattern basato su GSA impersonation rappresenta una soluzione solida, pienamente supportata e ancora oggi ampiamente adottata in produzione su Google Cloud, in particolare quando la medesima Service Account deve essere condivisa trasversalmente con altri servizi (come Cloud Run o istanze Compute Engine). Tuttavia, all'interno di architetture Kubernetes enterprise con centinaia di microservizi, emergono alcune considerazioni operative e architetturali:
- Gestione del Ciclo di Vita delle GSA: Richiede il censimento e la profilazione di una GSA per ciascun KSA applicativo (rapporto 1:1), aumentando il volume di entità IAM da manutenere nei progetti.
- Tracciamento nei Cloud Audit Logs: L'attributo principale (
principalEmail) registra l'indirizzo della GSA intermedia. Per identificare con precisione quale specifico KSA o pod abbia generato la richiesta, le analisi di incident response o audit devono estrarre i metadati di delega annidati (serviceAccountDelegationInfo). - Passaggi di Minting del Token: A runtime, il Metadata Server deve prima eseguire lo scambio con Google STS e successivamente invocare l'API
iam.serviceAccounts.getAccessTokenper emettere il token della GSA. - Presidio del Perimetro dei Privilegi: L'associazione tra KSA e GSA richiede una governance attenta per evitare che più KSA condividano la medesima GSA allargando inavvertitamente il raggio d'azione dei privilegi.
L'Architettura Direct Principal Grant
Con i Direct Principal Grants, Google Cloud estende il motore di Workload Identity Federation nativo direttamente ai cluster GKE. Non viene creata alcuna Service Account intermedia a livello di progetto Google Cloud: il Kubernetes Service Account diventa un principal IAM di primo livello riconosciuto nativamente da Cloud IAM.
In questo modello a singolo hop, il token federato emesso da STS concede direttamente l'accesso alle risorse target:
Quando si abilita Workload Identity su un cluster GKE, Google Cloud crea automaticamente un Workload Identity Pool dedicato associato al progetto ospitante, con la sintassi convenzionale:
{PROJECT_ID}.svc.id.goog
All'interno di questo pool, ogni KSA instanziato in un cluster possiede un identificatore uniforme della risorsa (URI) immutabile e deterministico:
principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{PROJECT_ID}.svc.id.goog/subject/ns/{NAMESPACE}/sa/{KSA_NAME}
In alternativa, Cloud IAM supporta la sintassi legacy contratta per retrocompatibilità:
serviceAccount:{PROJECT_ID}.svc.id.goog[{NAMESPACE}/{KSA_NAME}]
L'adozione del formato completo principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/... è la sintassi ufficiale raccomandata da Google per le architetture Zero Trust moderne. Permette la federazione granulare a livello di attributi e supporta controlli condizionali IAM avanzati (IAM Conditions) basati su attributi del token OIDC Kubernetes, come etichette dei namespace o claim crittografici.
Implementazione Infrastructure as Code con Terraform
L'implementazione dei Direct Principal Grants elimina il blocco di risorse google_service_account per i carichi applicativi, riducendo il codice IaC a un'assegnazione diretta dei ruoli sulle risorse di destinazione.
Nel seguente scenario enterprise, un'applicazione bancaria settlement-engine nel namespace settlement deve leggere segreti da Secret Manager e decifrare transazioni tramite Cloud KMS, senza alcuna GSA intermedia.
1. Dichiarazione delle Risorse IAM Dirette
data "google_project" "current" {
project_id = var.project_id
}
locals {
# Calcolo del principal identifier canonico per il KSA
ksa_principal = "principal://iam.googleapis.com/projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${var.project_id}.svc.id.goog/subject/ns/${var.namespace}/sa/${var.ksa_name}"
}
# Assegnazione accesso in lettura a Secret Manager direttamente al KSA
resource "google_secret_manager_secret_iam_member" "settlement_secrets" {
project = var.project_id
secret_id = "settlement-database-credentials"
role = "roles/secretmanager.secretAccessor"
member = locals.ksa_principal
}
# Assegnazione ruolo di decifratura Cloud KMS direttamente al KSA
resource "google_kms_crypto_key_iam_member" "settlement_crypto" {
crypto_key_id = var.kms_crypto_key_id
role = "roles/cloudkms.cryptoKeyDecrypter"
member = locals.ksa_principal
}
2. Configurazione del Manifest Kubernetes (KSA Pulito)
Sul versante Kubernetes, scompare qualsiasi riferimento a email di Service Account Google. Il KSA rimane una risorsa pura e disaccoppiata dall'infrastruttura esterna:
apiVersion: v1
kind: ServiceAccount
metadata:
name: settlement-engine
namespace: settlement
labels:
app.kubernetes.io/name: settlement-engine
app.kubernetes.io/part-of: core-banking
automountServiceAccountToken: true
Nessuna annotazione iam.gke.io/gcp-service-account è richiesta. Il GKE Metadata Server rileva la richiesta del token OIDC proiettato da parte del pod, lo valida rispetto all'issuer del cluster (https://container.googleapis.com/v1/projects/.../locations/.../clusters/...) e presenta direttamente l'identità del KSA a Cloud IAM.
Matrice Comparativa: GSA Impersonation vs Direct Principal Grant
| Caratteristica | GSA Impersonation (Tradizionale) | Direct Principal Grant (Federato) |
|---|---|---|
| Numero di GSA Create | 1:1 per ciascun KSA applicativo | Zero GSA intermedie |
| Audit Log Primario | Email della GSA intermedia | URI canonico del KSA (principal://...) |
| Passaggi Minting Token | 2 hop (STS + getAccessToken) |
1 hop (STS federato diretto) |
| Configurazione KSA | Annotazione richiesta sul manifest | KSA standard, nessuna annotazione |
| Blast Radius & Perimetro | Condivisione GSA da presidiare | Isolato per singolo namespace e KSA |
| Funzioni di Controllo (DORA) | Audit trail tramite tracciamento delega | Audit trail nativo, granulare e diretto |
| Supporto IAM Conditions | Applicabile alla relazione di delega | Pieno supporto su claim OIDC del KSA |
Verifiche di Sicurezza e Cloud Audit Logs
Uno dei vantaggi più evidenti per i Cloud Security Architect e per le funzioni di controllo (Audit interno, Risk Management, Compliance orientate a DORA e PCI-DSS v4) risiede nella purezza del dato di audit.
Quando il microservizio accede a un segreto o a un bucket storage, la voce di registro registrata in Cloud Logging isola istantaneamente il soggetto Kubernetes chiamante:
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": {
"principalSubject": "principal://iam.googleapis.com/projects/108429519284/locations/global/workloadIdentityPools/prj-core-banking-prod.svc.id.goog/subject/ns/settlement/sa/settlement-engine"
},
"serviceName": "secretmanager.googleapis.com",
"methodName": "google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion",
"resourceName": "projects/prj-core-banking-prod/secrets/settlement-database-credentials/versions/1"
}
}
Non vi è alcuna necessità di correlare sessioni di delega o analizzare catene di chiamate AssumeRole/Impersonate: l'evidenza forense è inequivocabile e collegata all'esatto namespace e KSA del cluster.
Nel comporre il principalSubject in Terraform o nelle policy IAM, è obbligatorio utilizzare il Project Number numerico (es. 108429519284) e non il Project ID alfanumerico nella sezione projects/{PROJECT_NUMBER}. L'uso del Project ID genererà un errore di validazione sintattica da parte dell'API di Cloud IAM.
Raccomandazioni Architetturali per l'Adozione Enterprise
- Standardizzare il Modulo Terraform Riutilizzabile: Incapsulare la costruzione della stringa
principal://...all'interno di un modulo Terraform condiviso o di una funzione locale, per garantire che nessun team commetta errori di battitura nei segmenti del path/subject/ns/{ns}/sa/{sa}. - Definire Namespace e KSA Immutabili: Poiché la policy IAM fa riferimento diretto alla tupla
ns/{NAMESPACE}/sa/{KSA_NAME}, qualsiasi ridenominazione di un namespace o di un service account Kubernetes deve essere coordinata con un aggiornamento preventivo delle policy IAM in Terraform, per evitare interruzioni di servizio. - Migrazione Incrementale a Impatto Zero: I due pattern possono coesistere all'interno dello stesso cluster GKE. È possibile migrare un singolo microservizio per volta: assegnare il Direct Principal Grant, verificare il corretto accesso tramite Cloud Logging e successivamente rimuovere l'annotazione sul KSA e la GSA dismessa.
- Hardening del Metadata Server: Assicurarsi sempre che l'opzione
workload_metadata_config = GKE_METADATAsia abilitata su tutti i node pool GKE del cluster, inibendo qualsiasi accesso ai metadati legacy delle Compute Engine VM.
Managing application identity on Kubernetes is one of the foundational pillars of enterprise security posture. In hybrid and strictly regulated cloud environments, such as banking and digital payments, ensuring that a microservice running on Google Kubernetes Engine (GKE) can call Google Cloud APIs (Cloud Storage, Secret Manager, Cloud KMS, BigQuery) without relying on static credentials is a mandatory compliance requirement.
For years, the industry benchmark on Google Cloud has been GKE Workload Identity, implemented through an impersonation model: the Kubernetes Service Account (KSA) was granted the IAM role roles/iam.workloadIdentityUser on a dedicated Google Service Account (GSA), enabling the GKE Metadata Server to issue an OAuth2 token through a cryptographic exchange chain. This pattern represented a major milestone in cloud-native security by eliminating static JSON service account keys in Kubernetes Secrets. However, at enterprise scale across dozens of clusters and hundreds of microservices, it requires provisioning and managing a dedicated GCP Service Account for each application workload.
Modern Google Cloud IAM architecture now complements and streamlines this workflow via Direct Principal Grants within Workload Identity Federation: binding IAM permissions directly to the canonical identifier of the KSA, removing intermediate Google Service Accounts and the double-hop impersonation workflow.
The Anatomy of the Double Hop: Classic Impersonation Workflow
In the traditional Workload Identity pattern, the pod identity and the cloud IAM identity reside in two separate boundaries requiring explicit bidirectional mapping between Kubernetes and Google Cloud:
- Local KSA: Created in the application's Kubernetes namespace (e.g.,
payment-processorin namespacepayments). - Remote GSA: Created in the Google Cloud project (e.g.,
gsa-payment-processor@prj-core-banking-prod.iam.gserviceaccount.com).
Connecting the two identities requires two explicit configuration steps. First, granting roles/iam.workloadIdentityUser on the GSA to the federated KSA member:
resource "google_service_account_iam_member" "workload_identity_user" {
service_account_id = google_service_account.app_gsa.name
role = "roles/iam.workloadIdentityUser"
member = "serviceAccount:prj-core-banking-prod.svc.id.goog[payments/payment-processor]"
}
Second, the KSA must be decorated with a specific annotation informing the GKE mutating webhook which GSA to impersonate at runtime:
apiVersion: v1
kind: ServiceAccount
metadata:
name: payment-processor
namespace: payments
annotations:
iam.gke.io/gcp-service-account: gsa-payment-processor@prj-core-banking-prod.iam.gserviceaccount.com
At runtime, token issuance traverses a double-hop chain: an initial exchange with Google Security Token Service (STS) to validate the KSA OIDC identity, followed by an impersonation call to the IAM Credentials API:
Characteristics and Operational Considerations of the Impersonation Model
The GSA impersonation pattern represents a robust, fully supported, and widely adopted solution in production on Google Cloud, especially when the same Service Account must be shared across heterogeneous runtimes (such as Cloud Run or Compute Engine instances). However, in enterprise Kubernetes environments with hundreds of microservices, several architectural and operational factors arise:
- GSA Lifecycle Governance: Requires provisioning and maintaining a 1:1 GSA for every application KSA, expanding the inventory of IAM identities managed across projects.
- Audit Trail Correlation: The primary authenticated identity (
protoPayload.authenticationInfo.principalEmail) logs the intermediate GSA email. Correlating which specific KSA or pod originated a request requires incident response teams to inspect nested delegation metadata (serviceAccountDelegationInfo). - Token Minting Steps: At runtime, the GKE Metadata Server must perform an initial STS exchange followed by a second API call to
iam.serviceAccounts.getAccessToken. - Privilege Scope Management: The mapping between KSAs and GSAs requires rigorous governance to prevent unintended sharing of a single GSA across multiple workloads.
The Direct Principal Grant Architecture
With Direct Principal Grants, Google Cloud natively extends its Workload Identity Federation engine directly to GKE clusters. No intermediate Google Service Account is provisioned in the Google Cloud project: the Kubernetes Service Account becomes a first-class IAM principal recognized directly by Cloud IAM.
In this single-hop model, the federated token issued by STS directly grants access to target resources:
When Workload Identity is enabled on a GKE cluster, Google Cloud automatically provisions a dedicated Workload Identity Pool for the hosting project using the canonical syntax:
{PROJECT_ID}.svc.id.goog
Within this pool, every KSA instantiated across any cluster in the project possesses an immutable and deterministic Uniform Resource Identifier (URI):
principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{PROJECT_ID}.svc.id.goog/subject/ns/{NAMESPACE}/sa/{KSA_NAME}
Cloud IAM also accepts the contracted legacy shorthand for backward compatibility:
serviceAccount:{PROJECT_ID}.svc.id.goog[{NAMESPACE}/{KSA_NAME}]
Adopting the full canonical format principal://iam.googleapis.com/projects/{PROJECT_NUMBER}/... is Google's recommended standard for modern Zero Trust architectures. It enables granular attribute-based access control and supports advanced IAM Conditions based on Kubernetes OIDC token claims, such as namespace labels or pod cryptometrics.
Infrastructure as Code Implementation with Terraform
Implementing Direct Principal Grants removes the google_service_account resource block for application workloads, streamlining IaC to direct role assignments on target resources.
In the following enterprise scenario, a banking application settlement-engine in the settlement namespace requires read access to Secret Manager and decryption privileges in Cloud KMS, with zero intermediate GSAs.
1. Direct IAM Resource Binding
data "google_project" "current" {
project_id = var.project_id
}
locals {
# Canonical principal identifier for the KSA
ksa_principal = "principal://iam.googleapis.com/projects/${data.google_project.current.number}/locations/global/workloadIdentityPools/${var.project_id}.svc.id.goog/subject/ns/${var.namespace}/sa/${var.ksa_name}"
}
# Grant Secret Manager Secret Accessor directly to the KSA
resource "google_secret_manager_secret_iam_member" "settlement_secrets" {
project = var.project_id
secret_id = "settlement-database-credentials"
role = "roles/secretmanager.secretAccessor"
member = locals.ksa_principal
}
# Grant Cloud KMS CryptoKey Decrypter directly to the KSA
resource "google_kms_crypto_key_iam_member" "settlement_crypto" {
crypto_key_id = var.kms_crypto_key_id
role = "roles/cloudkms.cryptoKeyDecrypter"
member = locals.ksa_principal
}
2. Clean Kubernetes ServiceAccount Manifest
On the Kubernetes side, all references to Google Service Account emails disappear. The KSA remains a pure resource decoupled from underlying cloud infrastructure:
apiVersion: v1
kind: ServiceAccount
metadata:
name: settlement-engine
namespace: settlement
labels:
app.kubernetes.io/name: settlement-engine
app.kubernetes.io/part-of: core-banking
automountServiceAccountToken: true
No iam.gke.io/gcp-service-account annotation is required. The GKE Metadata Server intercepts the projected OIDC token from the pod, verifies it against the cluster issuer (https://container.googleapis.com/v1/projects/.../locations/.../clusters/...), and presents the KSA identity directly to Cloud IAM.
Comparative Matrix: GSA Impersonation vs Direct Principal Grant
| Feature | GSA Impersonation (Traditional) | Direct Principal Grant (Federated) |
|---|---|---|
| Provisioned GSAs | 1:1 for each application KSA | Zero intermediate GSAs |
| Primary Audit Log | Intermediate GSA email | Canonical KSA URI (principal://...) |
| Token Acquisition Steps | 2 hops (STS + getAccessToken) |
1 hop (direct federated STS token) |
| Kubernetes Configuration | Mandatory annotation on manifest | Standard clean KSA, zero annotations |
| Blast Radius & Scope | Shared GSA scope to govern | Isolated per namespace and KSA |
| Internal Control Functions (DORA) | Audit trail via delegation tracking | Native, granular, and direct audit trail |
| IAM Conditions Support | Applicable to the delegation binding | Full support on KSA OIDC token claims |
Security Verification and Cloud Audit Logs
A primary benefit for Cloud Security Architects and internal control functions (Internal Audit, Risk Management, Compliance under DORA and PCI-DSS v4) lies in audit trail precision.
When the workload accesses a secret or an object store bucket, Cloud Logging records the calling Kubernetes entity directly:
{
"protoPayload": {
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": {
"principalSubject": "principal://iam.googleapis.com/projects/108429519284/locations/global/workloadIdentityPools/prj-core-banking-prod.svc.id.goog/subject/ns/settlement/sa/settlement-engine"
},
"serviceName": "secretmanager.googleapis.com",
"methodName": "google.cloud.secretmanager.v1.SecretManagerService.AccessSecretVersion",
"resourceName": "projects/prj-core-banking-prod/secrets/settlement-database-credentials/versions/1"
}
}
There is no need to reconstruct delegation chains or analyze AssumeRole/Impersonate traces: forensic evidence is unambiguous, pinpointing the exact cluster namespace and service account.
When building the principalSubject in Terraform or IAM policies, you must specify the numeric Project Number (e.g., 108429519284) and not the alphanumeric Project ID in the projects/{PROJECT_NUMBER} section. Passing the Project ID triggers a syntax validation error from the Cloud IAM API.
Enterprise Adoption Best Practices
- Standardize Reusable Terraform Modules: Encapsulate
principal://...string generation inside a shared Terraform module or local expression to prevent syntax errors in the/subject/ns/{ns}/sa/{sa}path segments. - Treat Namespaces and KSAs as Immutable: Because IAM policies bind directly to the
ns/{NAMESPACE}/sa/{KSA_NAME}tuple, renaming a Kubernetes namespace or service account must be coordinated with pre-planned IAM updates in Terraform to prevent application downtime. - Zero-Downtime Incremental Migration: Both patterns seamlessly coexist within the same GKE cluster. Teams can migrate microservices incrementally: bind the Direct Principal Grant, verify successful access in Cloud Logging, and subsequently delete the KSA annotation and decommission the legacy GSA.
- Metadata Server Hardening: Enforce
workload_metadata_config = GKE_METADATAon all GKE node pools across your organization, entirely blocking access to legacy Compute Engine VM instance metadata.