Eu tenho procurado uma solução para este problema dentro e fora nos últimos 16 meses. Mas, cada vez que olho, parece impossível fazer isso com o protocolo SSH, conforme especificado nas RFCs relevantes e implementado pelas principais implementações.
No entanto, se você estiver disposto a usar um cliente SSH levemente modificado e estiver disposto a utilizar protocolos de maneira que não foi exatamente planejada quando eles foram projetados, é possível obter isso. Mais sobre isso abaixo.
Por que não é possível
O cliente não envia o nome do host como parte do protocolo SSH.
Ele pode enviar o nome do host como parte de uma pesquisa de DNS, mas isso pode ser armazenado em cache, e o caminho do cliente através dos resolvedores para servidores autoritativos pode não cruzar o proxy e, mesmo que isso ocorra, não existe uma maneira robusta de associar pesquisas de DNS específicas a clientes SSH específicos.
Também não há nada que você possa fazer com o próprio protocolo SSH. Você precisa escolher um servidor sem ter visto o banner da versão SSH do cliente. Você precisa enviar um banner para o cliente, antes que ele envie qualquer coisa para o proxy. Os banners dos servidores podem ser diferentes e você não tem chance de adivinhar qual é o correto para usar.
Mesmo que este banner seja enviado sem criptografia, você não pode modificá-lo. Cada parte desse banner será verificada durante a configuração da conexão; portanto, você estará causando uma falha na conexão um pouco abaixo da linha.
A conclusão para mim é bastante clara: é preciso mudar algo no lado do cliente para fazer essa conectividade funcionar.
A maioria das soluções alternativas está encapsulando o tráfego SSH dentro de um protocolo diferente. Pode-se também imaginar uma adição ao próprio protocolo SSH, no qual o banner da versão enviado pelo cliente inclui o nome do host. Isso pode permanecer compatível com os servidores existentes, já que parte do banner está atualmente especificada como um campo de identificação de formulário livre e, embora os clientes normalmente esperem o banner de versão do servidor antes de enviar o seu próprio, o protocolo permite que o cliente envie seu banner primeiro. Algumas versões recentes do cliente ssh (por exemplo, no Ubuntu 14.04) enviam o banner sem aguardar o banner do servidor.
Não conheço nenhum cliente que tenha tomado medidas para incluir o nome do host do servidor nesse banner. Enviei um patch para a lista de discussão do OpenSSH para adicionar esse recurso. Mas foi rejeitado com base no desejo de não revelar o nome do host a ninguém bisbilhotando no tráfego SSH. Como um nome de host secreto é fundamentalmente incompatível com a operação de um proxy baseado em nome, não espere ver uma extensão SNI oficial para o protocolo SSH tão cedo.
Uma solução real
A solução que melhor funcionou para mim foi usar o IPv6.
Com o IPv6, posso ter um endereço IP separado atribuído a cada servidor, para que o gateway possa usar o endereço IP de destino para descobrir para qual servidor enviar o pacote. Às vezes, os clientes SSH podem estar em execução nas redes em que a única maneira de obter um endereço IPv6 seria usando o Teredo. Sabe-se que o Teredo não é confiável, mas apenas quando o final do IPv6 nativo da conexão está usando uma retransmissão pública do Teredo. Pode-se simplesmente colocar um relé Teredo no gateway, onde você executaria o proxy. O Miredo pode ser instalado e configurado como um relé em menos de cinco minutos.
Uma solução alternativa
Você pode usar um host de salto / host de bastião. Essa abordagem é direcionada para casos em que você não deseja expor a porta SSH dos servidores individuais diretamente à Internet pública. Ele tem o benefício adicional de reduzir o número de endereços IP externos que você precisa para SSH, e é por isso que é utilizável nesse cenário. O fato de ser uma solução destinada a adicionar outra camada de proteção por motivos de segurança não impede que você a use quando não precisar da segurança adicionada.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Um truque sujo para fazê-lo funcionar se a solução real (IPv6) estiver fora do seu alcance
O truque que estou prestes a descrever deve ser usado apenas como o último recurso. Antes mesmo de pensar em usar esse hack, recomendo fortemente obter um endereço IPv6 para cada um dos servidores que você deseja alcançar externamente através do SSH. Use o IPv6 como método principal para acessar seus servidores SSH e use esse hack apenas quando precisar executar um cliente SSH a partir de uma rede somente IPv4, onde você não tem nenhuma influência sobre a implantação do IPv6.
A ideia é que o tráfego entre o cliente e o servidor precise ser um tráfego SSH perfeitamente válido. Mas o proxy só precisa entender o suficiente sobre o fluxo de pacotes para identificar o nome do host. Como o SSH não define uma maneira de enviar um nome de host, você pode considerar outros protocolos que oferecem essa possibilidade.
HTTP e HTTPS permitem que o cliente envie um nome de host antes que o servidor envie quaisquer dados. A questão agora é se é possível construir um fluxo de bytes que seja simultaneamente válido como tráfego SSH e como HTTP e HTTPS. HTTPs é praticamente um não iniciador, mas HTTP é possível (para definições suficientemente liberais de HTTP).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
Isso parece SSH ou HTTP para você? É SSH e completamente compatível com RFC (exceto que alguns dos caracteres binários foram distorcidos um pouco pela renderização do SF).
A cadeia de caracteres da versão SSH inclui um campo de comentário, que acima tem o valor / HTTP/1.1
. Após a nova linha, o SSH possui alguns dados binários do pacote. O primeiro pacote é uma MSG_SSH_IGNORE
mensagem enviada pelo cliente e ignorada pelo servidor. A carga útil a ser ignorada é:
:
Host: example.com
Se um proxy HTTP for suficientemente liberal no que aceita, a mesma sequência de bytes será interpretada como um método HTTP chamado SSH-2.0-OpenSSH_6.6.1
e os dados binários no início da mensagem de ignorar serão interpretados como um nome de cabeçalho HTTP.
O proxy não entenderia nem o método HTTP nem o primeiro cabeçalho. Mas ele pode entender o Host
cabeçalho, que é tudo o que precisa para encontrar o back-end.
Para que isso funcione, o proxy teria que ser projetado com o princípio de que ele precisa entender apenas HTTP suficiente para encontrar o back-end e, uma vez encontrado o back-end, o proxy simplesmente passará o fluxo de bytes brutos e deixará a real terminação. da conexão HTTP a ser feita pelo back-end.
Pode parecer um pouco exagerado fazer tantas suposições sobre o proxy HTTP. Mas se você estava disposto a instalar um novo software desenvolvido com a intenção de oferecer suporte ao SSH, os requisitos para o proxy HTTP não parecem muito ruins.
No meu próprio caso, achei que esse método funcionava em um proxy já instalado, sem alterações no código, na configuração ou de outra forma. E isso foi para um proxy escrito com apenas HTTP e sem SSH em mente.
Prova de conceito cliente e proxy . (Isenção de responsabilidade de que o proxy é um serviço operado por mim. Sinta-se à vontade para substituir o link depois que qualquer outro proxy for confirmado para suportar esse uso.)
Advertências deste hack
- Não use. É melhor usar a solução real, que é o IPv6.
- Se o proxy tentar entender o tráfego HTTP, ele certamente será interrompido.
- Confiar em um cliente SSH modificado não é bom.