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

Un tunnel SSH via plusieurs hops

Le tunneling de données sur SSH est assez simple :

ssh -D9999 username@example.com

configure le port 9999 sur votre localhost comme un tunnel vers example.com, mais j'ai un besoin plus spécifique :

  • Je travaille localement sur localhost
  • host1 est accessible à localhost
  • host2 n'accepte que les connexions de host1
  • J'ai besoin de créer un tunnel de localhost vers host2

En fait, je veux créer un tunnel SSH “multi-hop”. Comment puis-je faire cela ? Idéalement, j'aimerais le faire sans avoir besoin d'être super-utilisateur sur n'importe laquelle des machines.

回答 (15)

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

Vous avez essentiellement trois possibilités :

  1. Tunnel de localhost à host1 :

  2. Tunnel de localhost à host1 et de host1 à host2 :

  3. Tunnel de localhost à host1 et de localhost à host2 :

Normalement, je choisirais l'option 1. Si la connexion de host1 à host2 doit être sécurisée, choisissez l'option 2. L'option 3 est principalement utile pour accéder à un service sur host2 qui n'est accessible qu'à partir de host2 lui-même.

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

Il y a une excellente réponse expliquant l'utilisation de la directive de configuration de ProxyCommand pour SSH :

Ajoutez ceci à votre ~/.ssh/config (voir man 5 ssh_config pour les détails) :

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

Alors ssh host2 va automatiquement créer un tunnel à travers host1 (fonctionne également avec le transfert X11 etc. Cela fonctionne également pour une classe entière d'hôtes, par exemple identifiés par domaine :

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

Update

OpenSSH 7.3 introduit une directive ProxyJump, simplifiant le premier exemple à

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

OpenSSH v7.3 supporte un commutateur -J et une option ProxyJump, qui permettent un ou plusieurs hôtes de saut séparés par des virgules, donc, vous pouvez simplement faire cela maintenant :

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

Nous avons une passerelle ssh vers notre réseau privé. Si je suis à l'extérieur et que je veux un shell distant sur une machine à l'intérieur du réseau privé, je dois passer par la passerelle ssh et de là vers la machine privée.

Pour automatiser cette procédure, j'utilise le script suivant :

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

Qu'est-ce qui se passe :

  1. Etablir un tunnel pour le protocole ssh (port 22) vers la machine privée.
  2. Seulement si cela réussit, ssh vers la machine privée en utilisant le tunnel. (l'opérateur && s'en assure).
  3. Après avoir fermé la session ssh privée, je veux que le tunnel ssh se ferme aussi. Cela se fait par le biais de l'astuce “sleep 10”. Habituellement, la première commande ssh se ferme au bout de 10 secondes, mais pendant ce temps, la deuxième commande ssh aura établi une connexion en utilisant le tunnel. Par conséquent, la première commande ssh maintient le tunnel ouvert jusqu'à ce que les deux conditions suivantes soient remplies : sleep 10 est terminé et le tunnel n'est plus utilisé.
18
18
18
2012-01-11 11:02:06 +0000

Après avoir lu ce qui précède et collé le tout, j'ai créé le script Perl suivant (enregistrez-le sous le nom mssh dans /usr/bin et rendez-le exécutable) :

#!/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($_);
}

Utilisation :

Pour accéder à HOSTC via HOSTA et HOSTB (même utilisateur) :

mssh HOSTA HOSTB HOSTC

Pour accéder à HOSTC via HOSTA et HOSTB et utiliser des numéros de port SSH non par défaut et différents utilisateurs :

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

Pour accéder à HOSTC via HOSTA et HOSTB et utiliser X-forwarding :

mssh HOSTA HOSTB HOSTC -X

Pour accéder au port 8080 sur HOSTC via HOSTA et HOSTB :

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

Cette réponse est similaire à celle de Kynan, car elle implique l'utilisation de ProxyCommand. Mais il est plus pratique d'utiliser IMO.

Si vous avez netcat installé dans vos machines à houblon, vous pouvez ajouter ce snippet à votre ~/.ssh/config :

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

Alors

ssh -D9999 host1+host2 -l username

fera ce que vous avez demandé.

Je suis venu ici pour chercher l'endroit original où j'ai lu ce truc. Je posterai un lien quand je l'aurai trouvé.

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

J'ai fait ce que je pense que vous vouliez faire avec

ssh -D 9999 -J host1 host2

On me demande les deux mots de passe, alors je peux utiliser localhost:9999 pour un proxy SOCKS pour héberger2. C'est le plus proche que je puisse imaginer de l'exemple que vous avez montré en premier lieu.

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

-L 9999:host2:80

Signifie se lier à localhost:9999 et tout paquet envoyé à localhost:9999 le transmettre à host2:80

-R 9999:localhost:9999

Signifie tout paquet reçu par host1:9999 le renvoyer à localhost:9999

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

vous devriez pouvoir utiliser la redirection de port pour accéder à un service sur host2 à partir de localhost. Un bon guide se trouve [ ici ]&3. Extrait :

Il existe deux types de redirection portuaire : la redirection locale et la redirection à distance. Elles sont également appelées respectivement tunnels sortants et entrants. La redirection de port local redirige le trafic venant d'un port local vers un port distant spécifié.

Par exemple, si vous lancez la commande

ssh2 -L 1234:localhost:23 username@host

, tout le trafic venant du port 1234 sur le client sera redirigé vers le port 23 sur le serveur (hôte). Notez que localhost sera résolu par le serveur sshds après l'établissement de la connexion. Dans ce cas, localhost se réfère donc au serveur (hôte) lui-même.

Remote port forwarding fait le contraire : il redirige le trafic venant d'un port distant vers un port local spécifié.

Par exemple, si vous lancez la commande

ssh2 -R 1234:localhost:23 username@host

tout le trafic qui arrive au port 1234 sur le serveur (hôte) sera redirigé vers le port 23 sur le client (localhost).

Dans votre casting, remplacez localhost dans l'exemple par host2 et host par host1.

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

Ma réponse est vraiment la même que toutes les autres réponses ici, mais, je voulais clarifier l'utilité de ~/.ssh/config et de ProxyJump.

Disons que je dois arriver à une destination en 3 sauts, et, pour chaque saut, j'ai besoin d'un nom d'utilisateur, d'un hôte, d'un port et d'une identité spécifiques. En raison des critères d'identité, cela ne peut se faire qu'avec le fichier de configuration de ~/.ssh/config :

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

Depuis votre ordinateur, vous pouvez tester chaque saut individuellement, c'est-à-dire

$ 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

Un autre avantage du fichier ~/.ssh/config est qu'il permet également les transferts de fichiers sftp, par exemple

$ 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

Dans cette réponse, je vais passer en revue un exemple concret. Il vous suffit de remplacer les noms d'hôtes, les noms d'utilisateurs et les mots de passe des ordinateurs par les vôtres. Supposons que nous ayons la topologie de réseau suivante :

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

Pour être concret, supposons que nous ayons les noms d'hôtes, les noms d'utilisateurs et les mots de passe des ordinateurs suivants :

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

Objectif : nous voulons mettre en place un proxy SOCKS qui écoute sur le port 9991 de LocalPC de sorte que chaque fois qu'une connexion sur LocalPC est initiée depuis le port 9991, elle passe par mit.edu puis par hec.edu. Exemple de cas d'utilisation : hec.edu a un serveur HTTP qui n'est accessible que sur http://127.0.0.1:8001 , pour des raisons de sécurité. Nous aimerions pouvoir visiter http://127.0.0.1:8001 en ouvrant un navigateur web sur LocalPC.


Configuration

Dans LocalPC, ajoutez ~/.ssh/config :

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

Puis dans le terminal de LocalPC, lancez :

ssh -D9991 HEC

Il vous demandera le mot de passe de bob sur mit.edu (c'est-à-dire dylan123), puis il vous demandera le mot de passe de john sur hec.edu (c'est-à-dire À ce stade, le proxy SOCKS fonctionne maintenant sur le port doe456 de 9991.

Par exemple, si vous voulez visiter une page web sur LocalPC en utilisant le proxy SOCKS, vous pouvez faire dans Firefox :

Quelques remarques :

  • dans LocalPC, ~/.ssh/config est le nom de connexion : vous pouvez le changer pour ce que vous voulez.
  • Le HEC indique à -D9991 de mettre en place un proxy SOCKS4 sur le port ssh.
0
0
0
2020-02-22 07:13:01 +0000

Seulement cela m'a aidé sur plus de deux hôtes :

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

Il vous demandera trois mots de passe. Inspiré par cette réponse

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

Dans mon cas, j'ai fait

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

host2:8890 tourne sur un Jupyter Notebook.

Ensuite j'ai configuré Firefox pour utiliser localhost:9999 comme hôte SOCKS.

Donc maintenant j'ai le notebook tournant sur host2 accessible par Firefox à localhost:8890 sur ma machine.

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

L'option 2 de la meilleure réponse pourrait être utilisée avec des utilisateurs ssh différents de l'actuel alias : 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

Les trois options mentionnées dans la réponse acceptée n'ont pas du tout fonctionné pour moi. Comme je n'ai pas beaucoup d'autorisation sur les deux hôtes, et qu'il semble que notre équipe DevOps ait des règles assez strictes en matière d'authentification et qu'elle fasse de l'AMF. D'une certaine manière, les commandes ci-dessus ne peuvent pas bien fonctionner avec notre authentification.

Le contexte est vraiment similaire aux réponses ci-dessus cependant : je ne peux pas accéder directement au serveur de production par ssh, et je dois faire un saut en utilisant un serveur de saut.

Encore une autre solution - une solution naïve

J'ai fini par le faire d'une manière très naïve : au lieu d'essayer d'exécuter toutes les commandes sur mon ordinateur portable, j'exécute les commandes sur chacune des machines , comme ci-dessous :

  1. SSH dans votre serveur de saut, puis exécutez ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Si vous êtes invité à saisir un mot de passe, tapez-le.
  2. Sur votre ordinateur portable, lancez ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Cela transmettra toutes vos demandes sur le port 6969 de votre ordinateur portable, au serveur de saut. Ensuite, comme nous l'avons configuré à l'étape précédente, le serveur de saut transférera à nouveau les demandes du port 6969 au port 2222 sur le serveur de destination protégé.

Vous devriez voir la commande “hang” là après avoir imprimé un message - cela signifie qu'ils fonctionnent ! Une exception - vous ne devriez pas voir de message d'erreur comme Could not request local forwarding., si vous voyez cela, alors cela ne fonctionne toujours pas :(. Vous pouvez maintenant essayer de lancer la requête sur le port 6969 depuis votre ordinateur portable, et voir si cela fonctionne.

Avec un peu de chance, si vous êtes quelqu'un qui a échoué à toutes les méthodes ci-dessus, vous pouvez peut-être essayer ceci.