Wie erreichen Sie eine Datenbank, die nur auf localhost lauscht?
Eine Postgres-Instanz auf einem entfernten Server ist absichtlich an 127.0.0.1 gebunden. Nichts im Netzwerk kann sie erreichen, nur Prozesse, die auf genau dieser Maschine laufen. Ihr Laptop ist nicht diese Maschine, also scheitert psql -h remote-server -p 5432, bevor es überhaupt zur Authentifizierung kommt. Den Port nach außen zu öffnen, hebt genau den Grund auf, aus dem er an Loopback gebunden wurde.
Was Sie eigentlich wollen: sich für ein paar Minuten die Sicht der entfernten Maschine auf 127.0.0.1 ausleihen, von Ihrem eigenen Terminal aus. Sobald Sie sich einloggen, hat SSH schon eine verschlüsselte, authentifizierte Verbindung zwischen den beiden Maschinen offen. Port-Forwarding nutzt diese Verbindung, um anderen Traffic als den Ihrer Shell zu transportieren.
Wie funktioniert SSH-Port-Forwarding unter der Haube?
Eine normale SSH-Sitzung wirkt wie eine einzige Röhre: Tastenanschläge gehen rein, Terminal-Ausgabe kommt zurück. Darunter multiplext das Protokoll mehrere unabhängige Kanäle über diese eine verschlüsselte Verbindung. Die Shell ist ein Kanal. Eine weitergeleitete TCP-Verbindung ist ein anderer.
Wenn Sie ssh bitten, einen Port weiterzuleiten, öffnet es für jede Verbindung zu diesem Port einen neuen Kanal, transportiert die rohen Bytes durch die bestehende Sitzung und übergibt sie auf der anderen Seite. Kein neues Zertifikat, kein zweiter Login. Der Tunnel erbt genau den Zugriff, den der SSH-Login bereits hat.
Behalten Sie das im Kopf, bevor Sie sich die Flags ansehen. -L, -R und -D sind keine drei verschiedenen Mechanismen. Sie lenken denselben Channel-Forwarding-Mechanismus nur in unterschiedliche Richtungen.
Wie nutzen Sie lokales SSH-Port-Forwarding (-L)?
| |
Lesen Sie das Argument von -L als local_port:destination_host:destination_port. Das öffnet Port 5432 auf Ihrem Laptop. Alles, was sich damit verbindet, wird durch den Tunnel zu localhost:5432 weitergeleitet, so wie es von db-host aus aussieht, also die Postgres-Instanz, die an ihr eigenes Loopback gebunden ist. In einem zweiten Terminal:
| |
psql denkt, es spricht mit einer lokalen Datenbank. Tatsächlich spricht es über SSH mit der Loopback-Schnittstelle von db-host.
Der Zielhost in der Mitte muss nicht der SSH-Server selbst sein:
| |
Das leitet über jumpbox zu einer dritten Maschine weiter, die jumpbox erreichen kann, Sie aber nicht. Das ist die Form, die die meisten meinen, wenn sie „SSH-Tunnel" sagen: eine Maschine mit dem Zugriff, der Ihnen fehlt, die in ein Netzwerk weiterleitet, das Sie sonst nicht anfassen könnten. Läuft bei Ihnen bereits lokal ein Postgres, ist Port 5432 auf Ihrem Laptop belegt; wählen Sie einen anderen lokalen Port wie 15432 und geben Sie -p 15432 an psql.
Wie geben Sie einen lokalen Port mit SSH-Remote-Forwarding frei (-R)?
-R macht dasselbe rückwärts. Die Syntax ist remote_port:destination_host:destination_port, aber der Listener öffnet sich auf der entfernten Seite.
| |
public-host lauscht jetzt auf Port 8080, und jede Verbindung dorthin kommt durch den Tunnel zurück zu localhost:3000 auf Ihrer Maschine: Ihrem lokalen Dev-Server. Sie bekommen eine erreichbare Adresse für einen Webhook-Anbieter oder einen Kollegen, ohne einen Port in Ihrer eigenen Firewall zu öffnen, indem Sie sich eine Maschine ausleihen, die bereits eine hat.
Ein Standardverhalten bringt viele durcheinander. sshd bindet remote-weitergeleitete Ports an die Loopback-Schnittstelle des entfernten Hosts, also scheitert curl public-host:8080 von einer dritten Maschine aus, selbst wenn die Weiterleitung läuft. Nur Prozesse auf public-host selbst können sie erreichen. Das zu ändern erfordert GatewayPorts yes in der sshd_config dieses Servers, was der Administrator des Servers kontrolliert. Kein Flag an Ihrem ssh-Befehl hebt das auf.
Wie machen Sie aus einem SSH-Tunnel einen SOCKS-Proxy (-D)?
-L und -R leiten jeweils zu einem festen Ziel weiter, das beim Ausführen des Befehls entschieden wird. -D wählt gar kein Ziel:
| |
Das öffnet einen SOCKS-Proxy auf localhost:1080. Jede Anwendung, die darauf zeigt, wählt das Ziel für jede Verbindung selbst, und ssh leitet sie von der Position von remote-host im Netzwerk aus weiter:
| |
Der Teil -hostname zählt. Mit dem einfachen --socks5 löst curl internal-service.corp auf Ihrem Laptop auf, wo dieser Name nicht existiert, und scheitert, bevor der Proxy überhaupt zum Einsatz kommt. --socks5-hostname schickt den Namen durch den Tunnel, damit remote-host ihn auflöst.
Browser funktionieren genauso, sobald Sie den Proxy in ihren Netzwerkeinstellungen setzen (und in Firefox „proxy DNS when using SOCKS v5" aktivieren). Jede Website sieht dann die IP von remote-host und unterliegt dessen Firewall-Regeln. So erreichen Sie ein ganzes internes Netzwerk über eine einzige Jumpbox oder prüfen, was eine Website einer anderen Region ausliefert, ohne für jedes Ziel eine eigene Weiterleitung zu schreiben.
Welchen SSH-Forwarding-Typ sollten Sie wählen: -L, -R oder -D?
-L lokal | -R remote | -D dynamisch | |
|---|---|---|---|
| Listener öffnet sich auf | Ihrer Maschine | Dem entfernten Host | Ihrer Maschine |
| Ziel | Fest, auf der Kommandozeile gesetzt | Fest, auf der Kommandozeile gesetzt | Pro Verbindung von der App gewählt |
| Typischer Einsatz | Einen Dienst erreichen, den nur der entfernte Host sieht | Ihren lokalen Dienst der Gegenseite freigeben | Beliebigen Traffic durch das Netzwerk des entfernten Hosts routen |
| Braucht am anderen Ende | Nichts außer einem normalen SSH-Login | GatewayPorts, wenn andere ihn erreichen müssen | Nichts außer einem normalen SSH-Login |
Wie halten Sie einen SSH-Tunnel im Hintergrund offen?
Zwei Probleme zeigen sich schnell: der vollständige Befehl, den Sie jedes Mal neu eintippen, und der Tunnel, der stirbt, sobald das Terminal schließt. ~/.ssh/config löst das erste:
| |
ssh db-tunnel öffnet jetzt beide Weiterleitungen auf einmal. LocalForward, RemoteForward und DynamicForward entsprechen -L, -R und -D, eine Zeile pro Weiterleitung, so viele wie Sie in einem Host-Block brauchen.
Für eine Weiterleitung, die unbeaufsichtigt laufen soll, fügen Sie -N hinzu (keinen entfernten Befehl ausführen, nur den Tunnel halten) und -f (nach der Authentifizierung in den Hintergrund gehen), plus ein Keepalive, damit eine still abgebrochene Verbindung bemerkt wird, statt hängen zu bleiben:
| |
Das verbindet sich trotzdem nicht neu, wenn das Netzwerk ausfällt. ssh beendet sich, und die Weiterleitung ist weg. autossh umhüllt denselben Befehl und startet ihn neu, wenn die Verbindung stirbt:
| |
-M 0 schaltet den eigenen Monitoring-Port von autossh ab und lässt das ServerAlive*-Keepalive eine tote Verbindung erkennen. Auf modernem OpenSSH ist das die Konfiguration, die Sie verwenden sollten.
Wann sollten Sie SSH-Tunnel nicht verwenden?
Ein weitergeleiteter Port erbt den Zugriff des SSH-Logins, der ihn geöffnet hat. Praktisch, solange Sie den Schlüssel in der Hand haben, und eine echte Angriffsfläche, wenn Schlüssel oder Sitzung kompromittiert werden, weil der Tunnel alles preisgibt, was dieser Login erreichen kann. AllowTcpForwarding in sshd_config ist der serverseitige Schalter (Standard yes). Ihn auf no zu setzen, schließt diesen Weg, aber jeder mit Shell-Zugriff auf der Maschine kann trotzdem seinen eigenen Forwarder starten — es hilft also nur, wenn Sie auch den Shell-Zugriff einschränken.
Tunnel skalieren zudem nicht über eine Handvoll Personen oder Dienste hinaus. Jeder ist ein Prozess, gebunden an ein Terminal oder eine autossh-Instanz. Es gibt keinen zentralen Ort, um zu sehen, wer einen Tunnel offen hat, Zugriff zu widerrufen, ohne jede Client-Konfiguration anzufassen, oder eine einzige Richtlinie über alle Nutzer hinweg durchzusetzen. An diesem Punkt sollte ein VPN oder ein Identity-Aware-Proxy vor dem Dienst den Haufen persönlicher Tunnel ersetzen.
Sie sind auch nicht dasselbe Werkzeug wie ein Reverse Proxy oder Cloudflare Tunnel, auch wenn alle drei etwas erreichbar machen, das es vorher nicht war. Ein Reverse Proxy steht dauerhaft vor einem Dienst und ist dem öffentlichen Internet zugewandt. Cloudflare Tunnel ist näher an -R: eine ausgehende Verbindung von Ihrer Seite, die von außen erreichbar wird, aber über Cloudflares Edge, mit stabilem Hostnamen und ohne Serverport, den Sie verwalten müssten. Und während sich Reverse Proxy oder Cloudflare Tunnel auf Zertifikate verlassen, wie in wie der TLS-Handshake funktioniert beschrieben, braucht ein SSH-Tunnel keines. Die Verschlüsselung ist die SSH-Sitzung selbst, authentifiziert durch Ihren Schlüssel statt durch ein Zertifikat, dem ein Browser vertrauen würde.
Was sollten Sie zuerst einrichten?
Nehmen Sie den Tunnel, den Sie am häufigsten neu eintippen, und machen Sie noch heute einen Host-Block in ~/.ssh/config daraus. Etwas zu erreichen, das nur ein entfernter Host sieht, ist -L. Einen lokalen Dienst über eine Maschine mit öffentlicher Adresse freizugeben, ist -R. Den Traffic einer Anwendung durch ein entferntes Netzwerk zu routen, ohne Ziele im Voraus festzulegen, ist -D. Einmal in der Config, ist ssh -N -f db-tunnel alles, was Sie noch eintippen müssen — und Sie vertippen sich um 23 Uhr nicht mehr bei der Portnummer.