Bastion EC2 sob demanda evita porta aberta no acesso ao RDS
Um script publicado por Marcelo Palenzuela no Medium liga a instância EC2 ponte só na hora do acesso, abre um túnel via Systems Manager até o RDS privado e desliga a ponte ao final, sem nunca abrir porta de entrada no Security Group.
Por Danilo Barros8 min de leitura
Quem administra RDS ou Aurora em subnet privada conhece o dilema: manter uma instância EC2 bastion ligada 24 horas por dia só para o acesso eventual de um DBA ou de um engenheiro de plantão é desperdício de fatura e superfície de ataque desnecessária. Um artigo publicado em 8 de novembro de 2026 no Medium por Marcelo Palenzuela descreve um jeito de resolver isso com AWS Systems Manager (SSM) Session Manager e um script shell que liga a ponte só quando alguém precisa dela.
O desenho parte de uma premissa comum em produção: o RDS fica sempre available, mas a EC2 que serve de bastion não precisa. Este tutorial reconstrói o fluxo descrito pela fonte, com o detalhe de cada passo e os pontos onde o ambiente de um arquiteto ou SRE normalmente escorrega: policy IAM mal escopada, Security Group permissivo, ou bastion esquecida ligada no fim de semana.
O que você vai construir
O resultado final é um túnel de port forwarding entre a máquina de trabalho (Linux Ubuntu, no caso da fonte) e o endpoint do RDS, passando por uma EC2 ponte dentro da VPC. O cliente do banco (psql, por exemplo) conecta em 127.0.0.1 numa porta local, e o SSM encaminha esse tráfego até a porta do banco no RDS.
A EC2 ponte não tem regra de entrada (Inbound) no Security Group: toda a sessão usa conexão de saída HTTPS (porta 443) para os endpoints ssm, ssmmessages e ec2messages. Um script, ssm-rds-tunnel.sh, automatiza o ciclo completo: consulta o status do RDS, liga a EC2 se estiver desligada, espera a conexão SSM ficar disponível, abre o túnel e, ao encerrar com CTRL+C, pergunta se deve desligar a ponte.
Em resumo: o RDS continua sem IP público nem porta exposta à internet, e a ponte que faz a ligação existe ligada apenas durante a janela de uso.
Pré-requisitos
Antes de reproduzir o fluxo, confirme:
- Uma instância RDS já criada e com status
available(o script não liga nem desliga o banco, só consulta). - Uma instância EC2 (Ubuntu recente ou Amazon Linux) com a tag
Namecom valor único no ambiente, usada como ponte. - Um instance profile na EC2 ponte com role contendo a policy gerenciada
AmazonSSMManagedInstanceCore. - SSM Agent na versão
3.1.1374.0ou superior na EC2 ponte, requisito específico do port forwarding para host remoto. - Security Group do RDS liberando entrada, na porta do banco, a partir do Security Group da EC2 ponte (referenciado como origem, não por CIDR).
- Na máquina de trabalho: AWS CLI v2, Session Manager Plugin, bash e o utilitário
ss(pacoteiproute2), usado pelo script para checar se a porta local está livre. - No IAM: um usuário ou grupo com a policy de privilégio mínimo do próximo passo e, adicionalmente, a policy gerenciada
SignInLocalDevelopmentAccess, necessária para oaws login.
Passo a passo
1. Instalar as ferramentas na máquina de trabalho
cd ~/
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/installcurl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "session-manager-plugin.deb"
sudo dpkg -i ./session-manager-plugin.deb
session-manager-plugin --version2. Validar o ambiente AWS
Antes de criar qualquer policy, confirme no console que a EC2 ponte aparece em Systems Manager como gerenciada (Fleet Manager), que o Security Group do RDS referencia o da ponte como origem na porta do banco, e que a rota entre as duas sub-redes existe.
3. Criar a policy IAM de privilégio mínimo
A policy restringe o usuário a ligar/desligar só a EC2 ponte identificada, consultar só o RDS indicado e abrir sessões SSM só nessa EC2 e no documento AWS-StartPortForwardingSessionToRemoteHost:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConsultarInstanciasEC2",
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
},
{
"Sid": "LigarEDesligarEC2Ponte",
"Effect": "Allow",
"Action": ["ec2:StartInstances", "ec2:StopInstances"],
"Resource": "arn:aws:ec2:<REGIAO>:<ID_CONTA_AWS>:instance/<ID_INSTANCIA_EC2_PONTE>"
},
{
"Sid": "ConsultarRDS",
"Effect": "Allow",
"Action": "rds:DescribeDBInstances",
"Resource": "arn:aws:rds:<REGIAO>:<ID_CONTA_AWS>:db:<NOME_DO_RDS>"
},
{
"Sid": "IniciarTunelHostRemoto",
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": [
"arn:aws:ec2:<REGIAO>:<ID_CONTA_AWS>:instance/<ID_INSTANCIA_EC2_PONTE>",
"arn:aws:ssm:*::document/AWS-StartPortForwardingSessionToRemoteHost"
]
},
{
"Sid": "OpenDataChannelPropriaSessao",
"Effect": "Allow",
"Action": "ssmmessages:OpenDataChannel",
"Resource": "arn:aws:ssm:*:*:session/${aws:userid}-*"
},
{
"Sid": "EncerrarEReconectarPropriasSessoes",
"Effect": "Allow",
"Action": ["ssm:TerminateSession", "ssm:ResumeSession"],
"Resource": "arn:aws:ssm:*:*:session/${aws:userid}-*"
}
]
}Substitua <REGIAO>, <ID_CONTA_AWS>, <ID_INSTANCIA_EC2_PONTE> e <NOME_DO_RDS> pelos valores reais. Essa é a parte que mais importa para quem responde pelo ambiente: sem ela, qualquer pessoa com ssm:StartSession genérico poderia abrir sessão em qualquer instância gerenciada da conta, não só na ponte.
4. Criar o script do túnel
Salve o conteúdo como ssm-rds-tunnel.sh e rode chmod +x ssm-rds-tunnel.sh. A lógica, descrita pelo autor, segue uma ordem fixa: valida AWS CLI, plugin e credenciais; consulta o RDS (status e endpoint) e aborta se ele não estiver utilizável; localiza a EC2 pela tag Name e recusa a execução se houver mais de uma com o mesmo valor; liga a EC2 se estiver parada e aguarda (WAIT_EC2, 60 segundos por padrão) até ela ficar running; checa se a porta local está livre; e abre o túnel.
O comando central, executado com o endpoint e a porta lidos diretamente do RDS, é este:
aws ssm start-session \
--target "$EC2_ID" \
--document-name AWS-StartPortForwardingSessionToRemoteHost \
--parameters "{\"host\":[\"${RDS_ENDPOINT}\"],\"portNumber\":[\"${RDS_PORT}\"],\"localPortNumber\":[\"${LOCAL_PORT}\"]}"Um detalhe de bash vale registrar: o script usa trap ':' INT para que o CTRL+C não mate o processo pai. É o processo filho (aws ssm start-session) que recebe o sinal e encerra a sessão; o script segue vivo até a pergunta final sobre desligar a EC2.
5. Autenticar o AWS CLI
aws login [--profile <NOME_DO_PROFILE>]O comando abre o navegador para login do usuário IAM e grava credenciais temporárias no profile. O script valida essas credenciais com aws sts get-caller-identity antes de qualquer outra chamada.
6. Abrir o túnel
./ssm-rds-tunnel.sh <NOME_DA_EC2> <NOME_DO_RDS> [<PROFILE_AWS>]Para trocar a porta local (útil quando a porta padrão do banco já está ocupada na máquina) ou o tempo de espera da EC2:
LOCAL_PORT=5433 WAIT_EC2=90 ./ssm-rds-tunnel.sh <NOME_DA_EC2> <NOME_DO_RDS> [<PROFILE_AWS>]7. Conectar o cliente do banco
Em outro terminal, com o túnel aberto, aponte o cliente para 127.0.0.1 na porta local escolhida. No exemplo da fonte, um RDS PostgreSQL:
psql -h 127.0.0.1 -p <PORTA_LOCAL> -U <USUARIO> -d <BANCO>Testando
O primeiro sinal de que o fluxo funcionou aparece nos próprios logs do script, que imprime o status consultado em cada etapa: RDS '<nome>' -> status: available; endpoint: <endpoint> e, em seguida, EC2 '<nome>' -> status: stopped ou running. Se a EC2 estava parada, o log mostra a contagem regressiva do WAIT_EC2 até a conexão SSM ficar disponível.
Com o túnel aberto, a linha Abrindo o túnel: 127.0.0.1:<porta> -> <endpoint>:<porta> (via <id-da-ec2>) confirma o encaminhamento. Rodar uma query simples pelo psql conectado em 127.0.0.1, como SELECT version(); ou SELECT now();, valida que o tráfego realmente chegou ao RDS e não ficou preso num SSM aberto sem destino. Se a query falhar com timeout, o problema costuma estar no Security Group do RDS, não no túnel em si: confira se ele realmente aceita o Security Group da ponte como origem na porta do banco.
Vale testar também o caminho de erro proposital: rodar o script apontando para um nome de RDS inexistente ou para uma tag Name duplicada em duas EC2s. Nos dois casos o script deve recusar a execução antes de tentar abrir qualquer sessão, o que é o comportamento esperado de uma policy e de um script pensados para rodar sem supervisão.
Limpando os recursos
Encerre o cliente do banco e pressione CTRL+C no terminal onde o script está rodando. Ele pergunta:
Deseja desligar a instância EC2 '<NOME_DA_EC2>' (<ID_INSTANCIA_EC2_PONTE>)? Sim (S) ou Não (N) [padrão: S]:Responda S (ou Enter) para desligar a ponte. A fonte não traz um valor de preço específico, mas é importante lembrar: a EC2 parada deixa de cobrar pelo uso de computação, mas os volumes EBS anexados continuam sendo cobrados enquanto existirem. Se a ponte for usada só em testes pontuais, vale encerrar (terminate) a instância de verdade, e não apenas pará-la, além de remover o Security Group criado para ela e desanexar a role e a policy do usuário de teste do IAM.
O RDS, neste desenho, permanece sempre ligado e não faz parte da limpeza: ele é o banco de produção, não um recurso de laboratório. Quem estiver reproduzindo o fluxo só para aprender deve lembrar de apagar a instância RDS de teste separadamente, pelo ciclo normal do serviço, se ela não for necessária depois do exercício.
Próximos passos
Ligar a ponte apenas quando necessário reduz a superfície de acesso, já que ela fica desligada fora das janelas de uso.
Marcelo Palenzuela, autor do artigo no Medium
O próprio autor aponta três direções naturais a partir daqui. A primeira é registrar as sessões do Session Manager em CloudWatch Logs ou S3, o que transforma o túnel num rastro auditável: quem abriu, quando e por quanto tempo, sem depender de log de aplicação ou de acesso direto ao banco. A segunda é restringir a policy por tags quando o ambiente tiver mais de uma ponte, trocando <ID_INSTANCIA_EC2_PONTE> fixo por uma condição aws:ResourceTag que valide o time ou o ambiente. A terceira é adaptar o script ao fluxo real da equipe.
Nessa adaptação, dá para ir além do que a fonte descreve: um EventBridge Scheduler que força o desligamento da ponte depois de um tempo de inatividade, mesmo que alguém esqueça o terminal aberto, fecha o cenário em que o custo volta a vazar por descuido. Para ambientes com Aurora em cluster, o mesmo padrão se aplica trocando o describe-db-instances por describe-db-clusters e apontando o túnel para o endpoint de leitura ou de escrita conforme o caso de uso. Em qualquer dos dois, o ganho real não é a ferramenta em si, é tirar o bastion da lista de coisas que ficam ligadas por hábito.
Fonte: Medium — tag aws
Serviços neste textoRDS e Aurora
Este artigo foi escrito por Danilo Barros. Conteúdo produzido pela redação de agentes de IA do AWS Hub, a partir de fontes oficiais citadas no texto. Saiba como produzimos no expediente.