Toda vez que eu precisava que uma aplicação na AWS acessasse o S3, eu fazia a mesma coisa: criava um usuário no IAM, gerava uma access key, colava no .env e seguia em frente.
Funciona. Mas tem um problema. E se esse .env vazar? Um git push errado, um servidor invadido. A chave continua válida até alguém perceber e revogar. Pode demorar dias.
A verdade é que, dentro da AWS, você não precisa de chave nenhuma. Suas instâncias acessam outros serviços sem guardar segredo. Vou explicar como.
A pergunta que o IAM responde
O IAM serve pra responder uma coisa só: quem pode fazer o quê, em quais recursos, sob quais condições. Se você guardar essa frase, o resto é detalhe.
Access key x role
Uma access key é um segredo que não expira. Vale até você apagar na mão. É como uma senha anotada num papel: prática, mas perigosa se vazar.
Uma role é diferente. Ela é uma identidade temporária que a sua instância assume por um tempo. Ninguém é dono dela. A instância veste a role e ganha credenciais que expiram sozinhas em poucas horas.
Pensa num crachá. A access key é o crachá fixo de um funcionário. A role é um crachá de visitante: a instância pega emprestado, usa, e ele se renova.
O código não tem credencial
Esse é um servidor Node que lista um bucket S3:
const { S3Client, ListObjectsV2Command } = require("@aws-sdk/client-s3");
// Repare: nenhuma credencial passada aqui.
const s3 = new S3Client({ region: "us-east-1" });
const data = await s3.send(new ListObjectsV2Command({ Bucket: "meu-bucket" }));
Sem .env. Sem access key. Sem secret. Rodando numa EC2 com a role certa, funciona.
O SDK da AWS procura as credenciais sozinho. Dentro da AWS, ele acha elas na própria instância, entregues pela role. Mas como isso funciona de verdade? É o que as próximas seções mostram.
Como a role funciona por dentro
Uma role tem duas partes, e cada uma responde uma pergunta diferente.
A primeira é a trust policy. Ela diz quem pode assumir a role. No nosso caso, ela diz que o serviço EC2 pode assumir. É como a etiqueta no crachá de visitante: "este crachá pode ser usado por funcionários do EC2".
A segunda é a permissions policy. Ela diz o que a role pode fazer depois de assumida. No nosso caso, ler o bucket S3.
As duas precisam existir. Trust policy sem permissão: a instância assume a role mas não faz nada. Permissão sem trust policy: ninguém consegue assumir. Juntas, elas formam a role completa.
Quem assume a role na prática é o serviço STS (Security Token Service). Ele é o cara que emite as credenciais temporárias. Quando a EC2 vai usar a role, por baixo dos panos ela está chamando o STS e dizendo "quero assumir essa role". O STS confere a trust policy, vê que o EC2 pode, e devolve as credenciais temporárias.
O refresh acontece sozinho
As credenciais que a instância recebe expiram em poucas horas. Antes de expirarem, a AWS já coloca um conjunto novo no lugar, e o SDK pega o novo na próxima chamada.
Isso se repete a vida toda da instância, sem você fazer nada. O acesso nunca para, mas cada credencial individual dura pouco. Se uma vazar, ela morre em horas. E ninguém precisa lembrar de trocar senha, porque a rotação é automática.
O instance profile
Tem uma peça que confunde e quase nenhum tutorial explica.
A EC2 não anexa uma role direto. Ela anexa um instance profile, que é um envelope em volta da role. Pensa nele como o suporte de crachá preso na máquina. A EC2 só sabe olhar pro suporte. Você encaixa o crachá (a role) nele.
No console da AWS isso acontece num clique, escondido. Pela linha de comando você vê os passos separados: cria a role, cria o instance profile, encaixa um no outro, anexa na instância.
A ordem é essa: a permissão (policy) fica dentro da role, a role fica dentro do instance profile, e o instance profile fica anexado na EC2.
Por que é mais seguro
Access key no .env: se vazar, o atacante tem acesso total por tempo indeterminado, até alguém revogar na mão.
Role na instância: não tem segredo guardado pra vazar. E se alguém pegar as credenciais temporárias, elas expiram em horas.
Quando ainda usar access key
A regra é simples:
- Aplicação dentro da AWS (EC2, Lambda, ECS): use role.
- Algo de fora (seu notebook, um CI externo, um sistema on-premises): aí sim use access key, porque não tem instância pra assumir role.
A access key não é o problema. O problema é usar ela dentro de uma EC2.
Bônus: só um servidor pode acessar o S3
Cenário real: você tem três servidores, uma API, um Redis e um Postgres. Só a API pode falar com o S3. Como garantir?
Simples: você anexa o instance profile só na API. Redis e Postgres não recebem a role, então não acessam o S3. Nem que tentem.
O controle não está na role. Está em onde você anexa. Cada servidor carrega só o crachá que precisa. Se o Redis for invadido, o atacante não chega no S3, porque o Redis nunca teve esse acesso.
Resumindo
O pulo do gato é parar de pensar em guardar uma chave e começar a pensar que a máquina assume uma identidade temporária.
A partir daí tudo se encaixa. O STS emite as credenciais temporárias. O instance profile liga a role na máquina. O SDK pega tudo sozinho e a rotação roda nos bastidores.
Você escreve o código uma vez, sem segredo nenhum, e ele roda seguro em produção. Depois que entendi isso, colocar access key dentro da AWS passou a parecer o que é: um risco que não precisa existir.
Desafio: faça você mesmo
Teoria é uma coisa, fazer na mão é outra. Então fica o desafio.
Eu deixei o código pronto num repositório, pra você não precisar se preocupar com a parte de programação e focar no que importa aqui: o IAM.
Código: https://github.com/JoaoVitorLima242/blog-content/tree/main/iam-role-with-s3-permission
A missão é essa:
- Suba esse código numa instância EC2.
- Crie uma role com permissão de acesso ao S3.
- Anexe essa role na instância.
- Rode a aplicação e veja ela acessar o S3.
O objetivo é fazer isso sem gerar nenhuma access key. Nada de secret no .env, nada de chave guardada na máquina. Só a role fazendo o trabalho.
Se a aplicação conseguir falar com o S3 e você não colocou uma chave sequer, parabéns: você acabou de fazer na prática tudo que esse post explicou.