Ich habe in den letzten 16 Monaten immer wieder nach einer Lösung für dieses Problem gesucht. Aber jedes Mal, wenn ich nachschaue, scheint es unmöglich zu sein, dies mit dem SSH-Protokoll zu tun, das in relevanten RFCs spezifiziert und von größeren Implementierungen implementiert ist.
Wenn Sie jedoch bereit sind, einen leicht modifizierten SSH-Client zu verwenden, und wenn Sie bereit sind, Protokolle auf eine Weise zu verwenden, die nicht genau beabsichtigt war, als sie entworfen wurden, ist dies möglich. Mehr dazu weiter unten.
Warum ist es nicht möglich
Der Client sendet den Hostnamen nicht als Teil des SSH-Protokolls.
Möglicherweise wird der Hostname als Teil einer DNS-Suche gesendet, aber diese wird möglicherweise zwischengespeichert, und der Pfad vom Client über Resolver zu autorisierenden Servern führt möglicherweise nicht über den Proxy, und selbst wenn dies der Fall ist, gibt es keine zuverlässige Möglichkeit, bestimmte DNS-Suchen zuzuordnen bestimmte SSH-Clients.
Mit dem SSH-Protokoll selbst können Sie nichts Besonderes anfangen. Sie müssen einen Server auswählen, ohne das SSH-Versionsbanner vom Client gesehen zu haben. Sie müssen ein Banner an den Client senden, bevor etwas an den Proxy gesendet wird. Die Banner von den Servern können unterschiedlich sein, und Sie können nicht erraten, welches das richtige ist.
Auch wenn dieses Banner unverschlüsselt gesendet wird, können Sie es nicht ändern. Jedes Bit dieses Banners wird während des Verbindungsaufbaus überprüft, so dass Sie ein wenig später einen Verbindungsfehler verursachen würden.
Die Schlussfolgerung für mich ist ziemlich klar, man muss etwas auf der Client-Seite ändern, damit diese Konnektivität funktioniert.
Die meisten Problemumgehungen kapseln den SSH-Verkehr in einem anderen Protokoll. Man kann sich auch eine Ergänzung des SSH-Protokolls vorstellen, bei der das vom Client gesendete Versionsbanner den Hostnamen enthält. Dies kann mit vorhandenen Servern kompatibel bleiben, da ein Teil des Banners derzeit als Freiform-Identifikationsfeld angegeben ist. Obwohl Clients in der Regel auf das Versionsbanner vom Server warten, bevor sie ihr eigenes senden, gestattet das Protokoll dem Client, ihr Banner zu senden zuerst. Einige neuere Versionen des ssh-Clients (z. B. unter Ubuntu 14.04) senden das Banner, ohne auf das Server-Banner zu warten.
Ich kenne keinen Client, der Schritte unternommen hat, um den Hostnamen des Servers in dieses Banner aufzunehmen. Ich habe einen Patch an die OpenSSH-Mailingliste gesendet , um eine solche Funktion hinzuzufügen. Es wurde jedoch aufgrund des Wunsches abgelehnt, den Hostnamen niemandem preiszugeben, der den SSH-Verkehr untersucht. Da ein geheimer Hostname grundsätzlich nicht mit dem Betrieb eines namensbasierten Proxys kompatibel ist, sollten Sie nicht erwarten, bald eine offizielle SNI-Erweiterung für das SSH-Protokoll zu erhalten.
Eine echte Lösung
Die Lösung, die für mich am besten funktionierte, war eigentlich die Verwendung von IPv6.
Mit IPv6 kann jedem Server eine separate IP-Adresse zugewiesen werden, sodass das Gateway anhand der Ziel-IP-Adresse herausfinden kann, an welchen Server das Paket gesendet werden soll. Die SSH-Clients können manchmal in Netzwerken ausgeführt werden, in denen die einzige Möglichkeit zum Abrufen einer IPv6-Adresse die Verwendung von Teredo ist. Teredo ist als unzuverlässig bekannt, jedoch nur, wenn das native IPv6-Ende der Verbindung ein öffentliches Teredo-Relay verwendet. Man kann einfach ein Teredo-Relay auf das Gateway setzen, auf dem Sie den Proxy ausführen würden. Miredo kann in weniger als fünf Minuten als Relais installiert und konfiguriert werden.
Ein Workaround
Sie können einen Sprunghost / Bastionshost verwenden. Dieser Ansatz ist für Fälle gedacht, in denen Sie den SSH-Port der einzelnen Server nicht direkt dem öffentlichen Internet aussetzen möchten. Es hat den zusätzlichen Vorteil, dass die Anzahl der für SSH benötigten extern zugewandten IP-Adressen reduziert wird, weshalb es in diesem Szenario verwendet werden kann. Die Tatsache, dass es sich um eine Lösung handelt, die aus Sicherheitsgründen eine weitere Schutzschicht hinzufügen soll, hindert Sie nicht daran, sie zu verwenden, wenn Sie die zusätzliche Sicherheit nicht benötigen.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Ein schmutziger Hack, damit es funktioniert, wenn die eigentliche Lösung (IPv6) außerhalb Ihrer Reichweite liegt
Der Hack, den ich beschreiben möchte, sollte nur als das absolut letzte Mittel verwendet werden. Bevor Sie überhaupt über diesen Hack nachdenken, empfehle ich dringend, für jeden Server, den Sie über SSH extern erreichen möchten, eine IPv6-Adresse zu erhalten. Verwenden Sie IPv6 als primäre Methode für den Zugriff auf Ihre SSH-Server und verwenden Sie diesen Hack nur, wenn Sie einen SSH-Client über ein IPv4-Netzwerk ausführen müssen, auf dessen Bereitstellung Sie keinen Einfluss haben.
Die Idee ist, dass der Verkehr zwischen Client und Server ein einwandfrei gültiger SSH-Verkehr sein muss. Der Proxy muss jedoch nur genug über den Paketstrom wissen, um den Hostnamen zu identifizieren. Da SSH keine Möglichkeit zum Senden eines Hostnamens definiert, können Sie stattdessen andere Protokolle in Betracht ziehen, die eine solche Möglichkeit bieten.
HTTP und HTTPS ermöglichen es dem Client, einen Hostnamen zu senden, bevor der Server Daten gesendet hat. Die Frage ist nun, ob es möglich ist, einen Strom von Bytes aufzubauen, der gleichzeitig als SSH-Verkehr und als HTTP und HTTPS gültig ist. HTTPs ist so ziemlich ein Nichtstarter, aber HTTP ist möglich (für ausreichend liberale Definitionen von HTTP).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
Sieht das für Sie nach SSH oder HTTP aus? Es ist SSH und vollständig RFC-konform (mit der Ausnahme, dass einige der Binärzeichen durch das SF-Rendering ein wenig entstellt wurden).
Die SSH-Versionszeichenfolge enthält ein Kommentarfeld, das oben den Wert hat / HTTP/1.1
. Nach dem Zeilenwechsel hat SSH einige binäre Paketdaten. Das erste Paket ist eine MSG_SSH_IGNORE
Nachricht, die vom Client gesendet und vom Server ignoriert wird. Die zu ignorierende Nutzlast ist:
:
Host: example.com
Wenn ein HTTP-Proxy ausreichend liberal ist, was er akzeptiert, wird dieselbe Folge von Bytes als aufgerufene HTTP-Methode interpretiert, SSH-2.0-OpenSSH_6.6.1
und die Binärdaten am Anfang der Ignorierungsnachricht werden als HTTP-Headername interpretiert.
Der Proxy würde weder die HTTP-Methode noch den ersten Header verstehen. Aber es könnte den Host
Header verstehen , der alles ist, was es braucht, um das Backend zu finden.
Damit dies funktioniert, muss der Proxy nach dem Prinzip entworfen werden, dass er nur genügend HTTP verstehen muss, um das Backend zu finden, und sobald das Backend gefunden wurde, leitet der Proxy einfach den Raw-Byte-Stream weiter und verlässt die reale Terminierung der vom Backend auszuführenden HTTP-Verbindung.
Es mag ein bisschen schwierig klingen, so viele Annahmen über den HTTP-Proxy zu machen. Wenn Sie jedoch bereit sind, eine neue Software zu installieren, die mit der Absicht entwickelt wurde, SSH zu unterstützen, dann klingen die Anforderungen für den HTTP-Proxy nicht allzu schlecht.
In meinem Fall funktionierte diese Methode auf einem bereits installierten Proxy ohne Änderungen an Code, Konfiguration oder anderem. Und dies war für einen Proxy, der nur mit HTTP und ohne SSH geschrieben wurde.
Proof-of-Concept- Client und Proxy . (Haftungsausschluss, dass es sich bei Proxy um einen von mir betriebenen Dienst handelt. Sie können den Link jederzeit ersetzen, sobald ein anderer Proxy bestätigt wurde, der diese Verwendung unterstützt.)
Vorsichtsmaßnahmen für diesen Hack
- Benutze es nicht. Verwenden Sie besser die echte Lösung, IPv6.
- Wenn der Proxy versucht, den HTTP-Datenverkehr zu verstehen, wird er mit Sicherheit unterbrochen.
- Sich auf einen modifizierten SSH-Client zu verlassen, ist nicht schön.