2010-01-16 05:58:48 +0000 2010-01-16 05:58:48 +0000
354
354

Ein SSH-Tunnel über mehrere Hops

Das Tunneln von Daten über SSH ist ziemlich unkompliziert:

ssh -D9999 username@example.com

richtet Port 9999 auf Ihrer localhost als Tunnel zu example.com ein, aber ich habe einen spezifischeren Bedarf:

  • Ich arbeite lokal auf localhost
  • host1 ist zugänglich für localhost
  • host2 akzeptiert nur Verbindungen von host1
  • Ich muss einen Tunnel von localhost nach host2

erstellen. Effektiv möchte ich einen “Multi-Hop”-SSH-Tunnel erstellen. Wie kann ich dies tun? Im Idealfall möchte ich dies tun, ohne dass ich auf irgendwo der Maschinen Superuser sein muss.

Antworten (15)

341
341
341
2010-01-17 21:31:56 +0000

Sie haben grundsätzlich drei Möglichkeiten:

  1. Tunnel von localhost bis host1:

  2. Tunnel von localhost bis host1 und von host1 bis host2:

  3. Tunnel von localhost bis host1 und von localhost bis host2:

Normalerweise würde ich mich für Option 1 entscheiden. Wenn die Verbindung von host1 zu host2 gesichert werden muss, wählen Sie Option 2. Option 3 ist hauptsächlich nützlich, um auf einen Dienst auf host2 zuzugreifen, der nur von host2 selbst aus erreichbar ist.

158
158
158
2010-08-01 17:10:27 +0000

Es gibt eine ausgezeichnete Antwort, die die Verwendung der Konfigurationsanweisung ProxyCommand für SSH erklärt :

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

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

Dann wird ssh host2 automatisch durch host1 tunneln (funktioniert auch mit X11-Weiterleitung usw.). )

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

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

Aktualisieren Sie

OpenSSH 7.3 führt eine ProxyJump-Richtlinie ein, wodurch das erste Beispiel zu

Host host2
  ProxyJump host1
``` vereinfacht wird
35
35
35
2016-08-10 09:11:34 +0000

Ab OpenSSH v7.3 unterstützt OpenSSH v7.3 eine -J-Umschaltung und eine ProxyJump-Option, die einen oder mehrere Komma-getrennte Sprungrechner erlauben, so dass Sie dies jetzt einfach tun können:

ssh -J jumpuser1@jumphost1,jumpuser2@jumphost2,...,jumpuserN@jumphostN user@host
20
20
20
2010-01-24 18:47:37 +0000

Wir haben ein ssh-Gateway in unser privates Netzwerk. Wenn ich mich außerhalb befinde und eine Remote-Shell auf einem Rechner innerhalb des privaten Netzwerks haben möchte, müsste ich ssh in das Gateway und von dort zum privaten Rechner senden.

Um dieses Verfahren zu automatisieren, benutze 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 passiert:

  1. Bauen Sie einen Tunnel für das ssh-Protokoll (Port 22) zum privaten Rechner auf:
  2. Nur wenn dies erfolgreich ist, ssh über den Tunnel auf den privaten Rechner. (Der Operator && stellt dies sicher).
  3. Nachdem die private ssh-Sitzung geschlossen wurde, soll auch der ssh-Tunnel geschlossen werden. Dies geschieht über den “Schlaf 10”-Trick. Normalerweise würde der erste ssh-Befehl nach 10 Sekunden geschlossen, aber während dieser Zeit hat der zweite ssh-Befehl eine Verbindung über den Tunnel aufgebaut. Infolgedessen hält der erste ssh-Befehl den Tunnel offen, bis die beiden folgenden Bedingungen erfüllt sind: Schlafmodus 10 ist beendet und der Tunnel wird nicht mehr benutzt.
18
18
18
2012-01-11 11:02:06 +0000

Nachdem ich das Obige gelesen und alles zusammengeklebt habe, habe ich das folgende Perl-Skript erstellt (speichern Sie es als mssh in /usr/bin und machen Sie 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($_);
}

Verwendung:

Um auf HOSTC über HOSTA und HOSTB (gleicher Benutzer) zuzugreifen:

mssh HOSTA HOSTB HOSTC

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

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

Um auf HOSTC über HOSTA und HOSTB zuzugreifen und X-Weiterleitung zu verwenden:

mssh HOSTA HOSTB HOSTC -X

Um auf Port 8080 auf HOSTC über HOSTA und HOSTB zuzugreifen:

mssh HOSTA HOSTB -L8080:HOSTC:8080
8
8
8
2013-03-13 09:57:28 +0000

Diese Antwort ist ähnlich wie bei Kynan, da sie die Verwendung von ProxyCommand beinhaltet. Aber es ist bequemer, IMO zu benutzen.

Wenn Sie netcat in Ihren Hopfenmaschinen installiert haben, können Sie dieses Snippet zu Ihrer ~/.ssh/config hinzufügen:

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

Dann werden

ssh -D9999 host1+host2 -l username

das tun, was Sie gefragt haben.

Ich bin hierher gekommen, um nach der ursprünglichen Stelle zu suchen, wo ich diesen Trick gelesen habe. Ich werde einen Link posten, wenn ich ihn finde.

6
6
6
2017-11-21 11:06:21 +0000

Ich habe mit

ssh -D 9999 -J host1 host2

getan, was ich denke Sie wollten. Ich werde zur Eingabe beider Kennwörter aufgefordert, dann kann ich localhost:9999 für einen SOCKS-Proxy für host2 verwenden. Das kommt dem von Ihnen gezeigten Beispiel am nächsten, das ich mir vorstellen kann.

4
4
4
2010-01-19 02:03:35 +0000
ssh -L 9999:host2:80 -R 9999:localhost:9999 host1

-L 9999:host2:80

Bedeutet bind to localhost:9999 und jedes an localhost:9999 gesendete Paket leitet es an host2:80

weiter > -R 9999:localhost:9999

Bedeutet jedes von host1:9999 empfangene Paket leitet es zurück an localhost:9999

2
2
2
2010-01-16 06:34:17 +0000

sollten Sie in der Lage sein, Portweiterleitung zu verwenden, um von host2 aus auf einen Dienst auf localhost zuzugreifen. Einen guten Leitfaden finden Sie hier . Auszug:

Es gibt zwei Arten der Portweiterleitung: lokale und entfernte Weiterleitung. Sie werden auch als ausgehende bzw. eingehende Tunnel bezeichnet. Die lokale Portweiterleitung leitet Verkehr, der an einem lokalen Port ankommt, an einen bestimmten entfernten Port weiter.

Wenn Sie beispielsweise den Befehl

ssh2 -L 1234:localhost:23 username@host

erteilen, wird der gesamte Verkehr, der an Port 1234 auf dem Client ankommt, an Port 23 auf dem Server (Host) weitergeleitet. Beachten Sie, dass localhost vom sshdserver aufgelöst wird, nachdem die Verbindung hergestellt ist. In diesem Fall bezieht sich localhost also auf den Server (Host) selbst.

Remote-Port-Weiterleitung bewirkt das Gegenteil: sie leitet den Verkehr, der an einem Remote-Port ankommt, an einen bestimmten lokalen Port weiter.

Wenn Sie zum Beispiel den Befehl

ssh2 -R 1234:localhost:23 username@host

erteilen, wird der gesamte Verkehr, der an Port 1234 auf dem Server (Host) ankommt, an Port 23 auf dem Client (localhost) weitergeleitet.

Ersetzen Sie in Ihrer Besetzung localhost im Beispiel durch host2 und host durch host1.

1
1
1
2019-12-18 04:08:37 +0000

Meine Antwort ist wirklich die gleiche wie alle anderen Antworten hier, aber ich wollte die Nützlichkeit von ~/.ssh/config und ProxyJump verdeutlichen.

Nehmen wir an, ich muss in 3 Hops zu einem Ziel gelangen, und für jeden Hop benötige ich einen bestimmten Benutzernamen, Host, Port und Identität. Wegen der Identitätskriterien kann dies nur mit der ~/.ssh/config-Konfigurationsdatei erfolgen:

Host hop1
    User user1
    HostName host1
    Port 22
    IdentityFile ~/.ssh/pem/identity1.pem

Host hop2
    User user2
    HostName host2
    Port 22
    IdentityFile ~/.ssh/pem/identity2.pem
    ProxyJump hop1

Host hop3
    User user3
    HostName host3
    Port 22
    IdentityFile ~/.ssh/pem/identity3.pem
    ProxyJump hop2

Von Ihrem Computer aus können Sie jeden Sprung einzeln testen, d.h.

$ ssh hop1 # will go from your PC to the host1 in a single step
$ ssh hop2 # will go from your PC to host1, then from host1 to host2, i.e. in two steps
$ ssh hop3 # will go from your PC to host1, then to host2, then to host3, i.e. in three steps

Eine weitere coole Sache an der ~/.ssh/config-Datei ist, dass dies auch sftp-Dateiübertragungen ermöglicht, z.B.

$ sftp hop1 # will connect your PC to host1 (i.e. file transfer between your PC and host1)
$ sftp hop2 # will connect your PC to host1 to host2 (i.e. file transfer between your PC and host2)
$ sftp hop3 # will connect your PC to host1 to host2 to host3 (i.e. file transfer between your PC and host3)
1
1
1
2017-03-18 20:13:02 +0000

In dieser Antwort werde ich auf ein konkretes Beispiel eingehen. Sie müssen nur die Hostnamen, Benutzernamen und Passwörter von Computern durch Ihre eigenen ersetzen.

Problemdarstellung

Nehmen wir der Konkretheit halber an, wir hätten folgende Netzwerktopologie:

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

Nehmen wir der Konkretheit halber an, wir hätten folgende Hostnamen, Benutzernamen und Passwörter von Computern:

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

Ziel: Wir wollen einen SOCKS-Proxy einrichten, der auf Port 9991 von LocalPC lauscht, so dass jedes Mal, wenn eine Verbindung auf LocalPC von Port 9991 aus initiiert wird, diese durch mit.edu dann hec.edu geht.

Beispiel für einen Anwendungsfall: hec.edu hat einen HTTP-Server, der aus Sicherheitsgründen nur unter http://127.0.0.1:8001 erreichbar ist. Wir möchten http://127.0.0.1:8001 besuchen können, indem wir einen Web-Browser auf LocalPC öffnen.


Konfiguration

In LocalPC fügen Sie in ~/.ssh/config hinzu:

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

Dann im Terminal von LocalPC:

ssh -D9991 HEC

Es fragt Sie nach dem Passwort von bob auf mit.edu (d.h, dylan123), dann fragt es Sie nach dem Passwort von john auf hec.edu (d.h., &001

Zu diesem Zeitpunkt läuft der SOCKS-Proxy nun auf Port doe456 von 9991.

Wenn Sie z.B. eine Webseite auf LocalPC mit dem SOCKS-Proxy besuchen möchten, können Sie in Firefox:

Einige Anmerkungen:

  • in LocalPC ist ~/.ssh/config der Verbindungsname: Sie können ihn nach Belieben ändern.
  • Die HEC weist -D9991 an, einen SOCKS4-Proxy auf Port ssh einzurichten.
0
0
0
2020-02-22 07:13:01 +0000

Nur dies half mir auf mehr als zwei Hosts:

ssh -L 6010:localhost:6010 user1@host1 \
-t ssh -L 6010:localhost:6010 user2@host2 \
-t ssh -L 6010:localhost:6010 user3@host3

Es wird Sie mit drei Passwörtern auffordern. Von dieser Antwort inspiriert

0
0
0
2018-07-26 05:24:38 +0000

In meinem Fall habe ich

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

getan, wobei host2:8890 auf einem Jupyter-Notebook läuft.

Dann habe ich Firefox so konfiguriert, dass er localhost:9999 als SOCKS-Host verwendet.

Jetzt habe ich also das Notebook, das auf host2 läuft, über Firefox unter localhost:8890 auf meinem Rechner erreichbar.

0
0
0
2017-10-05 08:43:30 +0000

Die Option 2 der beste Antwort könnte mit anderen ssh-Benutzern als dem aktuellen verwendet werden: 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
0
0
2019-08-20 19:09:55 +0000

Die drei Optionen, die in der akzeptierten Antwort genannt wurden, haben bei mir überhaupt nicht funktioniert. Da ich nicht viele Genehmigungen für beide Gastgeber habe und es so aussieht, als ob unser DevOps-Team ziemlich strenge Regeln hat, wenn es um die Authentifizierung geht und MFA durchführt. Irgendwie können die obigen Befehle nicht gut mit unserer Authentifizierung zusammenspielen.

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

Yet Another Solution - a naiv one

Am Ende habe ich es auf eine sehr naive Art und Weise gemacht: Anstatt zu versuchen, alle Befehle auf meinem Laptop auszuführen, führe ich die Befehle auf jedem der Rechner aus, wie unten beschrieben:

  1. SSH in Ihren Sprungserver, dann lassen Sie ssh -v -L 6969:localhost:2222 -N your-protected.dest.server laufen. Wenn Sie zur Eingabe eines Passworts aufgefordert werden, geben Sie es ein:
  2. Führen Sie nun auf Ihrem Laptop ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name aus. Dadurch wird jede Ihrer Anfragen auf Port 6969 Ihres Laptops an den Jump-Server weitergeleitet. Da wir in unserem vorherigen Schritt konfiguriert haben, wird der Jump-Server wiederum Anfragen von Port 6969 an Port 2222 auf dem geschützten Zielserver weiterleiten.

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

Wenn Sie jemand sind, der alle oben genannten Methoden fehlgeschlagen ist, können Sie dies hoffentlich versuchen.