ProxyJump SSH: Configure ~/.ssh/config para Acessar Múltiplas VPS via Bastion Host

Configure ProxyJump no ~/.ssh/config pra conectar em múltiplas VPS internas via bastion host sem expor SSH na internet. Guia técnico passo a passo.

Expor SSH diretamente em cada VPS é cômodo no começo — você abre porta 22, libera no firewall e conecta. Em produção, isso vira um problema: a superfície de ataque cresce linearmente com cada servidor adicionado, logs de tentativa de força bruta se espalham por múltiplas máquinas e gerenciar chaves autorizadas em N VPS rapidamente fica insustentável.

A solução padrão da indústria é o bastion host (também chamado de jump host): uma única VPS pequena, endurecida e exposta publicamente que serve como ponto único de entrada. Todas as outras VPS ficam em rede privada, sem porta 22 pública. Você conecta no bastion, e dele salta pras VPS internas.

Este tutorial mostra como configurar isso direito usando a diretiva nativa ProxyJump do OpenSSH no arquivo ~/.ssh/config. Tempo de execução: 15-20 minutos. Persona-alvo: sysadmin com 2+ VPS em produção que quer reduzir superfície de ataque e centralizar acesso.

Pré-requisitos

Antes de começar

Você precisa de OpenSSH 7.3 ou superior no cliente local (verifique com ssh -V). Distribuições Linux modernas e macOS 10.15+ já vêm com versão compatível. Windows 10 build 1803+ e Windows 11 também suportam ProxyJump via OpenSSH nativo do PowerShell.

Confirme o cenário antes de continuar:

Bastion host bastion.exemplo.com (IP público)
VPS interna 1 10.0.0.10 (rede privada)
VPS interna 2 10.0.0.11 (rede privada)
Porta SSH bastion 22 (ou customizada)

Você precisa também: chave SSH gerada (ed25519 recomendado) com a pública já copiada pro bastion via ssh-copy-id, e as chaves correspondentes copiadas pra cada VPS interna. Acesso sudo nos servidores caso precise ajustar /etc/ssh/sshd_config.

Estrutura do arquivo ~/.ssh/config

O ~/.ssh/config é onde você declara apelidos e parâmetros pra hosts SSH. Ele é lido pelo cliente OpenSSH automaticamente e permite simplificar conexões — em vez de digitar ssh -p 2222 -i ~/.ssh/id_ed25519 [email protected], você digita apenas ssh bastion.

Cada bloco começa com Host <apelido> seguido de parâmetros indentados. A leitura é top-down: a primeira regra que casa com o nome do host vence. Use Host * no final pra defaults globais.

01

Verifique se o arquivo existe e tem permissões corretas:

mkdir -p ~/.ssh
touch ~/.ssh/config
chmod 700 ~/.ssh
chmod 600 ~/.ssh/config

Permissões erradas (qualquer coisa além de 600 pro config e 700 pro diretório) fazem o cliente OpenSSH ignorar o arquivo silenciosamente em algumas distribuições — é a primeira coisa a checar quando “minha configuração não funciona”.

02

Abra o arquivo no editor de sua preferência:

nano ~/.ssh/config

Você vai adicionar 3 blocos: um pro bastion host, um pra cada VPS interna, e (opcionalmente) um bloco Host * com defaults razoáveis.

Configuração básica com ProxyJump

A diretiva ProxyJump (atalho -J na linha de comando) diz ao OpenSSH pra abrir uma conexão TCP no host destino através de outro host intermediário, tudo numa única invocação. O salto é transparente: você roda ssh vps-app e o cliente conecta no bastion, abre um túnel até a VPS interna e autentica nela end-to-end.

03

Adicione o bloco do bastion host:

Host bastion
    HostName bastion.exemplo.com
    User deploy
    Port 22
    IdentityFile ~/.ssh/id_ed25519_bastion
    IdentitiesOnly yes

IdentitiesOnly yes é importante: força o cliente a usar apenas a chave declarada em IdentityFile, em vez de tentar todas as chaves carregadas no ssh-agent. Sem isso, o bastion pode receber 5-6 tentativas de chave erradas antes da correta, e configurações com MaxAuthTries baixo recusam a conexão.

04

Adicione os blocos das VPS internas com ProxyJump:

Host vps-app
    HostName 10.0.0.10
    User deploy
    IdentityFile ~/.ssh/id_ed25519_internal
    IdentitiesOnly yes
    ProxyJump bastion

Host vps-db
    HostName 10.0.0.11
    User deploy
    IdentityFile ~/.ssh/id_ed25519_internal
    IdentitiesOnly yes
    ProxyJump bastion

O valor bastion em ProxyJump referencia o bloco Host bastion declarado antes — não é hostname literal, é o apelido SSH. Você pode usar a mesma chave pras VPS internas (caso elas estejam em rede confiável) ou separar por host conforme a política de segurança.

05

Adicione defaults globais no final do arquivo:

Host *
    AddKeysToAgent yes
    ServerAliveInterval 60
    ServerAliveCountMax 3
    HashKnownHosts yes

ServerAliveInterval 60 faz o cliente enviar um keepalive a cada 60 segundos — evita que NAT/firewall derrubem conexões idle. HashKnownHosts yes mascara hostnames em ~/.ssh/known_hosts, útil pra não vazar topologia interna caso o arquivo seja exposto.

Salto múltiplo encadeado

Em ambientes segmentados com mais de uma camada de bastion (ex: bastion público → bastion DMZ → VPS app), ProxyJump aceita lista separada por vírgula.

06

Configure salto duplo:

Host vps-prod
    HostName 10.10.0.50
    User deploy
    ProxyJump bastion,dmz-jump

O cliente conecta sequencialmente: local → bastion → dmz-jump → vps-prod. Cada salto adiciona latência (handshake TCP + autenticação SSH). Na prática, 2 saltos é o limite onde a experiência de uso ainda é fluida — acima disso, considere uma VPN.

Latência cumulativa

Cada salto adiciona o RTT entre os hosts envolvidos. Se bastion está no Brasil e DMZ na Europa, o RTT total pra abrir prompt SSH pode passar de 500ms. Pra operação interativa intensiva, considere consolidar bastions na mesma região.

Endurecer o bastion host

O bastion vira o alvo concentrado de ataques — vale endurecê-lo além do padrão.

07

No bastion, edite /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Aplique estas configurações:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers deploy
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowTcpForwarding yes

AllowTcpForwarding yes é obrigatório — sem isso o ProxyJump quebra com erro channel 0: open failed: administratively prohibited. AllowUsers deploy restringe quem pode autenticar (lista branca). MaxAuthTries 3 corta força bruta cedo.

08

Recarregue o serviço SSH e teste antes de fechar a sessão atual:

sudo sshd -t && sudo systemctl reload ssh

sshd -t valida a sintaxe do arquivo antes do reload. Pular essa validação e ter erro de sintaxe deixa o serviço sem subir — você fica trancado fora.

Não feche a sessão atual antes de validar

Sempre abra uma segunda sessão SSH num terminal separado e teste o login com as novas configurações antes de encerrar a sessão original. Se você cometeu erro (chave errada, firewall bloqueando, sshd não subiu), ainda tem a sessão antiga ativa pra reverter.

Verificação

Com tudo configurado, testar é simples — o cliente abstrai o salto.

09

Conecte na VPS interna usando o apelido:

ssh vps-app

Se tudo está certo, você cai direto no prompt da vps-app sem nenhuma interação intermediária. Você pode adicionar -v (ssh -v vps-app) pra ver a negociação verbosa e confirmar que o salto via bastion aconteceu — procure por linhas como debug1: Setting up multiplex master socket e debug1: Connecting to 10.0.0.10 [10.0.0.10] port 22.

10

Confirme que a porta SSH das VPS internas está realmente fechada externamente:

nmap -p 22 10.0.0.10

Rodando isso a partir de qualquer máquina fora da rede privada, o resultado deve ser filtered ou closed. Se mostrar open, sua VPS interna ainda tem porta 22 acessível publicamente — corrija o firewall (ufw deny 22 ou regra de security group no provedor).

Resolução de problemas

Erro: “channel 0: open failed: administratively prohibited”

O bastion está com AllowTcpForwarding no no sshd_config. Edite o arquivo no bastion, mude pra yes e rode sudo systemctl reload ssh.

Erro: “Permission denied (publickey)” no segundo salto

A chave declarada em IdentityFile pra VPS interna não está autorizada nela. Copie a chave pública pra VPS interna via bastion: ssh-copy-id -i ~/.ssh/id_ed25519_internal.pub -o ProxyJump=bastion [email protected].

Conexão lenta no primeiro salto

Geralmente é DNS reverso lento no bastion. Adicione UseDNS no em /etc/ssh/sshd_config do bastion e recarregue o serviço. Reduz tempo de autenticação de 5-15 segundos pra subsegundo.

Conexão cai depois de alguns minutos idle

Falta ServerAliveInterval. Confirme que o bloco Host * no ~/.ssh/config tem ServerAliveInterval 60 e ServerAliveCountMax 3. Se o NAT do provedor é agressivo, reduza pra ServerAliveInterval 30.

Próximos passos

Com ProxyJump configurado, você pode evoluir o setup:

  • Adicionar 2FA no bastion via PAM + Google Authenticator pra exigir TOTP além da chave SSH.
  • Centralizar logs do bastion num syslog remoto pra ter trilha de auditoria de todos os acessos.
  • Implementar gravação de sessão com tlog ou auditd no bastion — útil pra compliance.
  • Failover de bastion com 2 instâncias e DNS round-robin pra evitar single point of failure.
  • Configurar SSH certificate authority pra emitir certificados de curta duração em vez de gerenciar chaves longas.

Se você está montando essa topologia em produção, vale lembrar que toda VPS Hostini já vem com IPv6 nativo e rede privada entre instâncias na mesma região — você pode usar a interface privada como caminho do bastion pras VPS internas, evitando tráfego desnecessário pela internet pública. Veja a página de VPS pra detalhes de rede.

Perguntas frequentes

Qual a diferença entre ProxyJump e ProxyCommand?

ProxyJump (introduzido no OpenSSH 7.3, 2016) é a forma moderna e mais simples de fazer salto via host intermediário. ProxyCommand é mais antigo, exige construir manualmente o comando com `ssh -W %h:%p` e tem mais pontos de falha. Use ProxyJump em qualquer ambiente com OpenSSH ≥ 7.3.

Posso encadear múltiplos saltos com ProxyJump?

Sim. A diretiva `ProxyJump bastion1,bastion2,bastion3` faz salto sequencial por cada host. Útil pra ambientes segmentados em múltiplas zonas (DMZ → app → DB). Cada salto adiciona latência — em prática, 2 saltos é o ponto onde a UX começa a degradar.

ProxyJump funciona com chaves SSH diferentes em cada host?

Sim. Cada bloco `Host` no `~/.ssh/config` declara seu próprio `IdentityFile`. O cliente usa a chave do bastion pra autenticar no salto, depois a chave da VPS interna pra autenticar no destino final. Recomendado: usar agent forwarding desabilitado e chaves separadas por host.

O bastion host vê a senha ou tráfego da minha sessão na VPS interna?

Não. ProxyJump usa o bastion apenas como túnel TCP (modo `-W`). A negociação TLS e a sessão SSH com a VPS interna são end-to-end entre seu cliente e a VPS final. O bastion encaminha bytes criptografados sem capacidade de decifrar.

Como evito digitar a passphrase da chave a cada conexão?

Use `ssh-agent` em conjunto com `AddKeysToAgent yes` no `~/.ssh/config`. A passphrase é pedida na primeira conexão e cacheada na memória do agent até logout. Em macOS, adicione `UseKeychain yes` pra persistir entre sessões via Keychain do sistema.

O ProxyJump quebra se o bastion estiver offline?

Sim — o bastion é ponto único de falha. Em ambientes críticos, configure 2 bastions com IPs diferentes e use `ProxyJump bastion-a,backup-bastion-b` como fallback manual, ou implemente um failover DNS apontando `bastion.exemplo.com` pra IPs redundantes.

Tópicos:
Próximos passos Cloud Ryzen com NVMe e proteção DDoS sempre ativa.Coloque em produção numa VPS Hostini →
Esse tutorial foi útil?
Falar no WhatsApp