Pods do Kubernetes como identidades de nuvem: EKS IRSA vs. AKS Workload Identity
Como um ServiceAccount do Kubernetes se torna uma identidade de nuvem real - a cadeia de confiança de federação OIDC por trás do AWS IRSA e do Azure AKS Workload Identity, mapeada um-para-um, com as particularidades que quebram sua memória muscular.
Tanto a AWS quanto o Azure resolvem o mesmo problema da mesma forma: um pod se autentica nas APIs da nuvem como ele mesmo, com um token de curta duração e sem segredo armazenado, transformando seu ServiceAccount do Kubernetes em uma identidade de nuvem federada. Na AWS, o recurso é o IRSA (IAM Roles for Service Accounts); no Azure, é o Microsoft Entra Workload Identity (o sucessor do aad-pod-identity, que foi descontinuado). Por baixo dos panos, ambos são o mesmo truque de federação OIDC, e as partes móveis se alinham quase um-para-um.
A versão resumida, se você ler apenas um parágrafo: o cluster se torna um emissor OpenID Connect que assina um token para o ServiceAccount de cada pod; o provedor de identidade da nuvem confia nesse emissor e troca o token por credenciais de nuvem reais com escopo para uma identidade. A AWS chama a identidade de IAM role e a protege com uma trust policy; o Azure a chama de user-assigned managed identity e a protege com uma federated identity credential. A string de assunto system:serviceaccount:<namespace>:<name> é o ponto central em ambos. Três coisas diferem o suficiente para te pegar: o Azure divide o "OIDC provider" em dois toggles de cluster separados que vêm desativados por padrão, o Azure exige adicionalmente um label no pod em si, e puxar sua imagem de contêiner é uma identidade diferente da sua workload identity em ambas as nuvens.
O problema que ambos resolvem
Antes da federação, conceder permissões de nuvem a um pod significava opções ruins: incorporar uma chave de acesso estática na imagem ou em um Secret (vazamentos, dificuldade de rotação), ou conceder a permissão a todo o nó para que cada pod nele a herdasse (sem isolamento, escopo excessivamente amplo). As soluções provisórias do lado da AWS eram interceptores de IMDS de nó como kube2iam e kiam; a do lado do Azure era aad-pod-identity. Todos eles faziam proxy da identidade do nó e eram complicados e propensos a condições de corrida.
A federação substitui isso por confiança criptográfica. O kubelet projeta um token OIDC assinado e de curta duração no pod, com escopo para o ServiceAccount do pod. O IdP da nuvem verifica a assinatura desse token em relação às chaves públicas publicadas do cluster e, se o emissor, o assunto e a audiência do token corresponderem a uma trust rule que você configurou, ele retorna credenciais para exatamente uma identidade de nuvem. Nenhum segredo é armazenado em lugar algum; o token projetado auto-rotaciona (vida útil padrão de cerca de uma hora) e é inútil fora do cluster.
O mecanismo compartilhado: federação OIDC
Toda configuração de workload identity, em qualquer nuvem, é o mesmo triângulo de confiança:
- O cluster é um emissor OIDC. Ele expõe um discovery document em
<issuer-url>/.well-known/openid-configuratione um endpoint JWKS com as chaves públicas com as quais assina os tokens do ServiceAccount. Essa issuer URL é a âncora de confiança. - O IdP da nuvem confia nesse emissor para um par
(subject, audience)específico. O subject identifica qual ServiceAccount, e a audience identifica para quem o token é. - A workload troca o token projetado por credenciais de nuvem. O SDK lê o arquivo de token que o kubelet projetou, o apresenta ao token endpoint da nuvem e recebe credenciais de curta duração para uma única identidade de nuvem.
Todo o resto é nomenclatura. Aqui está o mapa, percorrido linha por linha.
O mapa um-para-um
- O lado do Kubernetes. Na AWS, você anota o ServiceAccount com
eks.amazonaws.com/role-arn: <role arn>. No Azure, você o anota comazure.workload.identity/client-id: <managed identity client id>e rotula o pod comazure.workload.identity/use: "true". Ambos apontam o pod para uma identidade de nuvem; o Azure precisa do label extra no pod (mais sobre isso abaixo). - O "OIDC provider". Na AWS, este é o OIDC issuer do cluster EKS registrado uma vez no IAM como um OIDC identity provider. No Azure, são duas configurações de cluster: o OIDC issuer (
oidcIssuerProfile.enabled) que publica a issuer URL e as chaves de assinatura, e o workload-identity add-on (securityProfile.workloadIdentity.enabled), um mutating admission webhook. Ambos devem estar ativados. - A identidade. AWS: um IAM role. Azure: uma user-assigned managed identity (UAMI). Este é o objeto que o pod se torna.
- A trust rule. AWS: a trust policy do IAM role federa o OIDC provider e fixa
sub = system:serviceaccount:<ns>:<sa>eaud = sts.amazonaws.com. Azure: uma federated identity credential na UAMI comissuer = <cluster OIDC issuer url>,subject = system:serviceaccount:<ns>:<sa>eaudience = api://AzureADTokenExchange. Os mesmos três campos, em locais diferentes. - As permissões. AWS: uma IAM policy anexada ao role (ler este S3 bucket, puxar deste ECR repo). Azure: RBAC role assignments nos recursos de destino (Key Vault Secrets User, Storage Blob Data Reader, AcrPull, ...). Esta é a divisão filosófica mais profunda - a AWS anexa uma policy à identidade; o Azure concede um role no escopo do recurso.
- A troca. AWS: o SDK chama
sts:AssumeRoleWithWebIdentitycom o token projetado. Azure: o SDK faz uma troca de token do Entra viaDefaultAzureCredential/WorkloadIdentityCredential, apresentando o token projetado como uma client assertion. Ambos resultam em credenciais de curta duração, ambos não precisam de material secreto em seu código.
O restante desta postagem percorre cada nuvem de ponta a ponta e, em seguida, aborda as diferenças que realmente confundem as pessoas.
Walk-through: AWS EKS IRSA, de ponta a ponta
As peças, na ordem em que a confiança flui:
1. O OIDC issuer do cluster. Todo cluster EKS tem um, em uma URL como https://oidc.eks.<region>.amazonaws.com/id/<hash>. Você o registra uma vez no IAM como um OIDC identity provider para que o IAM confie nos tokens que ele assina.
2. O IAM role e sua trust policy. A trust policy é o que torna o role assumível por um ServiceAccount específico e nada mais:
{
"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"
}
}
}
Fixe ambos :sub e :aud. Fixar apenas o issuer permitiria que qualquer ServiceAccount no cluster assumisse o role - um clássico bug de confiança excessiva.
3. A IAM policy. Uma policy normal baseada em identidade anexada ao role concede o que o aplicativo realmente precisa (s3:GetObject em um bucket, ecr:GetDownloadUrlForLayer, e assim por diante).
4. O ServiceAccount. Um ServiceAccount simples com uma anotação:
apiVersion: v1
kind: ServiceAccount
metadata:
name: checkout
namespace: apps
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<account>:role/checkout
5. A projeção. O EKS inclui o Pod Identity Webhook integrado ao cluster. Quando um pod usa um ServiceAccount anotado, o webhook o modifica: ele injeta as variáveis de ambiente AWS_ROLE_ARN e AWS_WEB_IDENTITY_TOKEN_FILE e monta um volume de token projetado (audience sts.amazonaws.com, auto-rotacionado). Você não rotula o pod; a anotação do SA é suficiente.
6. A troca. A cadeia de credenciais padrão do AWS SDK percebe AWS_WEB_IDENTITY_TOKEN_FILE, chama sts:AssumeRoleWithWebIdentity com o token e armazena em cache as credenciais de curta duração retornadas. Seu código é apenas boto3.client("s3") - sem manipulação de credenciais.
Alternativa mais recente: o EKS Pod Identity (2023) faz o mesmo trabalho por meio de um agente no cluster e uma API de associação, em vez de um IAM OIDC provider por cluster e uma trust policy por role. É mais fácil de operar em escala, mas é uma implementação específica da AWS; o modelo de federação OIDC acima é o que se mapeia de forma limpa para o Azure, então é o que você deve ter em mente para uma comparação entre nuvens.
Walk-through: Azure AKS Workload Identity, de ponta a ponta
Mesmo fluxo de confiança, termos do Azure:
1. Dois toggles de cluster. Ative o OIDC issuer (oidcIssuerProfile.enabled = true), que publica uma issuer URL como https://<region>.oic.prod-aks.azure.com/<tenant>/<guid>/, e o workload-identity add-on (securityProfile.workloadIdentity.enabled = true), que instala o mutating webhook. Ambos vêm desativados por padrão em um novo cluster, e a oidcIssuerUrl do cluster é nula até que o primeiro seja ativado. Ativá-los é uma atualização in-place, não uma recriação.
2. A managed identity. Crie uma user-assigned managed identity. (As federated credentials se anexam apenas a identidades user-assigned, não a system-assigned.) Seu client id é o que o ServiceAccount fará referência.
3. A federated identity credential. Esta é a trust rule, um objeto filho na UAMI:
issuer: https://<region>.oic.prod-aks.azure.com/<tenant>/<guid>/
subject: system:serviceaccount:apps:checkout
audience: api://AzureADTokenExchange
Observe a mesma gramática de subject que a AWS e uma audience fixa diferente.
4. O RBAC. Conceda ao principal da UAMI os roles de que ele precisa nos recursos de destino: Key Vault Secrets User em um vault, Storage Blob Data Reader em uma storage account, AcrPull em um registry se o aplicativo chamar o data plane do registry. Não há policy document na identidade; o acesso é feito por role assignments no escopo de cada recurso.
5. O ServiceAccount e o pod label. O ServiceAccount carrega a anotação client-id, e - esta é a parte que o pessoal da AWS esquece - o pod também deve ser rotulado:
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. A projeção. O webhook do add-on só modifica pods que carregam o label azure.workload.identity/use: "true". Quando ele é acionado, injeta AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_FEDERATED_TOKEN_FILE e AZURE_AUTHORITY_HOST, e projeta um token com audience api://AzureADTokenExchange.
7. A troca. DefaultAzureCredential() (ou o WorkloadIdentityCredential explícito) lê essas variáveis de ambiente, apresenta o token projetado ao Entra ID como uma client assertion e recebe de volta um access token para o recurso de destino. Uma leitura do Key Vault é azure.identity.DefaultAzureCredential() mais SecretClient em Python, ou new DefaultAzureCredential() mais @azure/keyvault-secrets em TypeScript - e, novamente, nenhum segredo em qualquer parte do código.
A string de assunto é o ponto central (em ambas as nuvens)
O único valor que deve corresponder exatamente, em ambas as nuvens, é o token subject:
system:serviceaccount:<namespace>:<serviceaccount-name>
O kubelet insere isso no token com base no namespace e ServiceAccount reais do pod. A trust rule (IAM trust policy na AWS, federated credential no Azure) fixa a string que aceitará. Se o aplicativo for executado no namespace apps sob o ServiceAccount checkout, a trust rule deve dizer system:serviceaccount:apps:checkout - caractere por caractere. A falha mais comum de "não autentica como ninguém" é uma incompatibilidade aqui: o chart foi implantado em um namespace diferente, o ServiceAccount recebeu o nome da release em vez do nome do aplicativo, ou alguém assumiu o ServiceAccount default. Quando a federação falha silenciosamente, verifique o subject primeiro.
A audience é o outro campo fixado, e é uma constante fixa por nuvem: sts.amazonaws.com na AWS, api://AzureADTokenExchange no Azure. Você raramente a altera, mas a trust rule e o token projetado devem concordar nela.
Quatro diferenças que realmente te confundirão
1. O Azure divide o "OIDC provider" em dois toggles, e ambos vêm desativados por padrão. No EKS, o pod-identity webhook é integrado ao cluster e o único passo único é registrar o OIDC provider no IAM. No AKS, você deve habilitar separadamente o OIDC issuer e o workload-identity add-on; se esquecer o add-on, o issuer ainda publica tokens, mas nada os projeta nos pods, então o aplicativo não vê variáveis de ambiente AZURE_* e o DefaultAzureCredential silenciosamente passa para a próxima fonte de credenciais. Habilite ambos e lembre-se de que a federated credential não pode ser criada até que a issuer URL exista.
2. O Azure precisa de um label no pod, não apenas de uma anotação no ServiceAccount. O webhook do AKS se baseia no label azure.workload.identity/use: "true" no pod (a anotação do ServiceAccount sozinha não é suficiente). O webhook do IRSA se baseia na anotação do ServiceAccount e não precisa de label no pod. Este é o erro mais comum do lado do Azure: o ServiceAccount parece perfeito, mas o pod template esqueceu o label, então nenhum token é projetado.
3. Puxar a imagem é uma identidade diferente da workload identity - em ambas as nuvens. O pull da imagem é trabalho da identidade do node/kubelet: no AKS, a identidade do kubelet possui AcrPull; no EKS, o instance role do node group possui AmazonEC2ContainerRegistryReadOnly (ou você usa pull secrets). A workload identity é necessária apenas quando o código do aplicativo chama uma API de nuvem (ler um secret, listar um bucket, chamar o data plane do registry). Um serviço HTTP puro que nunca se comunica com um SDK de nuvem não precisa de workload identity alguma - e, inversamente, conceder AcrPull a uma workload identity não faz nada para o pull da imagem, porque o pull acontece antes que o aplicativo (e seu token federado) seja executado.
4. As permissões se anexam de forma diferente. A AWS anexa uma IAM policy ao role - a permissão viaja com a identidade. O Azure concede RBAC role assignments no escopo do recurso de destino - a concessão reside no item que está sendo acessado, não na identidade. O mesmo estado final (este pod pode ler aquele vault), mas você procura em lugares diferentes para auditá-lo: na AWS, leia as policies anexadas ao role; no Azure, liste os role assignments no recurso (ou os assignments da identidade em todos os escopos). Isso reflete a diferença mais ampla entre IAM e RBAC entre as duas nuvens.
Como é o ambiente injetado
Ambos os webhooks fornecem ao SDK tudo o que ele precisa por meio de variáveis de ambiente e um arquivo projetado, para que o código do aplicativo não faça referência a nenhum segredo e nenhuma lógica de credencial específica da nuvem:
- AWS:
AWS_ROLE_ARN,AWS_WEB_IDENTITY_TOKEN_FILE,AWS_REGION. A cadeia de credenciais padrão chamaAssumeRoleWithWebIdentitypara você. - Azure:
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_FEDERATED_TOKEN_FILE,AZURE_AUTHORITY_HOST. ODefaultAzureCredentialfaz a troca do Entra para você.
Em ambos os casos, o arquivo de token projetado tem vida curta e é auto-rotacionado pelo kubelet; o SDK o relê e atualiza as credenciais da nuvem de forma transparente. Não há nada para rotacionar, nada para armazenar e nada para vazar.
Uma lista de verificação de depuração que funciona em ambas as nuvens
Quando um pod "não consegue autenticar", percorra a cadeia de confiança em ordem:
- Issuer do cluster ativado? Confirme se a OIDC issuer URL existe (AKS: ambos os toggles true; EKS: o IAM OIDC provider está registrado). Sem issuer, sem âncora de confiança.
- Projeção acontecendo? Execute um exec no pod e verifique o arquivo de token e as variáveis de ambiente (
AWS_WEB_IDENTITY_TOKEN_FILE/AZURE_FEDERATED_TOKEN_FILE). A ausência no Azure geralmente significa que o pod label está ausente ou o add-on está desativado. - Subject corresponde? Compare o subject da trust rule com
system:serviceaccount:<actual-namespace>:<actual-sa>. Esta é a falha mais frequente. - Audience corresponde?
sts.amazonaws.com/api://AzureADTokenExchange, concordando em ambos os lados. - Permissões? Somente depois que a identidade for resolvida: a IAM policy no role, ou o RBAC role assignment no recurso de destino. Um resultado vazio aqui é uma falha de autorização, não de autenticação - uma distinção útil, porque indica que a própria federação está funcionando.
Quais certificações abordam isso
A workload identity se encontra exatamente onde o Kubernetes, o cloud IAM e a federação OIDC se encontram, então ela aparece em três trilhas de exames. Se você já possui uma credencial de uma nuvem, o mesmo conceito na outra nuvem é um pequeno salto.
No lado da AWS:
- AWS Certified Solutions Architect - Associate (SAA-C03) - IAM roles, EKS e como as identidades obtêm permissões de nuvem.
- AWS Certified Security - Specialty (SCS-C03) - IAM trust policies, federação OIDC e escopo de privilégio mínimo (exatamente a cadeia de confiança do IRSA).
No lado do Azure:
- Microsoft Azure Administrator Associate (AZ-104) - AKS, managed identities e RBAC role assignments.
- Microsoft Azure Security Engineer Associate (AZ-500) - Entra ID, managed identities, federated credentials e profundidade de RBAC.
No lado do Kubernetes:
- CNCF Certified Kubernetes Administrator (CKA) - ServiceAccounts, projected tokens e pod specs.
- CNCF Certified Kubernetes Security Specialist (CKS) - Segurança de ServiceAccount token, privilégio mínimo e redução da exposição de segredos estáticos.
Conclusão
IRSA e AKS Workload Identity são a mesma ideia com vocabulários diferentes: o cluster assina um token OIDC de curta duração para um ServiceAccount, o IdP da nuvem confia nesse issuer para um (subject, audience), e o pod troca o token por credenciais com escopo para uma única identidade de nuvem - um IAM role na AWS, uma managed identity no Azure. Aprenda o triângulo de confiança uma vez e a tradução é mecânica: role se torna managed identity, trust policy se torna federated credential, attached policy se torna RBAC assignment, AssumeRoleWithWebIdentity se torna uma troca de token do Entra. Mantenha três coisas em mente - os dois toggles de cluster do Azure, o pod label obrigatório do Azure e o fato de que o pull da imagem é uma identidade completamente diferente - e as negações que antes pareciam aleatórias começam a ser lidas como um design coerente e deliberado.