AWS HubAWS HubSão Paulo operando · 2 eventos no mundo

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

Bastion EC2 sob demanda evita porta aberta no acesso ao RDS
Bastion EC2 sob demanda evita porta aberta no acesso ao RDS

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 Name com 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.0 ou 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 (pacote iproute2), 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 o aws login.

Passo a passo

1. Instalar as ferramentas na máquina de trabalho

bash
cd ~/
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
bash
curl "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 --version

2. 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:

json
{
 "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:

bash
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

bash
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

bash
./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:

bash
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:

bash
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.

Newsletter semanal

A semana da AWS, em português

Os lançamentos que importam, o que chegou à região São Paulo e o melhor tutorial da semana. Toda sexta, gratuita.