Kubernetes-Pods als Cloud-Identitäten: EKS IRSA vs. GKE Workload Identity Federation
Wie ein Kubernetes ServiceAccount zu einer echten Cloud-Identität auf AWS und Google Cloud wird - die OIDC-Föderations-Vertrauenskette hinter EKS IRSA und GKE Workload Identity Federation, eins zu eins abgebildet, mit den Fallstricken, die Ihre Routine durchbrechen.
Sowohl AWS als auch Google Cloud lösen dasselbe Problem auf dieselbe Weise: Ein Pod authentifiziert sich gegenüber Cloud-APIs als er selbst, mit einem kurzlebigen Token und ohne gespeichertes Geheimnis, indem er seinen Kubernetes ServiceAccount in eine föderierte Cloud-Identität umwandelt. Auf AWS ist die Funktion IRSA (IAM Roles for Service Accounts); auf Google Cloud ist es Workload Identity Federation for GKE (2024 von „Workload Identity“ umbenannt). Im Grunde sind beide derselbe OIDC-Föderationstrick - aber GKE verbirgt viel mehr der zugrunde liegenden Mechanismen, und das ändert, wie Sie es konfigurieren, debuggen und verstehen.
Die Kurzversion, falls Sie nur einen Absatz lesen: Der Cluster signiert ein kurzlebiges Token für den ServiceAccount jedes Pods, und die Cloud tauscht dieses Token gegen echte Anmeldeinformationen aus, die auf eine Identität zugeschnitten sind. Auf AWS registrieren Sie einen IAM OIDC provider pro Cluster, erstellen eine IAM role und sichern diese mit einer Trust Policy; das SDK innerhalb des Pods ruft AssumeRoleWithWebIdentity auf. Auf GKE registrieren Sie nichts - jedes Projekt hat einen permanenten, von Google verwalteten Workload Identity Pool namens PROJECT_ID.svc.id.goog, jeder ServiceAccount ist automatisch ein Principal darin, und ein knotenlokaler Metadatenserver führt den Token-Austausch durch, sodass Ihre App einfach Application Default Credentials verwendet, als ob sie auf einer einfachen VM wäre. Drei Dinge unterscheiden sich ausreichend, um Probleme zu verursachen: GKE benötigt zwei Aktivierungsschalter (Cluster und Node Pool), GKE kann einem Kubernetes ServiceAccount Cloud-Berechtigungen ohne jegliches Cloud-Identitätsobjekt erteilen, und - wie immer - ist das Ziehen Ihres Container-Images eine andere Identität als Ihre Workload-Identität.
Das Problem, das beide lösen
Vor der Föderation bedeutete die Vergabe von Cloud-Berechtigungen an einen Pod schlechte Optionen: einen statischen Schlüssel in das Image oder ein Secret einzubetten (Lecks, Rotationsschwierigkeiten) oder die Berechtigung dem gesamten Knoten zu erteilen, sodass jeder Pod darauf sie erbt (keine Isolation, stark überdimensioniert). Die AWS-seitigen Notlösungen waren Node-IMDS-Interzeptoren wie kube2iam und kiam; die GCP-seitige Lösung war das Einbinden eines Node-Service-Account-Schlüssels in Pods. Alle waren umständlich, zu weit gefasst und anfällig für Race Conditions.
Die Föderation ersetzt dies durch kryptografisches Vertrauen. Der Cluster projiziert ein signiertes, kurzlebiges OIDC-Token, das auf den ServiceAccount des Pods zugeschnitten ist. Die Cloud überprüft dieses Token anhand der veröffentlichten Schlüssel des Clusters und gibt, wenn die Identität des Tokens einer von Ihnen konfigurierten Regel entspricht, Anmeldeinformationen für genau eine Cloud-Identität zurück. Es wird kein Geheimnis gespeichert; das Token rotiert automatisch (Standardlebensdauer etwa eine Stunde) und ist außerhalb des Clusters nutzlos.
Der gemeinsame Mechanismus: OIDC-Föderation
Jede Workload-Identitäts-Einrichtung, auf beiden Clouds, ist dasselbe Vertrauensdreieck:
- Der Cluster ist ein OIDC-Aussteller. Er stellt ein Discovery-Dokument und einen JWKS-Endpunkt mit den öffentlichen Schlüsseln bereit, mit denen er ServiceAccount-Tokens signiert. Dieser Aussteller ist der Vertrauensanker.
- Die Cloud vertraut diesem Aussteller für eine bestimmte ServiceAccount-Identität. AWS drückt das Vertrauen pro Rolle aus; GCP drückt es einmal pro Projekt, dauerhaft, aus, und Google verwaltet es für Sie.
- Die Workload tauscht das projizierte Token gegen Cloud-Anmeldeinformationen aus und erhält kurzlebige Anmeldeinformationen für eine einzelne Cloud-Identität.
Alles andere ist Benennung - und wie viel von Schritt 2 Sie selbst aufbauen müssen. Hier unterscheiden sich die beiden Clouds am meisten.
Die Eins-zu-eins-Zuordnung
- Die Kubernetes-Seite. Auf AWS annotieren Sie den ServiceAccount mit
eks.amazonaws.com/role-arn: <role arn>. Auf GKE, im modernen direkten Modell, annotieren Sie überhaupt nichts am ServiceAccount - Sie referenzieren ihn einfach als IAM principal. (Das ältere Impersonation-Modell verwendet eine Annotation; mehr dazu unten.) - Der „OIDC-Provider“. Auf AWS ist dies der OIDC-Aussteller des EKS-Clusters, den Sie einmal in IAM als OIDC identity provider registrieren - pro Cluster. Auf GKE ist es der Workload Identity Pool des Projekts
PROJECT_ID.svc.id.goog, der automatisch erstellt und von Google verwaltet wird; Sie registrieren ihn nie, und jeder ServiceAccount in jedem Cluster des Projekts ist bereits ein Principal darin. - Die Identität. AWS: eine IAM role. GKE direktes Modell: kein dediziertes Identitätsobjekt - der Kubernetes ServiceAccount selbst ist der Principal. GKE Impersonation-Modell: ein Google service account, den der KSA imitiert.
- Die Vertrauensregel. AWS: die Trust Policy der IAM role fixiert
sub = system:serviceaccount:<ns>:<sa>undaud = sts.amazonaws.com. GKE direkt: Es gibt kein separates Vertrauensdokument - Sie erteilen eine Rolle direkt an einen Principal Identifier, der den Namespace und ServiceAccount kodiert. GKE Impersonation: Sie erteilen dem KSA die Rolleroles/iam.workloadIdentityUserfür das Google service account. - Die Berechtigungen. AWS: eine IAM policy, die an die Rolle angehängt ist. GKE: IAM allow-policy role bindings für die Zielressource (oder das Projekt) -
roles/storage.objectViewer,roles/secretmanager.secretAccessorund so weiter. Dies ist die tiefere Trennung: AWS heftet eine Policy an die Identität; GCP erteilt eine Rolle im Bereich der Ressource. - Der Austausch. AWS: Das SDK innerhalb des Pods ruft
sts:AssumeRoleWithWebIdentitymit dem projizierten Token auf. GKE: Der Metadatenserver des Knotens führt den Austausch transparent durch, und Ihre App verwendet Application Default Credentials ohne jeglichen Austauschcode.
Der Rest dieses Beitrags führt Sie durch jede Cloud von Anfang bis Ende und geht dann auf die Unterschiede ein, die tatsächlich zu Problemen führen.
Walk-through: AWS EKS IRSA, Ende zu Ende
Die Komponenten, in der Reihenfolge des Vertrauensflusses:
1. Der Cluster OIDC-Aussteller, unter einer URL wie https://oidc.eks.<region>.amazonaws.com/id/<hash>. Sie registrieren ihn einmal in IAM als OIDC identity provider, damit IAM den von ihm signierten Tokens vertraut.
2. Die IAM role und ihre Trust Policy. Die Trust Policy macht die Rolle von einem ServiceAccount und nichts anderem übernehmbar:
{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::<account>:oidc-provider/oidc.eks.<region>.amazonaws.com/id/<hash>" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.<region>.amazonaws.com/id/<hash>:sub": "system:serviceaccount:apps:checkout",
"oidc.eks.<region>.amazonaws.com/id/<hash>:aud": "sts.amazonaws.com"
}
}
}
Fixieren Sie sowohl :sub als auch :aud - nur den Aussteller zu fixieren würde es jedem ServiceAccount im Cluster ermöglichen, die Rolle zu übernehmen.
3. Die IAM policy, die an die Rolle angehängt ist, gewährt, was die App benötigt (s3:GetObject für einen Bucket und so weiter).
4. Der ServiceAccount trägt eine Annotation:
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/checkout
5. Die Projektion. EKS liefert den im Cluster integrierten Pod Identity Webhook. Wenn ein Pod den annotierten ServiceAccount verwendet, injiziert der Webhook AWS_ROLE_ARN und AWS_WEB_IDENTITY_TOKEN_FILE und bindet ein projiziertes Token ein (Audience sts.amazonaws.com, automatisch rotiert).
6. Der Austausch. Das AWS SDK bemerkt AWS_WEB_IDENTITY_TOKEN_FILE, ruft sts:AssumeRoleWithWebIdentity auf und speichert die zurückgegebenen kurzlebigen Anmeldeinformationen im Cache. Ihr Code ist einfach boto3.client("s3").
Neuere Alternative: EKS Pod Identity (2023) erledigt dieselbe Aufgabe über einen On-Cluster-Agenten und eine Association API anstelle eines pro-Cluster IAM OIDC-Providers und einer pro-Rolle Trust Policy - was es, bemerkenswerterweise, viel mehr wie GKEs verwaltetes Modell erscheinen lässt. Sowohl IRSA als auch Pod Identity koexistieren in der Produktion; IRSA ist dasjenige, das sauber zum OIDC-Föderationsrahmen passt, daher ist es das, was Sie hier im Kopf behalten sollten.
Walk-through: GKE Workload Identity Federation, Ende zu Ende
Gleicher Vertrauensfluss, viel weniger aufzubauen:
1. Zwei Aktivierungsschalter. Aktivieren Sie den Workload Identity Pool auf dem Cluster und den Metadatenserver auf dem Node Pool:
gcloud container clusters update <cluster> --workload-pool=<project-id>.svc.id.goog
gcloud container node-pools update <pool> --cluster=<cluster> --workload-metadata=GKE_METADATA
Beide sind erforderlich. Das Cluster-Flag meldet den Cluster im Pool des Projekts an; das Node-Pool-Flag stellt den GKE Metadatenserver bereit (ein DaemonSet auf jedem Knoten), der Anmeldeinformationsanfragen abfangen wird. Ein Pod auf einem Node Pool ohne GKE_METADATA erhält die Identität des Knotens, nicht seine eigene - ein stiller, zu weit gefasster Fallback.
2. Der Pool existiert bereits. <project-id>.svc.id.goog wird automatisch für das Projekt erstellt; Sie registrieren nie einen OIDC provider, und es gibt keine pro-Konto-Provider-Grenze, die erreicht werden könnte. Jeder ServiceAccount in den Clustern des Projekts ist bereits ein Principal.
3a. Direkter Zugriff (der moderne Standard). Erteilen Sie eine Rolle direkt an den ServiceAccount, adressiert als IAM principal. Kein Google service account, keine Annotation:
gcloud storage buckets add-iam-policy-binding gs://<bucket> \
--role=roles/storage.objectViewer \
--member="principal://iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<project-id>.svc.id.goog/subject/ns/apps/sa/checkout"
Der Principal-Pfad kodiert ns/apps/sa/checkout - der Namespace und ServiceAccount sind die Vertrauensregel; es gibt kein separates Vertrauensdokument zu schreiben.
3b. Impersonation (das ältere Modell, immer noch für einige Dienste erforderlich). Erstellen Sie ein Google service account, lassen Sie den KSA es imitieren und annotieren Sie den KSA:
gcloud iam service-accounts add-iam-policy-binding checkout@<project-id>.iam.gserviceaccount.com \
--role=roles/iam.workloadIdentityUser \
--member="serviceAccount:<project-id>.svc.id.goog[apps/checkout]"
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
iam.gke.io/gcp-service-account: checkout@<project-id>.iam.gserviceaccount.com
Hier hält das Google service account die Berechtigungen (über normale Role Bindings), und der KSA leiht sie sich aus.
4. Der Austausch. Wenn Ihre App Anmeldeinformationen anfordert, fängt der GKE Metadatenserver die Anfrage an http://metadata.google.internal ab, ruft ein ServiceAccount JWT von der Kubernetes API für diesen Pod ab, tauscht es über den Security Token Service gegen ein kurzlebiges föderiertes Zugriffstoken aus (Standard 1 Stunde, proaktiv vor Ablauf aktualisiert) und gibt es zurück. Ihre App verwendet Application Default Credentials und die Standard-Google Cloud Client Libraries - sie verhält sich genau so, als würde sie auf einer GCE VM laufen. Es gibt keinen Webhook, der Umgebungsvariablen injiziert, keine AZURE_*-ähnlichen Variablen und keinen Austauschaufruf in Ihrem Code: Ein Cloud Storage-Lesevorgang ist einfach storage.Client() in Python.
Das Subject ist der Dreh- und Angelpunkt (auf beiden Clouds)
Der Wert, der auf beiden Clouds exakt übereinstimmen muss, ist die ServiceAccount-Identität:
- AWS fixiert das Token-Subject
system:serviceaccount:<namespace>:<serviceaccount>in der Trust Policy der Rolle. - GKE direkt kodiert dieselbe Paarung im Principal-Pfad
.../subject/ns/<namespace>/sa/<serviceaccount>. - GKE Impersonation kodiert es im Member
serviceAccount:<project-id>.svc.id.goog[<namespace>/<serviceaccount>].
Der häufigste Fehler „es authentifiziert sich als niemand“ auf beiden Clouds ist eine Nichtübereinstimmung hier: das Chart wurde in einen anderen Namespace deployt, der ServiceAccount erhielt den Release-Namen anstelle des App-Namens, oder der Pod fiel auf den default ServiceAccount zurück. Wenn die Föderation stillschweigend fehlschlägt, überprüfen Sie zuerst den Namespace und den ServiceAccount-Namen.
Unterschiede, die Sie tatsächlich stolpern lassen werden
1. GKE registriert den „OIDC-Provider“ für Sie - einmal, pro Projekt, für immer. Auf EKS registrieren Sie einen IAM OIDC provider pro Cluster, und bei Flottengröße können Sie das Soft Limit von 100 OIDC providers pro AWS-Konto erreichen und müssen dies umgehen. Auf GKE gibt es genau einen Pool pro Projekt (PROJECT_ID.svc.id.goog), der von Google erstellt und verwaltet wird und von jedem Cluster im Projekt gemeinsam genutzt wird. Nichts zu registrieren, nichts zu begrenzen. Die Kehrseite: Das Vertrauen ist standardmäßig projektweit, sodass Sie mit IAM bindings (welchen Namespace und ServiceAccount Sie gewähren) und nicht durch das Aufteilen von Providern den Umfang festlegen.
2. GKE tauscht das Token an einem knotenlokalen Metadatenserver aus, nicht mit einem In-Pod-SDK-Aufruf. IRSA mutiert den Pod (ein Webhook injiziert Umgebungsvariablen und ein projiziertes Token) und das SDK ruft STS auf. GKE fängt metadata.google.internal auf dem Knoten ab und gibt Anmeldeinformationen transparent zurück, sodass die App Application Default Credentials verwendet, als ob sie auf einer einfachen VM wäre. Zwei Konsequenzen: Ihre App benötigt keinen Cloud-spezifischen Anmeldeinformationscode, und der Node Pool muss den Metadatenserver aktiviert haben (GKE_METADATA), sonst erhält der Pod stillschweigend die Identität des Knotens anstelle seiner eigenen.
3. GKE kann einem ServiceAccount Cloud-Berechtigungen ohne Cloud-Identitätsobjekt erteilen. In IRSA gibt es immer eine IAM role. Im direkten Modell von GKE gibt es nichts auf der Cloud-Seite zu erstellen außer dem Role Binding selbst - der Kubernetes ServiceAccount ist der Principal (principal://.../subject/ns/NS/sa/SA). Das ältere Impersonation-Modell führt ein Google service account und die Annotation iam.gke.io/gcp-service-account ein, und Sie benötigen es immer noch für die wenigen Dienste, die einen föderierten Principal nicht direkt akzeptieren - aber greifen Sie zuerst zum direkten Binding.
4. GKE benötigt zwei Schalter, wie Azure. Cluster (--workload-pool) plus Node Pool (--workload-metadata=GKE_METADATA). Wenn die Node-Pool-Hälfte fehlt, gibt es keinen Fehler, nur die falsche (Knoten-)Identität. IRSA integriert die Projektion in EKS selbst, sodass es keinen äquivalenten zweiten Schalter gibt.
5. Das Ziehen des Images ist eine andere Identität als die Workload-Identität - auf beiden Clouds. Das Image-Pull ist die Aufgabe der Knoten-Identität: Auf GKE benötigt das Google service account des Node Pools roles/artifactregistry.reader, um aus Artifact Registry zu ziehen; auf EKS benötigt die Instance Role der Node Group AmazonEC2ContainerRegistryReadOnly (oder Sie verwenden Pull Secrets). Workload Identity ist nur dafür da, wenn der App-Code eine Cloud-API aufruft. Ein reiner HTTP-Dienst, der nie mit einem Cloud SDK kommuniziert, benötigt überhaupt keine Workload Identity - und das Gewähren einer Workload Identity artifactregistry.reader bewirkt nichts für den Image-Pull, da der Pull stattfindet, bevor die App (und ihr föderiertes Token) überhaupt läuft.
6. Berechtigungen werden unterschiedlich angehängt. AWS heftet eine IAM policy an die Rolle - die Berechtigung reist mit der Identität. GCP erteilt ein IAM role binding im Bereich der Zielressource (oder des Projekts) - die Berechtigung lebt auf dem Objekt, auf das zugegriffen wird, nicht auf der Identität. Gleicher Endzustand, unterschiedlicher Ort zur Überprüfung: Auf AWS lesen Sie die angehängten Policies der Rolle; auf GCP listen Sie die IAM policy der Ressource auf (oder verwenden Sie Policy Analyzer, um zu sehen, was ein Principal erreichen kann).
Was der App-Code tut
Beide Clouds enden bei „kein Geheimnis, kein Anmeldeinformationscode“, aber sie erreichen dies auf unterschiedliche Weise:
- AWS: Der Webhook setzt
AWS_ROLE_ARNundAWS_WEB_IDENTITY_TOKEN_FILE; die Standardkette des SDKs ruftAssumeRoleWithWebIdentityauf. Ihr Code:boto3.client("s3"). - GKE: Nichts wird in den Pod injiziert; die Application Default Credentials-Kette des SDKs trifft den Node Metadatenserver, der den Austausch durchführt. Ihr Code:
storage.Client().
In beiden Fällen sind die Anmeldeinformationen kurzlebig und werden transparent aktualisiert. Es gibt nichts zu rotieren, nichts zu speichern und nichts, das durchsickern könnte.
Eine Debugging-Checkliste, die auf beiden Clouds funktioniert
Wenn ein Pod „sich nicht authentifizieren kann“, gehen Sie die Vertrauenskette der Reihe nach durch:
- Cluster-/Projekt-Föderation aktiviert? EKS: Der IAM OIDC provider ist registriert. GKE: Der Cluster hat
--workload-poolund der Node Pool hatGKE_METADATA. Auf GKE ist ein fehlender Node-Pool-Schalter der klassische stille Fehler - der Pod erhält die Knotenidentität, nicht seine eigene. - Richtiger ServiceAccount? Der Pod verwendet tatsächlich den ServiceAccount, den Sie vermuten (nicht
default). Führen Sie exec aus und überprüfen Sie. - Identitätsübereinstimmung? Vergleichen Sie die Vertrauensregel mit dem tatsächlichen Namespace und ServiceAccount:
system:serviceaccount:<ns>:<sa>auf AWS,.../subject/ns/<ns>/sa/<sa>(oder[ns/sa]) auf GKE. Dies ist der häufigste Fehler auf beiden. - Austausch funktioniert? EKS: Der Pod hat
AWS_WEB_IDENTITY_TOKEN_FILE. GKE: curl -H "Metadata-Flavor: Google" metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email vom Pod gibt die erwartete Identität zurück. - Berechtigungen? Erst nachdem die Identität aufgelöst ist: die IAM policy für die Rolle (AWS) oder das Role Binding für die Zielressource (GCP). Ein leeres Ergebnis hier ist ein Autorisierungsfehler, kein Authentifizierungsfehler - was Ihnen sagt, dass die Föderation selbst funktioniert.
Welche Zertifizierungen dies behandeln
Workload Identity befindet sich genau an der Schnittstelle von Kubernetes, Cloud IAM und OIDC-Föderation, daher taucht es in drei Prüfungsbereichen auf. Wenn Sie bereits eine Cloud-Zertifizierung besitzen, ist dasselbe Konzept auf der anderen Cloud ein kurzer Sprung.
Auf der AWS-Seite:
- AWS Certified Solutions Architect - Associate (SAA-C03) - IAM roles, EKS und wie Identitäten Cloud-Berechtigungen erhalten.
- AWS Certified Security - Specialty (SCS-C03) - IAM trust policies, OIDC-Föderation und Least-Privilege-Scoping (genau die IRSA-Vertrauenskette).
Auf der Google Cloud-Seite:
- Google Cloud Associate Cloud Engineer - GKE, IAM, service accounts und Role Bindings.
- Google Cloud Professional Cloud Security Engineer - Workload Identity, IAM-Tiefe, Service-Account-Sicherheit und Least Privilege.
Auf der Kubernetes-Seite:
- CNCF Certified Kubernetes Administrator (CKA) - ServiceAccounts, Projected Tokens und Pod Specs.
- CNCF Certified Kubernetes Security Specialist (CKS) - ServiceAccount Token-Sicherheit, Least Privilege und Reduzierung der Exposition statischer Secrets.
Fazit
IRSA und GKE Workload Identity Federation sind dieselbe Idee mit unterschiedlichem Implementierungsaufwand: Der Cluster signiert ein kurzlebiges OIDC-Token für einen ServiceAccount, und die Cloud tauscht es gegen Anmeldeinformationen aus, die auf eine einzelne Identität zugeschnitten sind. AWS lässt Sie das Vertrauen selbst zusammenstellen - einen OIDC provider pro Cluster, eine IAM role, eine Trust Policy, ein projiziertes Token, das das SDK austauscht. GKE stellt Ihnen einen verwalteten, projektweiten Pool zur Verfügung, ermöglicht es Ihnen, eine Rolle direkt an den Kubernetes ServiceAccount zu binden, ohne überhaupt eine Cloud-Identität zu benötigen, und führt den Austausch an einem Node Metadatenserver durch, sodass Ihre App niemals Anmeldeinformationen sieht. Lernen Sie das Vertrauensdreieck einmal, und die Übersetzung ist mechanisch: Rolle wird zu Principal Binding (oder einem Service Account, den Sie imitieren), Trust Policy wird zu einem IAM Member String, angehängte Policy wird zu einem Role Binding für die Ressource, und AssumeRoleWithWebIdentity wird zu einem unsichtbaren Metadatenserver-Austausch. Behalten Sie drei Dinge im Auge - GKEs zwei Schalter, sein direktes Modell ohne Identitätsobjekt und die Tatsache, dass das Image-Pull eine völlig andere Identität ist - und die Ablehnungen, die früher zufällig aussahen, beginnen sich als kohärentes, bewusstes Design zu lesen.