EKS: IRSA und Node Pools
IAM für Pods und sinnvolle Worker-Strategie.
von PlatPrep
Wer EKS kennt und IAM kennt, weiß wie schnell es kompliziert wird. Der klassische Ansatz: Nodes bekommen eine Node-IAM-Role, und alle Pods auf diesen Nodes erben die Permissions. Das ist funktional, aber unschön — jeder Pod auf dem Node kann alles tun, was die Node-Role erlaubt. Das ist mehr Zugriff als fast jeder Pod je braucht.
IRSA — IAM Roles for Service Accounts — löst das granularer: ein Kubernetes-ServiceAccount wird mit einer IAM-Rolle verknüpft, und Pods mit diesem ServiceAccount bekommen temporäre Credentials für genau diese Rolle. Kein Node-Sharing mehr, kein statischer Access Key.
Was unter der Haube passiert
IRSA ist OIDC-Federation. Jeder EKS-Cluster hat einen OIDC-Issuer-Endpoint, den du einmalig als Identity Provider in IAM registrierst. Kubernetes stellt für den Pod ein signiertes, projiziertes ServiceAccount-Token aus (kurzlebig, mit Audience sts.amazonaws.com). Ein Mutating Webhook von EKS injiziert in jeden Pod mit annotiertem ServiceAccount zwei Umgebungsvariablen: AWS_ROLE_ARN und AWS_WEB_IDENTITY_TOKEN_FILE. Die AWS-SDKs erkennen das automatisch und rufen sts:AssumeRoleWithWebIdentity auf — AWS prüft die Token-Signatur gegen den OIDC-Endpoint und gibt temporäre Credentials heraus.
Der eigentliche Sicherheitsanker ist die Trust Policy der IAM-Rolle:
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE1234"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE1234:sub": "system:serviceaccount:data-team:s3-reader",
"oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE1234:aud": "sts.amazonaws.com"
}
}
}
Die sub-Condition bindet die Rolle an genau einen ServiceAccount in genau einem Namespace. Der häufigste Praxisfehler ist hier ein zu großzügiges Wildcard wie system:serviceaccount:*:* — damit kann jeder Pod im Cluster die Rolle übernehmen, und die ganze Übung war umsonst. Das zweithäufigste Problem: Die ServiceAccount-Annotation eks.amazonaws.com/role-arn wird nach dem Pod-Start geändert, und niemand versteht, warum die alten Pods noch die alte Rolle nutzen — der Webhook greift nur bei Pod-Erstellung.
Erwähnenswert ist die neuere Alternative EKS Pod Identity: gleiche Grundidee, aber die Zuordnung von ServiceAccount zu Rolle passiert über eine EKS-API-Assoziation statt über OIDC-Provider und Trust-Policy-Conditions. Das vereinfacht vor allem Setups mit vielen Clustern, weil die Rolle nicht mehr pro Cluster-OIDC-Provider konfiguriert werden muss. IRSA bleibt relevant — unter anderem, weil es auch außerhalb von EKS-Standardszenarien funktioniert — aber für neue Setups lohnt der Blick auf beide.
Die Node-Role abdichten
IRSA für Pods einzuführen reicht nicht, solange Pods weiterhin an die Credentials der Node-Role kommen. Der Weg dahin führt über das Instance Metadata Service (IMDS): http://169.254.169.254 beantwortet Credential-Anfragen jedes Prozesses auf dem Node — auch aus Pods. Zwei Maßnahmen schließen das: IMDSv2 erzwingen (Session-Token-Pflicht) und das Response-Hop-Limit auf 1 setzen, womit Antworten den Container-Netzwerk-Namespace nicht mehr erreichen. Danach hat ein Pod nur noch die Rechte seines ServiceAccounts — oder gar keine. Gleichzeitig sollte die Node-Role selbst auf das Minimum schrumpfen, das Kubelet und CNI brauchen.
Node Pools: Trennung nach Anforderungen
Node Pools sind das zweite Strukturierungswerkzeug. Typische Schnitte: ein GPU-Pool für ML-Workloads (teuer, getainted, nur für Pods mit expliziter Toleration — sonst blockiert ein vergessener Log-Shipper die 30-Euro-pro-Stunde-Instanz), ein High-Memory-Pool für Datenbanken und Caches, ein Standard-Pool für den Rest. IRSA funktioniert quer dazu — die Credentials hängen am ServiceAccount, nicht am Node — aber Pools liefern eine zusätzliche Isolationsebene: kritische Workloads auf dedizierten Nodes mit minimaler Node-Role, abgesichert über Taints und Tolerations statt nur nodeSelector, denn ein nodeSelector hindert fremde Pods nicht daran, auf den Knoten zu wandern.
Bei der Frage “Managed Node Groups oder Karpenter?” lautet die ehrliche Antwort: für statische, vorhersehbare Pools reichen Managed Node Groups völlig; sobald Workloads stark schwanken oder Spot-Instances konsequent genutzt werden sollen, spielt Karpenter seine Stärken aus — Details dazu im Artikel über Karpenter, Consolidation und Spot. Als Node-OS ist Bottlerocket eine Empfehlung wert: minimale Attack-Surface, immutables Root-Filesystem, automatische Updates über einen Operator.
Ein Kostenfaktor, der bei der Pool-Planung gern untergeht: Je mehr spezialisierte Pools, desto schlechter die durchschnittliche Auslastung, weil jeder Pool sein eigenes Headroom braucht. Drei sauber begründete Pools schlagen acht historisch gewachsene — und jeder Pool sollte eine dokumentierte Antwort auf die Frage haben, warum seine Workloads nicht im Standard-Pool laufen können.
Das Gesamtbild: OIDC-Federation für Pods (IRSA oder Pod Identity), abgedichtetes IMDS, schlanke Node-Roles, Pools nach Workload-Profil. Das ist kein optionales Hardening, sondern die Baseline für produktive EKS-Cluster. Dasselbe Federation-Prinzip taucht übrigens an anderer Stelle wieder auf — bei GitHub Actions OIDC zu AWS — und wer Managed Kubernetes breiter lernen will, findet mehr im Lernpfad Managed Kubernetes.