SSH genom ett NGINX-strömrelä utan att förstöra det
Ett PROXY-huvud är ett meddelande till en mottagare som förväntar sig det, och en proxad DNS-post är inte ett TCP-relä. Fixa kanten och uppströmmen var för sig.
Offentlig SSH genom ett relä misslyckas vid två oberoende gränser, och att fixa den ena avslöjar vanligtvis den andra. Antingen returnerar kanten ett HTTP-svar i stället för att föra en ström, eller så sätter reläet ett huvud framför som backend aldrig har gått med på att läsa. Tre förhållanden avgör vilket du ser: vad den offentliga posten faktiskt pekar på, vad strömblocket skickar uppströms och vilket namn SSH-klienten verifierar. Arbeta igenom dem i den ordningen.
Två gränser, två symtom
Ett fel vid kanten ser ut som en HTTP-statussida som anländer där en SSH-banner borde vara. Anslutningen fullbordas på TCP-nivå och de första byten är text som tillhör en webbserver. Det är ett protokollavvik, inte ett tappat paket. Det berättar också att kanten medvetet betjänar något annat för det namnet.
Ett fel uppströms ser annorlunda ut. Anslutningen når sshd, sshd läser byte som inte är det identitetsutbyte det förväntar sig, och sessionen dör eller hänger utan att någonsin erbjuda en nyckel.
De två är lätta att förväxla eftersom båda ger en klient som ansluter och sedan misslyckas. Det första protokollsvar du tar emot är det som skiljer dem, och därför hör det hemma i ärendet.
PROXY-huvudet är ett meddelande, inte en brytare
I strömmodulen instruerar proxy_protocol on NGINX att före nyttolasten skriva ett PROXY-protokollhuvud mot uppströmmen. Det aktiverar inte TCP-proxy och har ingenting med SSH-kryptering att göra. Det är en anteckning riktad till mottagaren. Om mottagaren inte kan läsa den blir den skräp i början av strömmen.
Den mottagande tjänsten måste medvetet konsumera det huvudet. En normal sshd förväntar sig SSH-identitetsutbytet som de första byten på anslutningen, så en oväntad inledning bryter handskakningen på ett sätt som ser ut som en skadad server.
stream {
server {
listen 2222;
proxy_pass 10.77.0.20:22;
}
}
Det fragmentet är hela ett fungerande relä för en backend som nås över VPN:en. Det visas ingen PROXY-direktiv eftersom mottagaren inte talar protokollet, och den frånvaron är ett beslut, inte en utelämning.
Separera också de två riktningarna. Att sätta proxy_protocol på listen-raden betyder att NGINX förväntar sig att dess egen klient skickar huvudet först; att aktivera det för en vanlig SSH-klient ger precis motsatsen till det problem folk vanligtvis försöker lösa.
Vad det offentliga värdnamnet faktiskt når
En HTTP 400-sida i en SSH-session är bevis om kanten. Något på vägen talar HTTP, och inget nyckel- eller kontorelaterat arbete på backend kommer att ändra det. Sluta ändra backend tills kanten är utredd.
Cloudflares vanliga proxade post bär sin stödda HTTP- och HTTPS-trafik på definierade portar. Att slå på proxyn för ett värdnamn skapar inte ett allmänt TCP-relä, och att flytta SSH till port 443 gör det inte till HTTPS.
Det finns två användbara riktningar. En post med bara DNS kan peka på ditt eget TCP-relä, vilket lägger beslutet om exponering i dina händer. Alternativt kan du använda en produkt som uttryckligen dokumenterar SSH-transport, med sin egen konfiguration för ursprung, klient och åtkomst.
Vad du än väljer behöver reläet fortfarande en VPN-rutt till backend, en tillåten anslutning och en returväg. Rutten är inte valfri bara för att den offentliga sidan fungerar.
Håll SSH-identiteten stabil genom ett alias
Användare vill vanligtvis fortsätta skriva sitt befintliga värdnamn. Ett alias i SSH-konfigurationen kan göra det utan att ändra något på reläet, och det håller ändringen på klientsidan där den hör hemma.
Host git.example.com
HostName ssh-entry.example.net
Port 2222
User git
HostKeyAlias git.example.com
Utan HostKeyAlias slår OpenSSH upp värdnyckeln under det namn den ansluter till, så posten flyttar sig och varje användare ser en varning om ändrad nyckel vid första användningen. Att sätta aliaset håller known-hosts-poster stabila medan anslutningen fortfarande går till reläet.
Detta alias gäller bara för SSH-baserade fjärrarkiv. En HTTPS-Git-URL konsulterar det inte, och den typiska fjärr-URL:en måste matcha den transport du faktiskt har etablerat.
Det du inte får göra är att stänga av värdnyckelverifieringen för att bli av med varningen. Det tar bort ett skydd som vidarebefordringsreparationen aldrig bad om att röra, och det förvandlar en kosmetisk olägenhet till en verklig exponering.
Verifiera varje hopp separat
Testa den offentliga lyssnaren ensam, sedan relä-till-backend-anslutningen från relä värden och först därefter klienten från ände till ände. Var och en av de tre testerna utesluter ett annat lager. Hoppas ett över betyder det att de två återstående resultaten inte kan tolkas. Tre observationer, tre platser där ett fel kan bo.
Behåll destinationsadressen, porten och de första protokollbyten tillsammans med varje resultat. Den trion skiljer ett kantfel från ett VPN-routningsproblem och från ett SSH-autentiseringsfel, och det är skillnaden mellan en reparation och en gissning.
Dokumentera slutligen den avsedda exponeringen. Ett TCP-relä som för SSH till en privat backend är en medveten öppning i den offentliga perimetern, och det förtjänar samma granskning som alla andra inkommande tjänster.