Ein SSH-Tunnel über mehrere Hops


332

Das Tunneln von Daten über SSH ist ziemlich einfach:

ssh -D9999 username@example.com

Richtet Port 9999 auf Ihrem localhostals Tunnel ein example.com, aber ich habe einen genaueren Bedarf:

  • Ich arbeite vor Ort an localhost
  • host1 ist zugänglich für localhost
  • host2 Akzeptiert nur Verbindungen von host1
  • Ich muss einen Tunnel von localhostbis erstellenhost2

Eigentlich möchte ich einen "Multi-Hop" SSH-Tunnel erstellen. Wie kann ich das machen? Im Idealfall möchte ich dies tun, ohne auf einem der Computer Superuser sein zu müssen .


2
Wofür hast du es benutzt? Ich möchte es als Sockenproxy verwenden. Wird es funktionieren?
Zinken

2
Ja, sollten Sie in der Lage sein , die getunnelte Verbindung als SOCKS - Proxy zu verwenden, es sei denn , host2Weiterleitung verweigert
Mala

Ich dachte darüber nach, einen Wrapper über SSH zu erstellen, der dies mithilfe der mehrfachen Verwendung von ProxyCommand einrichten würde.
Pavel Šimerda

@prongs Hast du es vor all den Jahren geschafft, dies für den SOCKS-Proxy zu verwenden?
Drux

Antworten:


324

Sie haben grundsätzlich drei Möglichkeiten:

  1. Tunnel von localhostbis host1:

    ssh -L 9999:host2:1234 -N host1
    

    Wie oben erwähnt, wird die Verbindung von host1bis host2nicht gesichert.

  2. Tunnel von localhostnach host1und von host1nach host2:

    ssh -L 9999:localhost:9999 host1 ssh -L 9999:localhost:1234 -N host2
    

    Dadurch wird ein Tunnel von localhostbis host1und ein weiterer Tunnel von host1bis geöffnet host2. Doch der Hafen 9999zu host2:1234können von jedermann verwendet werden host1. Dies kann ein Problem sein oder auch nicht.

  3. Tunnel von localhostnach host1und von localhostnach host2:

    ssh -L 9998:host2:22 -N host1
    ssh -L 9999:localhost:1234 -N -p 9998 localhost
    

    Dadurch wird ein Tunnel von localhostnach geöffnet , host1über den der eingeschaltete SSH-Dienst host2verwendet werden kann. Dann wird ein zweiter Tunnel von localhostbis host2durch den ersten Tunnel geöffnet .

Normalerweise würde ich mit der Option gehe 1. Wenn die Verbindung von host1zu host2Bedürfnissen gesichert wird, gehen mit der Option 2. Option 3 in erster Linie nützlich ist , einen Dienst zuzugreifen auf , host2dass nur erreichbar von ist host2selbst.


17
Option 3 war das, wonach ich gesucht habe, danke!
Mala

1
Ich möchte auf diese Weise surfen. Welches ist das Beste? Ich habe zuerst versucht, aber es hat nicht funktioniert. Ich habe in meinem Browser localhost einen Socken-Proxy eingestellt: 1234, aber kein Glück. :( bitte helfen ..
Zinken

1
@ Prongs versuchen Option 3
Mala

6
@Noli Wenn Sie ssh-agent verwenden (was Sie sollten), können Sie ihn mit der -AOption ssh über die Verbindungen weiterleiten .
Mika Fischer

2
@musically_ut - das ist richtig, der zweite ssh-Befehl wird auf host1 im Hintergrund ausgeführt. Wenn Sie also lokal erneut eine Verbindung herstellen möchten, müssen Sie nur den ersten Teil erneut ausführen. Wenn Sie hinzufügen -f, wird der Hintergrund sofort lokal ssh -f -L 9999:localhost:9999 host1angezeigt und die Verbindung wird wiederhergestellt, wenn Sie im ersten Fall die Tastenkombination Strg-C gedrückt haben. Oder ergänzen Sie -fden ursprünglichen Double-SSH-Befehl, wenn Sie ihn zum ersten Mal ausführen und alles sofort im Hintergrund ausführen.
Mark Fisher

153

Es gibt eine hervorragende Antwort, die die Verwendung der ProxyCommandKonfigurationsanweisung für SSH erklärt :

Fügen Sie dies zu Ihrer hinzu ~/.ssh/config(siehe man 5 ssh_configfür Details):

Host host2
  ProxyCommand ssh host1 -W %h:%p

Dann ssh host2wird automatisch durchtunneln host1(funktioniert auch mit X11 - Forwarding etc.).

Dies funktioniert auch für eine ganze Klasse von Hosts, z. B. nach Domain identifiziert:

Host *.mycompany.com
  ProxyCommand ssh gateway.mycompany.com -W %h:%p

Aktualisieren

OpenSSH 7.3 führt eine ProxyJumpDirektive ein, die das erste Beispiel vereinfacht

Host host2
  ProxyJump host1

3
Gibt es eine Möglichkeit, dies bedingt zu tun? Ich möchte das nur manchmal tun. Auch dies ist speziell für Befehle, aber ich suche etwas für den gesamten Port 22 (ssh, sftp, etc).
Stephane

1
@Stephane was meinst du mit speziell für befehle ? Ihre SSH - Konfiguration wird durch irgendetwas verwendet , um ssh, einschließlich git, sftpusw. afaik.
kynan

1
@Stephane Mir ist keine Möglichkeit bekannt, dies bedingt zu aktivieren (z. B. nur, wenn Sie sich außerhalb des Netzwerks des Zielhosts befinden). Ich setze diese Option für alle fraglichen Hosts in einem Konfigurationsblock und kommentiere dann die Zeile nach Bedarf (un). Nicht perfekt, aber es funktioniert.
kynan

2
@Stephane sicher: ssh -F /path/to/altconfig. Beachten Sie, dass dies das gesamte System ignoriert /etc/ssh/ssh_config.
kynan

19
Eine einfache Möglichkeit, Einstellungen als "bedingt" zu definieren, besteht darin, in .ssh / config zwei verschiedene Hosts mit demselben Hostnamen zu definieren. Stellen Sie eine Verbindung zu host2-tunnel her, wenn Sie den Tunnel benötigen, und zu host2, wenn Sie dies nicht möchten.
Steve Bennett

25

OpenSSH v7.3 unterstützt einen -JSchalter und eine ProxyJumpOption, die einen oder mehrere durch Kommas getrennte Sprung-Hosts zulassen. Sie können dies also jetzt einfach tun:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host

ssh -J benutzer1 @ host1 -YC4c arcfour, blowfish-cbc benutzer2 @ host2 firefox -no-remote Dies beschleunigt den Wechsel von firefox von host2 zu localhost.
Jaur

21

Wir haben ein SSH-Gateway in unser privates Netzwerk. Wenn ich draußen bin und eine Remote-Shell auf einem Computer im privaten Netzwerk haben möchte, muss ich in das Gateway und von dort auf den privaten Computer ssh.

Um diesen Vorgang zu automatisieren, verwende ich das folgende Skript:

#!/bin/bash
ssh -f -L some_port:private_machine:22 user@gateway "sleep 10" && ssh -p some_port private_user@localhost

Was ist los:

  1. Richten Sie einen Tunnel für das SSH-Protokoll (Port 22) zum privaten Computer ein.
  2. Nur wenn dies erfolgreich ist, ssh in den privaten Rechner über den Tunnel. (Der &&-Operator stellt dies sicher).
  3. Nach dem Schließen der privaten SSH-Sitzung soll auch der SSH-Tunnel geschlossen werden. Dies geschieht über den Trick "Schlaf 10". Normalerweise wird der erste ssh-Befehl nach 10 Sekunden geschlossen. In dieser Zeit hat der zweite ssh-Befehl jedoch eine Verbindung über den Tunnel hergestellt. Infolgedessen hält der erste Befehl ssh den Tunnel offen, bis die folgenden beiden Bedingungen erfüllt sind: sleep 10 ist beendet und der Tunnel wird nicht mehr verwendet.

1
Sehr schlau!!! LIEBE ES!
Hendy Irawan

18

Nachdem ich das Obige gelesen und alles zusammengeklebt habe, habe ich das folgende Perl-Skript erstellt (speichere es als mssh in / usr / bin und mache es ausführbar):

#!/usr/bin/perl

$iport = 13021;
$first = 1;

foreach (@ARGV) {
  if (/^-/) {
    $args .= " $_";
  }
  elsif (/^((.+)@)?([^:]+):?(\d+)?$/) {
    $user = $1;
    $host = $3;
    $port = $4 || 22;
    if ($first) {
      $cmd = "ssh ${user}${host} -p $port -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $first = 0;
    }
    else {
      $cmd .= " -L $iport:$host:$port";
      push @cmds, "$cmd -f sleep 10 $args";
      $cmd = "ssh ${user}localhost -p $iport -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no";
      $args = '';
      $iport ++;
    }
  }
}
push @cmds, "$cmd $args";

foreach (@cmds) {
  print "$_\n";
  system($_);
}

Verwendungszweck:

So greifen Sie über HOSTA und HOSTB (gleicher Benutzer) auf HOSTC zu:

mssh HOSTA HOSTB HOSTC

So greifen Sie über HOSTA und HOSTB auf HOSTC zu und verwenden nicht standardmäßige SSH-Portnummern und verschiedene Benutzer:

mssh user1@HOSTA:1234 user2@HOSTB:1222 user3@HOSTC:78231

So greifen Sie über HOSTA und HOSTB auf HOSTC zu und verwenden die X-Weiterleitung:

mssh HOSTA HOSTB HOSTC -X

So greifen Sie über HOSTA und HOSTB auf Port 8080 auf HOSTC zu:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
das ist großartig
Mala

1
Ich kann Ihnen nicht genug danken, dieses Drehbuch erleichtert mir das tägliche Leben. Das einzige, was ich geändert habe, war, int (rand (1000)) zu iport hinzuzufügen, damit mehrere Instanzen gleichzeitig ausgeführt werden können. Ich schulde dir definitiv ein Bier.
Mala

Das funktioniert wirklich gut. Eine weitere Verbesserung wäre, HOSTB, HOSTC usw. mit localhosts / etc / hosts und ~ / .ssh / config zu beheben
Steve Bennett

Auch ich habe Malas Kommentar unterstützt. Ohne den randomisierten Port, wenn Sie dann versuchen, werden mssh HOSTA HOSTDSie tatsächlich bei HOSTB enden (und vielleicht nicht erkennen ..)
Steve Bennett

8

Diese Antwort ähnelt kynan, da ProxyCommand verwendet wird. Aber es ist bequemer, IMO zu verwenden.

Wenn Sie Netcat auf Ihren Hop-Maschinen installiert haben, können Sie dieses Snippet zu Ihrer ~ / .ssh / config hinzufügen:

Host *+*
    ProxyCommand ssh $(echo %h | sed 's/+[^+]*$//;s/\([^+%%]*\)%%\([^+]*\)$/\2 -l \1/;s/:/ -p /') nc $(echo %h | sed 's/^.*+//;/:/!s/$/ %p/;s/:/ /')

Dann

ssh -D9999 host1+host2 -l username

werde tun, was du gefragt hast.

Ich bin auf der Suche nach dem ursprünglichen Ort hierher gekommen, an dem ich diesen Trick gelesen habe. Ich werde einen Link posten, wenn ich ihn finde.


2
Ich glaube, das ist der Ursprung des Tricks: wiki.gentoo.org/wiki/SSH_jump_host
slm

5

Ich habe getan, was ich glaube, mit dem du machen wolltest

ssh -D 9999 -J host1 host2

Ich werde nach beiden Passwörtern gefragt, dann kann ich localhost: 9999 als SOCKS-Proxy für host2 verwenden. Es ist das nächste Beispiel, das ich mir vorstellen kann.


Das hat perfekt funktioniert!
Arthur Silva

4
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999: host2: 80

Mittel binden an localhost: 9999 und alle an localhost: 9999 gesendeten Pakete leiten sie an host2: 80 weiter

-R 9999: localhost: 9999

Bedeutet, dass jedes von host1: 9999 empfangene Paket an localhost: 9999 weitergeleitet wird


Geniale, einfachste Antwort, um einen Tunnel zu erstellen, damit Sie direkt von localhost auf die Anwendung auf host2 zugreifen können: 9999
dvtoever

Nach dieser Antwort erhalte ich eine channel 3: open failed: administratively prohibited: open failed Fehlermeldung.
Franck Dernoncourt

2

Sie sollten in der Lage sein, über die Portweiterleitung auf einen Dienst host2von zuzugreifen localhost. Ein guter Leitfaden befindet sich hier . Auszug:

Es gibt zwei Arten der Portweiterleitung: lokale und Remote-Weiterleitung. Sie werden auch als ausgehende und eingehende Tunnel bezeichnet. Die lokale Portweiterleitung leitet den an einem lokalen Port eingehenden Datenverkehr an einen angegebenen Remote-Port weiter.

Zum Beispiel, wenn Sie den Befehl ausgeben

ssh2 -L 1234:localhost:23 username@host

Der gesamte Datenverkehr, der auf dem Client an Port 1234 eingeht, wird an Port 23 auf dem Server (Host) weitergeleitet. Beachten Sie, dass localhost vom sshdserver aufgelöst wird, nachdem die Verbindung hergestellt wurde. In diesem Fall bezieht sich localhost daher auf den Server (Host) selbst.

Die Weiterleitung von Remoteports bewirkt das Gegenteil: Sie leitet den an einem Remoteport eingehenden Datenverkehr an einen angegebenen lokalen Port weiter.

Zum Beispiel, wenn Sie den Befehl ausgeben

ssh2 -R 1234:localhost:23 username@host

Der gesamte Datenverkehr, der an Port 1234 auf dem Server (Host) eingeht, wird an Port 23 auf dem Client (localhost) weitergeleitet.

Ersetzen Sie localhostin Ihrer Besetzung im Beispiel mit host2und hostmit host1.


laut diesem artikel wird die verbindung nur bis zur mittleren maschine (host1) gesichert. Gibt es eine Möglichkeit, um sicherzustellen, dass das Ganze sicher bleibt?
Mala

Ich habe das noch nie ausprobiert, aber wenn Host1 und Host2 beide SSH-Server sind, können Sie möglicherweise einen Tunnel von Host1 zu Host2 einrichten und dann einen Tunnel von LocalHost zu Host1 für denselben Dienst einrichten (um Ihren lokalen und Remote-Server zu erhalten) Ports richtig). Ich weiß nicht, ob das mit einem Befehl von localhost möglich ist.
Fideli

1

In dieser Antwort werde ich ein konkretes Beispiel durchgehen. Sie müssen nur die Hostnamen, Benutzernamen und Kennwörter der Computer durch Ihre ersetzen.

Problemstellung

Angenommen, wir haben die folgende Netzwerktopologie:

our local computer <---> server 1 <---> server 2

Nehmen wir der Vollständigkeit halber an, wir haben die Hostnamen, Benutzernamen und Passwörter der folgenden Computer:

LocalPC            <--->  hostname: mit.edu         <---> hec.edu
                          username: bob                   username: john 
                          password: dylan123              password: doe456

Ziel: Wir wollen einen SOCKS - Proxy einrichten, die auf Port hört 9991von LocalPCso dass jedes Mal , wenn eine Verbindung auf LocalPCvon Port initiiert wird 9991es durchläuft mit.edudann hec.edu.

Anwendungsbeispiel: Verfügthec.edu über einen HTTP-Server, auf den aus Sicherheitsgründen nur unter http://127.0.0.1:8001 zugegriffen werden kann. Wir möchten in der Lage sein, http://127.0.0.1:8001 zu besuchen, indem wir einen Webbrowser auf öffnen LocalPC.


Aufbau

In LocalPC, add in ~/.ssh/config:

Host HEC
    HostName hec.edu
    User john
    ProxyCommand ssh bob@mit.edu -W %h:%p

LocalPCFühren Sie dann im Terminal von Folgendes aus:

ssh -D9991 HEC

Es fragt Sie nach dem Passwort von bobon mit.edu(dh dylan123) und dann nach dem Passwort von johnon hec.edu(dh doe456).

Zu diesem Zeitpunkt wird der SOCKS-Proxy nun auf dem Port 9991von ausgeführt LocalPC.

Wenn Sie beispielsweise eine Webseite LocalPCmit dem SOCKS-Proxy besuchen möchten , können Sie in Firefox Folgendes tun:

Bildbeschreibung hier eingeben

Einige Bemerkungen:

  • in ~/.ssh/config, HECist der Verbindungsname: Sie können ihn in einen beliebigen Namen ändern.
  • Das -D9991sagt sshein SOCKS4 Proxy auf Port einzurichten 9991.

0

Wenn Sie SSH auf beiden Computern ausführen können, lesen Sie die ProxyCommand-Direktive von ssh. Auf diese Weise können Sie direkt von localhost zu host2 wechseln (mit einem einfachen Befehl, wenn Sie öffentliche Schlüssel verwenden !!). Dann können Sie mit host2 machen, was Sie wollen.

http://www.statusq.org/archives/2008/07/03/1916/


0

Die Option 2 der besten Antwort kann für andere SSH-Benutzer als die aktuelle verwendet werden, auch bekannt als: user @ host

    export local_host_port=30000
    export host1_user=xyz
    export host1=mac-host
    export host1_port=30000
    export host2=192.168.56.115
    export host2_user=ysg
    export host2_port=13306

    # Tunnel from localhost to host1 and from host1 to host2
    # you could chain those as well to host3 ... hostn
    ssh -tt -L $local_host_port:localhost:$host1_port $host1_user@$host1 \
    ssh -tt -L $host1_port:localhost:$host2_port $host2_user@$host2

0

In meinem Fall habe ich getan

localhost$ ssh -D 9999 host1
host1$ ssh -L 8890:localhost:8890 host2

wo host2:8890läuft auf einem Jupyter Notebook.

Dann habe ich Firefox für die Verwendung localhost:9999als SOCKS-Host konfiguriert .

Jetzt habe ich das Notebook auf meinem Computer , auf host2das Firefox localhost:8890zugreifen kann.


0

Die drei in der akzeptierten Antwort genannten Optionen haben bei mir überhaupt nicht funktioniert. Da ich über beide Hosts nicht viel Erlaubnis habe und es so aussieht, als hätte unser DevOps-Team ziemlich strenge Regeln, wenn es um die Authentifizierung und die Durchführung von MFA geht. Irgendwie können die obigen Befehle mit unserer Authentifizierung nicht gut umgehen.

Der Kontext ist jedoch den obigen Antworten sehr ähnlich: Ich kann nicht direkt auf den Produktionsserver zugreifen und muss einen Sprung mit einem Jump-Server machen.

Noch eine andere Lösung - eine naive

Am Ende habe ich es auf sehr naive Weise gemacht: Anstatt zu versuchen, alle Befehle auf meinem Laptop auszuführen, führe ich die folgenden Befehle auf jedem Computer aus :

  1. SSH in den Jump-Server und dann ausführen ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Wenn Sie zur Eingabe eines Kennworts aufgefordert werden, geben Sie es ein.
  2. Jetzt laufen Sie auf Ihrem Laptop ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Dadurch werden alle Ihre Anforderungen an Port 6969 Ihres Laptops an den Jump-Server weitergeleitet. Anschließend leitet der Jump-Server, da wir ihn in unserem vorherigen Schritt konfiguriert haben, Anforderungen von Port 6969 erneut an Port 2222 auf dem geschützten Zielserver weiter.

Sie sollten sehen, dass der Befehl dort "hängt", nachdem Sie eine Nachricht gedruckt haben - es bedeutet, dass sie funktionieren! Eine Ausnahme - Sie sollten keine Fehlermeldung sehen Could not request local forwarding., wenn Sie das sehen, dann funktioniert es immer noch nicht :(. Sie können jetzt versuchen, eine Anfrage auf Port 6969 von Ihrem Laptop aus zu starten und zu sehen, ob sie funktioniert.

Hoffentlich können Sie dies versuchen, wenn Sie jemand sind, bei dem alle oben genannten Methoden fehlgeschlagen sind.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.