Gibt es einen namensbasierten SSH-Reverse-Proxy für virtuelle Hosts?

36

Ich habe HTTP-Reverse-Proxys in unserer Entwicklungsumgebung sehr gern und fand den DNS-basierten Reverse-Proxy für virtuelle Hosts sehr nützlich. Wenn nur ein Port (und der Standardport) in der Firewall geöffnet ist, erleichtert dies die Verwaltung erheblich.

Ich würde gerne etwas Ähnliches finden, um SSH-Verbindungen herzustellen, habe aber nicht viel Glück gehabt. Ich würde es vorziehen, nicht einfach SSH-Tunneling zu verwenden, da dies andere Portbereiche als den Standard erfordert. Gibt es irgendetwas, das das kann?

Könnte HAProxy dies tun?

Ahanson
quelle
Handelt es sich um die beabsichtigte Übertragung von Anwendungsdateien oder um den tatsächlichen SSH-Zugriff auf Hosts?
Kyle Hodgson
Der direkte SSH-Zugriff auf den Host ist das Ziel. Diese Notwendigkeit ergibt sich aus dem Wunsch, einen Mercurial-Server intern zu betreiben, aber unser Server befindet sich hinter der Firewall. Im Moment arbeite ich daran, einfach eine HTTP-Version einzurichten, aber ich wollte, dass Commits SSH anstelle von HTTP verwenden. Ein direkter SSH-Zugriff auf andere Server wäre ein Bonus, wenn dies möglich wäre.
Ahanson
Das ist so ein nerviges Problem.
Bias
Meines Wissens nach unterstützt HAProxy dies jetzt über SNI. Obwohl ich das noch nicht in Gang bringen konnte.
Carel,

Antworten:

24

Ich glaube nicht, dass namenbasiertes SSH möglich sein wird, wenn man bedenkt, wie das Protokoll funktioniert.

Hier sind ein paar alternativen.

  • Sie können den Host, der für Port 22 antwortet, als Gateway einrichten. Anschließend können Sie den SSH-Server so konfigurieren, dass Anforderungen basierend auf dem Schlüssel an das Innere weitergeleitet werden. SSH Gateway Beispiel mit Schlüsseln

  • Sie können Ihren Client so einstellen, dass er diesen Host als Proxy verwendet. Das heißt, es wird ssh an den Gateway-Host gesendet und dieser Host verwendet, um eine Verbindung zum internen Host herzustellen. SSH-Proxy mit Client- Konfiguration .

  • Sie können auch einen einfachen HTTP-Proxy am Rand einrichten. Verwenden Sie dies dann, um eingehende Verbindungen zuzulassen. SSH über HTTP-Proxy .

Bei alledem ist es natürlich sehr wichtig, dass Sie das Gateway ordnungsgemäß konfigurieren und sperren.

Zoredache
quelle
18

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_IGNORENachricht, 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.1und 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 HostHeader 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.
Kasperd
quelle
Können Sie the gateway can use the destination IP address to find out which server to send the packet toin der IPv6-Lösung erläutern, was Sie damit meinen ?
Jacob Ford
@JacobFord Der Schlüssel zum Verständnis dieses Satzes ist, dass ich das Wort " Gateway" und nicht " Proxy" verwende . Mit IPv6 erhalten Sie genug IP-Adressen, um jedem Host eine zuzuweisen, und Sie haben immer noch Adressen zu vergeben. Alle Computer, die Sie hinter den Proxy stellen würden, hätten eine eigene IP-Adresse, in die Sie die Namen auflösen würden. Damit Sie keinen Proxy mehr benötigen, senden die Clients ihren Datenverkehr an die IP-Adresse des gewünschten Hosts und Sie können den Datenverkehr direkt dorthin leiten.
Kasperd
Für den Workaround-Teil gibt es eine relativ neue -JBefehlsoption für ssh . Sie können also Folgendes verwenden: ssh -J user1 @ front user2 @ target
PMN
3

Ich glaube nicht, dass dies möglich wäre, zumindest nicht so, wie Sie es beschrieben haben, obwohl ich es lieben würde, wenn ich mich als falsch erweisen würde. Es scheint nicht, dass der Client den Hostnamen sendet, zu dem er eine Verbindung herstellen möchte (zumindest nicht im Klartext). Der erste Schritt der SSH-Verbindung scheint darin zu bestehen, die Verschlüsselung einzurichten.

Außerdem würden Sie Probleme mit der Überprüfung des Hostschlüssels haben. SSH-Clients überprüfen Schlüssel basierend auf einer IP-Adresse sowie einem Hostnamen. Sie hätten mehrere Hostnamen mit unterschiedlichen Schlüsseln, aber die gleiche IP, mit der Sie sich verbinden.

Eine mögliche Lösung wäre ein "Bastion" -Host, auf dem Clients auf diesen Rechner zugreifen, eine normale (oder auf Wunsch eingeschränkte) Shell erhalten und von dort aus in interne Hosts sshen können.

Kennzeichen
quelle
Das Bastion-Konzept ist das, was wir derzeit eingerichtet haben, aber für meine Versionskontrolle problematisch (ohne zusätzlichen Aufwand).
Ahanson
Es ist sehr ärgerlich, dass ssh den fqdn nicht sendet und den DNS auf der Client-Seite verwaltet. Ich denke, die Leute machten sich keine Gedanken über öffentliche IP-Probleme und NAT, als die meisten Protokolle auf TCP / IP-Anwendungsebene erstellt wurden. Im Ernst, Sie sollten in der Lage sein, FQDN-basiertes NAT mit iptables (dh Kernel-Filtern) durchzuführen.
Bias
Der Hostschlüssel könnte der Schlüssel des Reverse-Proxys sein. Dies ist ein Vorteil, wenn Sie der Sicherheit des Backends vertrauen, da Sie die Kontrolle über das interne Netzwerk und die Hosts haben.
Rox0r
@bias Sie können NAT nicht basierend auf dem Hostnamen ausführen, auch wenn das übergeordnete Protokoll den Hostnamen sendet. Der Grund, warum dies nicht möglich ist, ist, dass die Auswahl des Backends beim Empfang des SYN-Pakets erfolgen muss, der Hostname jedoch erst gesendet wird, nachdem das SYN verarbeitet und ein SYN-ACK zurückgegeben wurde. Sie können jedoch einen sehr dünnen Proxy auf Anwendungsebene für Protokolle verwenden, die Hostnamen senden, z. B. http und https.
Kasperd
3

Es ist ein neues Kind auf dem Block. SSH piper leitet Ihre Verbindung basierend auf vordefinierten Benutzernamen weiter, die den inneren Hosts zugeordnet werden sollten. Dies ist die beste Lösung im Zusammenhang mit Reverse-Proxy.

SSh Piper auf Github

Meine ersten Tests haben bestätigt, dass SSH und SFTP funktionieren.

Király István
quelle
Das ist quasi ein MITM-Angriff. Ich erwarte, dass es in jedem Setup, das gegen MITM-Angriffe geschützt ist, kaputt geht.
Kasperd
1
Tatsächlich ist die Hosting-Partei sowohl der Host als auch der Mann in der Mitte, daher sind nur diese beiden involviert.
Király István
@ KirályIstván das ist ein tolles Projekt und beweist, dass andere Antworten nicht 100% richtig sind. Ich habe ein ähnliches Tool basierend auf github.com/gliderlabs/sshfront und etwas Bash / Python erstellt (ich kann es nicht öffnen, aber es ist nicht so interessant - Bash führt den SSH-Client aus und Python wird als Auth-Handler verwendet). Aber ich habe ein Problem mit der aktuellen Lösung. Sftp wird nicht unterstützt (scp funktioniert). Es hängt beim Versuch, das Subsystem auszuführen debug1: Sending subsystem: sftp. Es stellt sich heraus, dass die Shirt-Front keine Subsysteme unterstützt. Unterstützen Sie Saum in SSh Piper?
Maciek Sawicki
1
@MaciekSawicki Ich bin nicht der Autor. Ich benutze es nur in meinem Projekt. .)
Király István
2

Ich frage mich, ob Honeytrap (der Honeypot mit geringer Interaktion), der über einen Proxy-Modus verfügt, nicht geändert werden kann, um dies zu erreichen.

Dieser Honeypot kann jedes Protokoll an einen anderen Computer weiterleiten. Das Hinzufügen eines namensbasierten vhost-Systems (wie von Apache implementiert) könnte es zu einem perfekten Reverse-Proxy für jedes Protokoll machen, nicht wahr?

Ich habe nicht die Fähigkeiten, das zu erreichen, aber vielleicht könnte es ein großartiges Projekt sein.

Kafeine
quelle
exzellente Idee!
Bias
1
Die vhost-Funktion in Apache setzt voraus, dass der Client den Hostnamen sendet, bevor Daten vom Server gesendet wurden. Das SSH-Protokoll enthält keinen solchen Hostnamen, daher sehe ich nicht, wie Sie überhaupt mit der Implementierung einer solchen Funktion beginnen würden. Aber wenn Sie Ihre Ideen ausarbeiten können, würde ich gerne einen Proof of Concept implementieren oder Ihnen sagen, warum es nicht funktioniert.
Kasperd
1

Mit zunehmender Anzahl der Ports / Hosts, auf die Sie hinter einer Firewall zugreifen möchten, steigt der Komfort von VPNs.

VPNs mag ich allerdings nicht.

Alex
quelle
1

wegen der arbeitsweise von ssh denke ich ist das nicht möglich. Ähnlich wie bei https müssten Sie unterschiedliche (externe) IP-Adressen für verschiedene Hosts haben, da das Gateway nicht weiß, wo Sie eine Verbindung herstellen möchten, da alles in ssh verschlüsselt ist

Jure1873
quelle