Isso aconteceu de verdade, no trabalho. Tirei nomes de empresa, contas, domínios, repositórios e qualquer detalhe sensível.
01 / CONTEXTO
A aplicação estava no Kubernetes, mas sua identidade ainda era uma access key.
Os microsserviços no EKS precisavam acessar S3 e secrets. Do jeito que estava, recebiam AWS_ACCESS_KEY_ID eAWS_SECRET_ACCESS_KEY por variável de ambiente. Ou seja, chave fixa, e a segurança da aplicação dependia de ninguém errar ao guardar um secret.
Com IRSA, liguei uma IAM Role ao ServiceAccount de cada aplicação. O pod passou a receber uma credencial temporária, e nenhuma chave da AWS ficou no Kubernetes nem no código.
PRINCÍPIO DO PROJETOCada aplicação com a sua identidade, e acesso só ao que é dela.
02 / DESAFIO
A infraestrutura estava pronta para o IRSA. A aplicação, ainda não.
Se eu apagasse as chaves antes de mexer no código, a aplicação caía. Alguns clientes da AWS eram criados passando a chave direto, sem usar o caminho padrão do SDK para buscar credencial. Tive que acertar infraestrutura, manifests e código juntos.
Código
O cliente da AWS tinha que pegar a credencial do ambiente, não do código.
Kubernetes
Cada aplicação com o seu ServiceAccount, ligado à role certa.
IAM
A policy só com o recurso e as ações que a aplicação usa de verdade.
Transição
Tirar as chaves sem derrubar nenhuma aplicação.
03 / ARQUITETURA
Do ServiceAccount até a credencial temporária.
O Kubernetes entrega ao pod um token ligado ao ServiceAccount dele. O SDK da AWS usa esse token para pedir ao STS uma sessão da role. Antes de liberar, a trust policy confere o provedor OIDC do cluster, o namespace e o nome do ServiceAccount.
KUBERNETESO ServiceAccount diz qual aplicação é, dentro do cluster.
IAMA role tem só o que a aplicação pode fazer.
SDKO SDK busca e renova a credencial sozinho.
04 / IMPLEMENTAÇÃO
Infraestrutura e código tinham que andar juntos.
ServiceAccount dedicado por aplicação
Cada aplicação ganhou o seu ServiceAccount, com uma anotação apontando para a role dela. Assim duas aplicações não dividem a mesma identidade sem motivo.
apiVersion: v1
kind: ServiceAccount
metadata:
name: api-pagamentos
namespace: pagamentos-dev
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::<conta>:role/dev-api-pagamentos-irsaapiVersion: apps/v1
kind: Deployment
metadata:
name: api-pagamentos
namespace: pagamentos-dev
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: api-pagamentos
template:
metadata:
labels:
app.kubernetes.io/name: api-pagamentos
spec:
serviceAccountName: api-pagamentos # a identidade vem daqui
containers:
- name: api-pagamentos
image: <conta>.dkr.ecr.us-east-1.amazonaws.com/api-pagamentos:1.4.2-8f3a1c2
env:
- name: AWS_REGION
value: us-east-1
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
# Nenhuma access key é injetada no pod.SDK utilizando a cadeia padrão de credenciais
Tirei do código o trecho que passava access key e secret key. O cliente ficou só com a região, e o SDK encontra sozinho a credencial temporária do pod.
const client = new S3Client({
region: process.env.AWS_REGION
});
// Sem accessKeyId ou secretAccessKey no código.Permissão mínima no recurso correto
Montei as policies por pasta do bucket e por ação. Uma aplicação que só grava arquivo não ganha de brinde permissão para listar o bucket inteiro, apagar a pasta dos outros ou mudar a configuração do serviço.
Preservação das fronteiras existentes
Coloquei o IRSA sem abrir mais permissão no bucket e sem mexer na distribuição privada que já existia. A role da aplicação e a policy do bucket continuam funcionando juntas, uma reforçando a outra.
05 / MIGRAÇÃO
Só apaguei as chaves depois de testar o caminho inteiro.
- 01
Mapeamento
Levantei quais aplicações usavam chave, em quais pastas e para fazer o quê.
- 02
Infraestrutura
Criei as roles, as trust policies e os ServiceAccounts de cada ambiente.
- 03
Aplicação
Ajustei o código para o SDK buscar a credencial sozinho.
- 04
Validação
Testei subir a aplicação, ler, gravar e ver o acesso negado onde devia ser negado.
- 05
Remoção
Com tudo funcionando, apaguei as variáveis e os secrets com as chaves.
06 / RESULTADOS
Segurança em cada aplicação, não só no cluster.
Não tenho números para mostrar, então conto o que mudou.
Acabaram as access keys na aplicação a credencial agora vem do ambiente e expira.
Cada aplicação no seu quadrado com ServiceAccount e role próprios.
Só a permissão necessária por ação, recurso e pasta.
Ninguém mais troca chave a credencial se renova sozinha.
Erro mais fácil de achar dá para ver se o problema é código, identidade ou permissão.
07 / APRENDIZADOS
IRSA não é só configurar o cluster.
Para funcionar, três coisas precisam estar certas ao mesmo tempo: a role na AWS, o ServiceAccount do deployment e o jeito que a aplicação cria o cliente da AWS. Se qualquer uma continuar esperando chave fixa, não funciona.
O que mais ajudou foi tratar a identidade como parte da aplicação, e não como detalhe de infraestrutura. Assim deu para apagar os secrets sensíveis sabendo exatamente qual serviço acessa cada recurso.
william@devops:~$ echo $LESSON_LEARNED
"No Kubernetes, cada aplicação deve ter a sua identidade, e nenhuma permissão a mais."