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
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.exemplo.com (IP público) 10.0.0.10 (rede privada) 10.0.0.11 (rede privada) 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.
Verifique se o arquivo existe e tem permissões corretas:
mkdir -p ~/.ssh
touch ~/.ssh/config
chmod 700 ~/.ssh
chmod 600 ~/.ssh/configPermissõ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”.
Abra o arquivo no editor de sua preferência:
nano ~/.ssh/configVocê 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.
Adicione o bloco do bastion host:
Host bastion
HostName bastion.exemplo.com
User deploy
Port 22
IdentityFile ~/.ssh/id_ed25519_bastion
IdentitiesOnly yesIdentitiesOnly 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.
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 bastionO 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.
Adicione defaults globais no final do arquivo:
Host *
AddKeysToAgent yes
ServerAliveInterval 60
ServerAliveCountMax 3
HashKnownHosts yesServerAliveInterval 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.
Configure salto duplo:
Host vps-prod
HostName 10.10.0.50
User deploy
ProxyJump bastion,dmz-jumpO 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.
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.
No bastion, edite /etc/ssh/sshd_config:
sudo nano /etc/ssh/sshd_configAplique estas configurações:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers deploy
MaxAuthTries 3
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowTcpForwarding yesAllowTcpForwarding 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.
Recarregue o serviço SSH e teste antes de fechar a sessão atual:
sudo sshd -t && sudo systemctl reload sshsshd -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.
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.
Conecte na VPS interna usando o apelido:
ssh vps-appSe 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.
Confirme que a porta SSH das VPS internas está realmente fechada externamente:
nmap -p 22 10.0.0.10Rodando 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
tlogouauditdno 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.