O SSH público através de um relé falha em duas fronteiras não relacionadas, e corrigir uma costuma revelar a outra. Ou a borda devolve uma resposta HTTP em vez de transportar um fluxo, ou o relé antepõe um cabeçalho que o backend nunca aceitou ler. Três condições decidem qual das duas está a ver: para onde o registo público realmente aponta, o que o bloco de fluxo envia para a origem e que nome o cliente SSH verifica. Trate-as por essa ordem.

Duas fronteiras, dois sintomas

Uma falha na borda parece uma página de estado HTTP a chegar onde devia estar um banner SSH. A conexão completa-se ao nível TCP e os primeiros bytes são texto que pertence a um servidor web. É uma incompatibilidade de protocolo, não um pacote perdido. Também lhe diz que a borda serve deliberadamente outra coisa para esse nome.

Uma falha na origem parece diferente. A conexão chega ao sshd, o sshd lê bytes que não são a troca de identificação que espera, e a sessão morre ou fica pendurada sem nunca oferecer uma chave.

As duas confundem-se facilmente porque ambas produzem um cliente que liga e depois falha. A primeira resposta de protocolo que recebe é o que as distingue, e por isso pertence ao ticket.

O cabeçalho PROXY é uma mensagem, não um interruptor

Em o módulo de fluxo, proxy_protocol on instrui o NGINX a escrever um cabeçalho de protocolo PROXY em direção à origem antes da carga útil. Não ativa o proxy TCP e não tem nada a ver com a cifragem SSH. É uma nota dirigida ao recetor. Se o recetor não a conseguir ler, torna-se lixo no início do fluxo.

O serviço recetor tem de consumir esse cabeçalho de forma deliberada. Um sshd normal espera a troca de identificação SSH como primeiros bytes da conexão, por isso um preâmbulo inesperado quebra o aperto de mão de uma forma que parece um servidor corrompido.

stream {
    server {
        listen 2222;
        proxy_pass 10.77.0.20:22;
    }
}

Esse fragmento é todo um relé funcional para um backend alcançável através da VPN. Não aparece qualquer diretiva PROXY porque o recetor não fala o protocolo, e essa ausência é uma decisão, não uma omissão.

Separe também as duas direções. Definir proxy_protocol na linha listen significa que o NGINX espera que o seu próprio cliente envie primeiro o cabeçalho; ativá-lo para um cliente SSH comum produz exatamente o contrário do problema que as pessoas costumam tentar resolver.

O que o nome de anfitrião público alcança realmente

Uma página HTTP 400 numa sessão SSH é evidência sobre a borda. Algo no caminho fala HTTP, e nenhum trabalho com a chave ou a conta no backend o mudará. Pare de alterar o backend até a borda estar resolvida.

O registo com proxy comum da Cloudflare transporta o seu tráfego HTTP e HTTPS suportado em portas definidas. Ativar o proxy para um nome de anfitrião não cria um relé TCP geral, e mover o SSH para a porta 443 não o torna HTTPS.

Existem duas direções viáveis. Um registo apenas DNS pode apontar para o seu próprio relé TCP, o que deixa a decisão sobre a exposição nas suas mãos. Em alternativa, use um produto que documente explicitamente o transporte de SSH, com a sua própria configuração de origem, cliente e acesso.

Escolha o que escolher, o relé continua a precisar de uma rota VPN até ao backend, de uma ligação permitida e de um caminho de regresso. A rota não é opcional porque o lado público funciona.

Mantenha a identidade SSH estável através de um alias

Os utilizadores costumam querer continuar a escrever o nome de anfitrião existente. Um alias na configuração do SSH pode fazê-lo sem alterar nada no relé, e mantém a alteração do lado do cliente, onde lhe compete.

Host git.example.com
    HostName ssh-entry.example.net
    Port 2222
    User git
    HostKeyAlias git.example.com

Sem HostKeyAlias, o OpenSSH procura a chave do anfitrião sob o nome a que se liga, por isso a entrada desloca-se e cada utilizador vê um aviso de chave alterada no primeiro uso. Definir o alias mantém as entradas de known-hosts estáveis enquanto a ligação continua a ir para o relé.

Este alias aplica-se apenas a remotos baseados em SSH. Um URL Git por HTTPS não o consulta, e o URL típico do remoto tem de corresponder ao transporte que realmente aprovisionou.

O que não deve fazer é desativar a verificação da chave do anfitrião para fazer desaparecer o aviso. Isso remove uma proteção que a reparação do encaminhamento nunca pediu para tocar, e transforma um incómodo cosmético numa exposição real.

Verifique cada salto separadamente

Teste o ouvinte público isoladamente, depois a ligação do relé ao backend a partir do anfitrião do relé, e só então o cliente de ponta a ponta. Cada um desses três testes elimina uma camada diferente. Saltar um significa que os dois resultados restantes não podem ser interpretados. Três observações, três lugares onde uma falha pode viver.

Guarde o endereço de destino, a porta e os primeiros bytes de protocolo com cada resultado. Esse trio distingue um erro de borda de um problema de encaminhamento VPN e de uma falha de autenticação SSH, e é a diferença entre uma reparação e um palpite.

Por fim, documente a exposição pretendida. Um relé TCP que transporta SSH para um backend privado é uma abertura deliberada no perímetro público, e merece a mesma revisão que qualquer outro serviço de entrada.

Artigos relacionados