03 - Service Accounts e OIDC
video: https://youtu.be/... no frontmatter da nota.Service Accounts e OIDC
Workloads também são identidades: uma API em Kubernetes, um job de CI/CD e uma função serverless precisam provar quem são para chamar outros sistemas, e essa prova não pode ser uma senha gravada em arquivo de configuração. Service accounts são as identidades nativas de workloads, e a federação OIDC (OpenID Connect) é o mecanismo que elimina credenciais estáticas desse cenário, permitindo que o workload apresente um token de curta duração assinado que um provedor de nuvem troca por credenciais temporárias. Em produção, a combinação das duas técnicas é o que separa arquiteturas onde o vazamento de um repositório é um incidente de arquiteturas onde o vazamento não concede nada. O tema é central no módulo porque ataca a maior fonte de incidentes em nuvem: chaves de acesso de longa duração espalhadas por pipelines e contêineres.
Identidade de workload
Identidade humana e identidade de workload têm dinâmicas diferentes e não devem compartilhar modelo. Humanos autenticam por sessão interativa com MFA; workloads autenticam milhares de vezes por minuto, sem pessoa presente, e suas credenciais vivem onde qualquer dump de memória ou commit descuidado pode expor. Por isso, o desenho de identidade de workload persegue duas propriedades: ausência de segredo de longa duração e vínculo verificável entre o contexto de execução e a permissão.
A federação atende às duas propriedades. O workload nunca guarda segredo compartilhado; ele recebe do provedor de identidade um token JWT assinado, cujo payload descreve o contexto, como repositório, branch e ambiente de execução. O provedor de nuvem valida a assinatura contra as chaves públicas publicadas no endpoint OIDC e emite credenciais temporárias com o escopo mapeado. O segredo vira um artefato efêmero, renovado a cada execução.
Service accounts no Kubernetes
No Kubernetes, a ServiceAccount é o objeto que representa a identidade de um workload, e todo pod roda sob uma delas, explicitamente ou sob a default do namespace. A conta por si só não concede privilégio nenhum; o que autoriza é a combinação da ServiceAccount com RBAC, que liga a conta a roles de cluster ou de namespace. Boa prática estrutural: uma ServiceAccount por serviço e por finalidade, nunca a default, para que o escopo do pod seja legível no manifesto e auditável no cluster.
# ServiceAccount dedicada ao serviço de faturamento, sem automount genérico
apiVersion: v1
kind: ServiceAccount
metadata:
name: faturamento-api
namespace: producao
annotations:
# IRSA: mapeia a ServiceAccount para a role IAM no EKS
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/faturamento-api-prod
automountServiceAccountToken: true
---
# RBAC: a conta só pode ler configmaps do próprio namespace
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: faturamento-api-leitura
namespace: producao
subjects:
- kind: ServiceAccount
name: faturamento-api
namespace: producao
roleRef:
kind: Role
name: leitor-configmap
apiGroup: rbac.authorization.k8s.io
O token projetado no pod fica em um arquivo montado pelo kubelet, com expiração e audiência definidas, e renovação automática pelo TokenRequest API. O annotation no exemplo é a ponte do IRSA, tratada na sequência.
IRSA e Pod Identity
O IAM Roles for Service Accounts (IRSA) resolve o problema clássico do EKS: todos os pods no mesmo node compartilham a identidade IAM da instância, a menos que o cluster exponha um provedor OIDC que permita trocar o token da ServiceAccount por credenciais da role específica do pod. O fluxo tem quatro passos: o cluster tem um provedor OIDC registrado na conta; a ServiceAccount carrega a annotation com o ARN da role; a trust policy da role confia no provedor OIDC do cluster com condições de namespace e ServiceAccount; o SDK do workload usa o token montado para assumir a role e recebe credenciais temporárias.
- Escopo por pod: cada serviço ganha role própria, encerrando o antipadrão de instância com permissão para tudo.
- Sem segredo estático: nenhuma Access Key no pod, no env var ou no configmap; a credencial nasce e morre com a sessão.
- Auditoria precisa: o CloudTrail registra a role usada e a ServiceAccount de origem, o que liga chamada de API ao serviço exato.
- Menos blast radius horizontal: comprometer um pod não herda as permissões dos vizinhos no mesmo node.
O EKS Pod Identity é a alternativa mais recente ao IRSA: em vez de provedor OIDC por cluster, o agente do EKS troca o token por credenciais usando uma associação gerenciada entre ServiceAccount e role, com menos passos de configuração. A escolha entre eles é operacional; o princípio é o mesmo, identidade de pod mapeada a role temporária.
Federação OIDC para CI/CD
Pipelines são o caso mais frequente de chave estática mal cuidada: o deploy precisa de credenciais, e a solução rápida é gravar a Access Key como secret do repositório. Com federação OIDC, o GitHub Actions (ou GitLab CI, ou qualquer runner que emita token OIDC) apresenta um token assinado por referência ao provedor OIDC da conta, e assume a role de deploy sem segredo algum cadastrado. A trust policy condição o token por repositório e por branch, de modo que nem um fork rodando o workflow consegue assumir a role.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConfiarSomenteNoWorkflowDeDeploy",
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:empresa/faturamento:ref:refs/heads/main"
}
}
}
]
}
# Pipeline GitHub Actions assumindo role AWS por OIDC, sem secrets de chave
permissions:
id-token: write # único permissionamento necessário para a federação
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Assumir role de deploy
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/deploy-ci
aws-region: sa-east-1
- name: Aplicar infraestrutura
run: terraform -chdir=infra apply -auto-approve
A role assumida pelo pipeline deve ser a mais estreita possível, com permissão apenas sobre os recursos que o deploy toca. A rotação é automática por desenho: o token vale minutos, e as credenciais emitidas expiram junto.
Rotação automática de credenciais
O benefício silencioso da federação é eliminar a tarefa humana de rotação. Credenciais estáticas exigem agendamento, coordenação com sistemas consumidores e janela de risco no momento da troca; na prática, muitas organizações simplesmente não rotacionam, e a chave de dois anos atrás continua válida. Com OIDC, a rotação é propriedade do mecanismo: cada execução recebe token novo, assinado pelas chaves rotativas do provedor de identidade, e cada sessão STS expira sozinha.
Onde credencial estática é inevitável, por limitação de um fornecedor legado, o mínimo aceitável é rotação automatizada por segredo gerenciado, com o Secret Manager do provedor renovando e distribuindo, e alerta para qualquer credencial acima da idade máxima definida pela política. O objetivo é que credencial de longa duração seja anomalia com alarme, e não configuração corrente.
Armadilhas de credencial embutida
- Chave de acesso em variável de ambiente de contêiner: aparece em dump, em log de crash e em inspeção de pod por qualquer pessoa com acesso de leitura.
- Credencial em arquivo no repositório: histórico do git preserva tudo, e remover o arquivo não remove o segredo do histórico.
- Token de longa duração em secret do CI: a federação OIDC existe exatamente para eliminar esse padrão, e a secret gráfica costuma ser o ponto de partida do incidente.
- Credencial de serviço reutilizada por vários workloads: escopa demais e impede revogação cirúrgica; identidade por workload, sempre.
- Segredo copiado em imagem: build com credencial em camada de Dockerfile publica o segredo no registry junto com a imagem.
- Condições fracas na trust policy OIDC: confiar no provedor GitHub inteiro sem restringir sub permite que qualquer repositório público assuma a role de deploy.
Comparações
| Critério | Access Key estática | Federação OIDC | IRSA / Pod Identity |
|---|---|---|---|
| Duração da credencial | Permanente até rotação | Minutos, por execução | Minutos, por sessão de pod |
| Segredo armazenado | Sim, em secret ou arquivo | Não | Não |
| Rotação | Manual e agendada | Automática por execução | Automática por sessão |
| Auditabilidade | Principal genérico | Contexto de repo e branch | ServiceAccount e namespace |
| Risco de vazamento em git | Alto | Sem segredo para vazar | Sem segredo para vazar |
| Adequação | Legado sem alternativa | CI/CD e runners externos | Workloads em EKS |
Erros comuns
- Usar a ServiceAccount default em produção, que costuma acumular permissões de conveniência de namespace; conta dedicada por serviço é o ponto de partida.
- Trust policy OIDC sem restrição de sub, aceitando qualquer repositório da organização ou da internet; a condição por repositório e branch é o que torna a federação segura.
- Role de deploy com permissão de IAM quando o pipeline só aplica infraestrutura de rede e compute; o excesso vira prêmio para quem comprometer o workflow.
- Automount herdado por pods que não usam API do Kubernetes, ampliando superfície sem necessidade; desligue automountServiceAccountToken onde não se usa.
- Misturar identidade humana e de workload na mesma role, o que quebra a análise de uso e a separação de deveres.
- Esquecer a expiração do token em jobs longos: sessões de build que ultrapassam a validade falham com erro de credencial; dimensione a sessão STS para a duração real do job.
Boas práticas em produção
Inventarie e extermine: rode uma varredura por Access Keys ativas e por secrets de CI contendo credenciais AWS, e substitua cada uma por federação OIDC ou por role de workload, mantendo chave estática só onde o fornecedor não oferece alternativa. Padronize a criação de ServiceAccount e role por serviço em módulos de IaC, com trust policy condicionada por namespace e por conta, para que o padrão seguro seja o caminho fácil.
Meça a maturidade por três indicadores: número de credenciais estáticas ativas na conta, idade da credencial mais antiga e percentual de workloads com identidade dedicada. Publique esses números no dashboard da plataforma, porque essa visibilidade é o que mantém a extinção de chaves estáticas como projeto contínuo e não como campanha única.