Поды Kubernetes как облачные удостоверения: EKS IRSA против AKS Workload Identity
Как ServiceAccount Kubernetes становится реальным облачным удостоверением - цепочка доверия федерации OIDC, лежащая в основе AWS IRSA и Azure AKS Workload Identity, сопоставленная один к одному, с нюансами, которые нарушают вашу мышечную память.
И AWS, и Azure решают одну и ту же проблему одним и тем же способом: под аутентифицируется в облачных API как он сам, используя кратковременный токен и не храня секрет, превращая свой Kubernetes ServiceAccount в федеративное облачное удостоверение. В AWS эта функция называется IRSA (IAM Roles for Service Accounts); в Azure - Microsoft Entra Workload Identity (преемник устаревшего aad-pod-identity). По сути, оба используют один и тот же трюк федерации OIDC, и их компоненты совпадают почти один к одному.
Краткая версия, если вы прочитаете только один абзац: кластер становится издателем OpenID Connect, который подписывает токен для ServiceAccount каждого пода; поставщик удостоверений облака доверяет этому издателю и обменивает токен на реальные облачные учетные данные, привязанные к одному удостоверению. AWS называет удостоверение IAM role и защищает его политикой доверия; Azure называет его user-assigned managed identity и защищает его федеративным учетным данным удостоверения. Строка subject system:serviceaccount:<namespace>:<name> является ключевым элементом в обоих случаях. Три вещи отличаются достаточно, чтобы вызвать проблемы: Azure разделяет "поставщика OIDC" на два отдельных переключателя кластера, которые по умолчанию отключены; Azure дополнительно требует метку на самом поде; и извлечение образа контейнера является другим удостоверением, отличным от вашего workload identity, в обоих облаках.
Проблема, которую решают оба
До федерации предоставление поду облачных разрешений означало плохие варианты: встраивание статического ключа доступа в образ или Secret (утечки, проблемы с ротацией) или предоставление разрешения всему узлу, чтобы каждый под на нем наследовал его (отсутствие изоляции, чрезмерно широкая область действия). В AWS временными решениями были перехватчики IMDS узлов, такие как kube2iam и kiam; в Azure - aad-pod-identity. Все они проксировали удостоверение узла, были сложными и подверженными состоянию гонки.
Федерация заменяет это криптографическим доверием. kubelet проецирует подписанный, кратковременный токен OIDC в под, привязанный к ServiceAccount пода. Облачный IdP проверяет подпись этого токена по опубликованным открытым ключам кластера и, если issuer, subject и audience токена соответствуют настроенному вами правилу доверия, возвращает учетные данные для ровно одного облачного удостоверения. Никакой секрет нигде не хранится; проецируемый токен автоматически ротируется (срок действия по умолчанию около часа) и бесполезен за пределами кластера.
Общий механизм: федерация OIDC
Каждая настройка workload-identity, в любом облаке, представляет собой один и тот же треугольник доверия:
- Кластер является издателем OIDC. Он предоставляет документ обнаружения по адресу
<issuer-url>/.well-known/openid-configurationи конечную точку JWKS с открытыми ключами, которыми он подписывает токены ServiceAccount. Этот URL издателя является якорем доверия. - Облачный IdP доверяет этому издателю для конкретной пары
(subject, audience). Subject определяет, какой ServiceAccount, а audience - для кого предназначен токен. - Рабочая нагрузка обменивает проецируемый токен на облачные учетные данные. SDK считывает файл токена, спроецированный kubelet, представляет его конечной точке токенов облака и получает кратковременные учетные данные для одного облачного удостоверения.
Все остальное - это названия. Вот карта, пройдемся по строкам.
Сопоставление один к одному
- Сторона Kubernetes. В AWS вы аннотируете ServiceAccount с помощью
eks.amazonaws.com/role-arn: <role arn>. В Azure вы аннотируете его с помощьюazure.workload.identity/client-id: <managed identity client id>и помечаете под меткойazure.workload.identity/use: "true". Оба указывают поду на облачное удостоверение; Azure требует дополнительную метку пода (подробнее об этом ниже). - "Поставщик OIDC". В AWS это издатель OIDC кластера EKS, зарегистрированный один раз в IAM как поставщик удостоверений OIDC. В Azure это две настройки кластера: издатель OIDC (
oidcIssuerProfile.enabled), который публикует URL издателя и ключи подписи, и дополнение workload-identity (securityProfile.workloadIdentity.enabled), мутирующий admission webhook. Оба должны быть включены. - Удостоверение. AWS: IAM role. Azure: user-assigned managed identity (UAMI). Это объект, которым становится под.
- Правило доверия. AWS: политика доверия IAM role федеративно связывает поставщика OIDC и привязывает
sub = system:serviceaccount:<ns>:<sa>иaud = sts.amazonaws.com. Azure: федеративное учетное данное удостоверения на UAMI сissuer = <cluster OIDC issuer url>,subject = system:serviceaccount:<ns>:<sa>иaudience = api://AzureADTokenExchange. Те же три поля, но в разных местах. - Разрешения. AWS: политика IAM, прикрепленная к роли (чтение этого S3 bucket, извлечение из этого ECR repo). Azure: назначения ролей RBAC на целевых ресурсах (Key Vault Secrets User, Storage Blob Data Reader, AcrPull, ...). Это более глубокое философское различие - AWS прикрепляет политику к удостоверению; Azure предоставляет роль в области действия ресурса.
- Обмен. AWS: SDK вызывает
sts:AssumeRoleWithWebIdentityс проецируемым токеном. Azure: SDK выполняет обмен токенов Entra черезDefaultAzureCredential/WorkloadIdentityCredential, представляя проецируемый токен как client assertion. Оба дают кратковременные учетные данные, оба не требуют секретных материалов в вашем коде.
Остальная часть этого поста описывает каждый облачный сервис от начала до конца, а затем останавливается на различиях, которые на самом деле сбивают людей с толку.
Пошаговое руководство: AWS EKS IRSA, от начала до конца
Компоненты, в порядке потока доверия:
1. Издатель OIDC кластера. Каждый кластер EKS имеет его по URL, например https://oidc.eks.<region>.amazonaws.com/id/<hash>. Вы регистрируете его один раз в IAM как поставщика удостоверений OIDC, чтобы IAM доверял токенам, которые он подписывает.
2. IAM role и ее политика доверия. Политика доверия делает роль доступной для принятия конкретным ServiceAccount и ничем иным:
{
"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"
}
}
}
Закрепите оба :sub и :aud. Закрепление только издателя позволило бы любому ServiceAccount в кластере принять роль - классическая ошибка чрезмерного доверия.
3. Политика IAM. Обычная политика на основе удостоверения, прикрепленная к роли, предоставляет то, что действительно нужно приложению (s3:GetObject для одного bucket, ecr:GetDownloadUrlForLayer и так далее).
4. ServiceAccount. Простой ServiceAccount с одной аннотацией:
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/checkout
5. Проекция. EKS поставляется с Pod Identity Webhook, встроенным в кластер. Когда под использует аннотированный ServiceAccount, webhook изменяет его: он внедряет переменные среды AWS_ROLE_ARN и AWS_WEB_IDENTITY_TOKEN_FILE и монтирует том с проецируемым токеном (audience sts.amazonaws.com, автоматически ротируемый). Вам не нужно помечать под; аннотации SA достаточно.
6. Обмен. Цепочка учетных данных по умолчанию AWS SDK замечает AWS_WEB_IDENTITY_TOKEN_FILE, вызывает sts:AssumeRoleWithWebIdentity с токеном и кэширует возвращенные кратковременные учетные данные. Ваш код - это просто boto3.client("s3") - никакого управления учетными данными.
Новая альтернатива: EKS Pod Identity (2023) выполняет ту же работу через агент в кластере и API ассоциации вместо IAM OIDC provider для каждого кластера и политики доверия для каждой роли. Это проще в эксплуатации в масштабе, но является специфичной для AWS реализацией; модель федерации OIDC, описанная выше, является той, которая чисто сопоставляется с Azure, поэтому ее следует держать в уме для сравнения между облаками.
Пошаговое руководство: Azure AKS Workload Identity, от начала до конца
Тот же поток доверия, термины Azure:
1. Два переключателя кластера. Включите издателя OIDC (oidcIssuerProfile.enabled = true), который публикует URL издателя, например https://<region>.oic.prod-aks.azure.com/<tenant>/<guid>/, и дополнение workload-identity (securityProfile.workloadIdentity.enabled = true), которое устанавливает мутирующий webhook. Оба по умолчанию отключены в новом кластере, и oidcIssuerUrl кластера равен null, пока первый не будет включен. Включение их - это обновление на месте, а не пересоздание.
2. Управляемое удостоверение. Создайте user-assigned managed identity. (Федеративные учетные данные прикрепляются только к user-assigned identities, а не к system-assigned.) Его client id - это то, на что будет ссылаться ServiceAccount.
3. Федеративное учетное данное удостоверения. Это правило доверия, дочерний объект на UAMI:
issuer: https://<region>.oic.prod-aks.azure.com/<tenant>/<guid>/
subject: system:serviceaccount:apps:checkout
audience: api://AzureADTokenExchange
Обратите внимание на ту же грамматику subject, что и в AWS, и другой фиксированный audience.
4. RBAC. Предоставьте принципалу UAMI роли, которые ему необходимы на целевых ресурсах: Key Vault Secrets User на хранилище, Storage Blob Data Reader на учетной записи хранения, AcrPull на реестре, если приложение само вызывает data plane реестра. Документа политики на удостоверении нет; доступ осуществляется через назначения ролей в области действия каждого ресурса.
5. ServiceAccount и метка пода. ServiceAccount содержит аннотацию client-id, и - это та часть, о которой забывают пользователи AWS - под также должен быть помечен:
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
azure.workload.identity/client-id: <uami-client-id>
---
# in the Deployment's pod template:
metadata:
labels:
azure.workload.identity/use: "true" # required on the POD, not just the SA
spec:
serviceAccountName: checkout
6. Проекция. Webhook дополнения изменяет только поды, которые имеют метку azure.workload.identity/use: "true". Когда он срабатывает, он внедряет AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE и AZURE_AUTHORITY_HOST, и проецирует токен с audience api://AzureADTokenExchange.
7. Обмен. DefaultAzureCredential() (или явный WorkloadIdentityCredential) считывает эти переменные среды, представляет проецируемый токен Entra ID как client assertion и получает access token для целевого ресурса. Чтение Key Vault - это azure.identity.DefaultAzureCredential() плюс SecretClient в Python, или new DefaultAzureCredential() плюс @azure/keyvault-secrets в TypeScript - и снова, никаких секретов в коде.
Строка subject является ключевым элементом (в обоих облаках)
Единственное значение, которое должно точно совпадать в обоих облаках, - это subject токена:
system:serviceaccount:<namespace>:<serviceaccount-name>
kubelet вставляет это в токен на основе фактического namespace и ServiceAccount пода. Правило доверия (IAM trust policy в AWS, federated credential в Azure) закрепляет строку, которую оно примет. Если приложение работает в namespace apps под ServiceAccount checkout, правило доверия должно гласить system:serviceaccount:apps:checkout - символ в символ. Наиболее распространенная ошибка "аутентификация не удалась" - это несоответствие здесь: chart был развернут в другом namespace, ServiceAccount получил имя релиза вместо имени приложения, или кто-то использовал default ServiceAccount. Когда федерация молчаливо терпит неудачу, сначала проверьте subject.
Audience - это другое закрепленное поле, и оно является фиксированной константой для каждого облака: sts.amazonaws.com в AWS, api://AzureADTokenExchange в Azure. Вы редко меняете его, но правило доверия и проецируемый токен должны совпадать.
Четыре различия, которые на самом деле сбивают с толку
1. Azure разделяет "поставщика OIDC" на два переключателя, и оба по умолчанию отключены. В EKS webhook pod-identity встроен в кластер, и единственный одноразовый шаг - это регистрация поставщика OIDC в IAM. В AKS вы должны отдельно включить издателя OIDC и дополнение workload-identity; если забыть про дополнение, издатель все равно будет публиковать токены, но ничто не будет проецировать их в поды, поэтому приложение не увидит переменных среды AZURE_*, и DefaultAzureCredential тихо перейдет к следующему источнику учетных данных. Включите оба, и помните, что федеративное учетное данное не может быть создано, пока не существует URL издателя.
2. Azure требует метку на поде, а не только аннотацию на ServiceAccount. Webhook AKS срабатывает по метке azure.workload.identity/use: "true" на поде (одной аннотации ServiceAccount недостаточно). Webhook IRSA срабатывает по аннотации ServiceAccount и не требует метки пода. Это самая распространенная ловушка со стороны Azure: ServiceAccount выглядит идеально, но шаблон пода забыл метку, поэтому токен не проецируется.
3. Извлечение образа - это другое удостоверение, отличное от workload identity - в обоих облаках. Извлечение образа - это задача удостоверения узла/kubelet: в AKS удостоверение kubelet имеет AcrPull; в EKS роль экземпляра группы узлов имеет AmazonEC2ContainerRegistryReadOnly (или вы используете pull secrets). Workload identity требуется только тогда, когда код приложения вызывает облачный API (читает секрет, перечисляет bucket, вызывает data plane реестра). Чисто HTTP-сервису, который никогда не взаимодействует с облачным SDK, вообще не нужен workload identity - и наоборот, предоставление workload identity AcrPull ничего не дает для извлечения образа, потому что извлечение происходит до того, как приложение (и его федеративный токен) начнет работать.
4. Разрешения прикрепляются по-разному. AWS прикрепляет политику IAM к роли - разрешение перемещается вместе с удостоверением. Azure предоставляет назначения ролей RBAC в области действия целевого ресурса - разрешение находится на объекте, к которому осуществляется доступ, а не на удостоверении. Тот же конечный результат (этот под может читать это хранилище), но вы смотрите в разных местах, чтобы проверить его: в AWS, читаете прикрепленные политики роли; в Azure, перечисляете назначения ролей на ресурсе (или назначения удостоверения по областям). Это отражает более широкое различие между IAM и RBAC в двух облаках.
Как выглядит внедренная среда
Оба webhook передают SDK все необходимое через переменные среды и проецируемый файл, поэтому код приложения не ссылается на секреты и не содержит облачной логики учетных данных:
- AWS:
AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE,AWS_REGION. Цепочка учетных данных по умолчанию вызываетAssumeRoleWithWebIdentityза вас. - Azure:
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_FEDERATED_TOKEN_FILE,AZURE_AUTHORITY_HOST.DefaultAzureCredentialвыполняет обмен Entra за вас.
В обоих случаях файл проецируемого токена является кратковременным и автоматически ротируется kubelet; SDK повторно считывает его и прозрачно обновляет облачные учетные данные. Нет ничего, что нужно ротировать, ничего, что нужно хранить, и ничего, что может утечь.
Контрольный список отладки, работающий в обоих облаках
Когда под "не может аутентифицироваться", пройдите по цепочке доверия по порядку:
- Издатель кластера включен? Убедитесь, что URL издателя OIDC существует (AKS: оба переключателя включены; EKS: IAM OIDC provider зарегистрирован). Нет издателя - нет якоря доверия.
- Проекция происходит? Выполните exec в под и проверьте наличие файла токена и переменных среды (
AWS_WEB_IDENTITY_TOKEN_FILE/AZURE_FEDERATED_TOKEN_FILE). Отсутствие в Azure обычно означает, что метка пода отсутствует или дополнение отключено. - Subject совпадает? Сравните subject правила доверия с
system:serviceaccount:<actual-namespace>:<actual-sa>. Это наиболее частая причина сбоев. - Audience совпадает?
sts.amazonaws.com/api://AzureADTokenExchange, совпадающие с обеих сторон. - Разрешения? Только после разрешения удостоверения: политика IAM на роли или назначение роли RBAC на целевом ресурсе. Пустой результат здесь - это сбой авторизации, а не аутентификации - полезное различие, потому что оно говорит вам, что сама федерация работает.
Какие сертификации это охватывают
Workload identity находится на стыке Kubernetes, облачного IAM и федерации OIDC, поэтому он встречается в трех направлениях экзаменов. Если у вас уже есть учетные данные одного облака, та же концепция в другом облаке - это небольшой шаг.
Со стороны AWS:
- AWS Certified Solutions Architect - Associate (SAA-C03) - IAM roles, EKS и как удостоверения получают облачные разрешения.
- AWS Certified Security - Specialty (SCS-C03) - политики доверия IAM, федерация OIDC и ограничение привилегий (точно цепочка доверия IRSA).
Со стороны Azure:
- Microsoft Azure Administrator Associate (AZ-104) - AKS, managed identities и назначения ролей RBAC.
- Microsoft Azure Security Engineer Associate (AZ-500) - Entra ID, managed identities, федеративные учетные данные и глубина RBAC.
Со стороны Kubernetes:
- CNCF Certified Kubernetes Administrator (CKA) - ServiceAccounts, проецируемые токены и спецификации подов.
- CNCF Certified Kubernetes Security Specialist (CKS) - безопасность токенов ServiceAccount, наименьшие привилегии и снижение риска утечки статических секретов.
Итог
IRSA и AKS Workload Identity - это одна и та же идея, выраженная разной терминологией: кластер подписывает кратковременный токен OIDC для ServiceAccount, облачный IdP доверяет этому издателю для одной пары (subject, audience), и под обменивает токен на учетные данные, привязанные к одному облачному удостоверению - IAM role в AWS, managed identity в Azure. Изучите треугольник доверия один раз, и перевод будет механическим: role становится managed identity, trust policy становится federated credential, attached policy становится назначением RBAC, AssumeRoleWithWebIdentity становится обменом токенов Entra. Держите в уме три вещи - два переключателя кластера Azure, обязательную метку пода Azure и тот факт, что извлечение образа - это совершенно другое удостоверение - и отказы, которые раньше казались случайными, начнут восприниматься как связный, продуманный дизайн.