Un tunnel SSH via plusieurs sauts


332

La tunnellisation des données via SSH est assez simple:

ssh -D9999 username@example.com

configure le port 9999 sur votre localhosttunnel example.com, mais j’ai un besoin plus spécifique:

  • Je travaille localement sur localhost
  • host1 est accessible à localhost
  • host2 accepte uniquement les connexions de host1
  • Je dois créer un tunnel à partir localhostdehost2

Effectivement, je veux créer un tunnel SSH "multi-sauts". Comment puis-je faire ceci? Idéalement, j'aimerais le faire sans avoir besoin d'être superutilisateur sur aucune des machines.


2
Pourquoi l'avez-vous utilisé? Je veux l'utiliser pour le proxy chaussettes. Est-ce que ça marchera?
dents

2
Oui, vous devriez pouvoir utiliser la connexion par tunnel comme proxy SOCKS, à moins host2que la transmission ne soit refusée
Mala,

Je pensais créer un wrapper sur SSH pour le configurer en utilisant plusieurs utilisations de ProxyCommand.
Pavel Šimerda

@prongs Avez-vous réussi à utiliser cela pour le proxy SOCKS (il y a toutes ces années)?
Drux

Réponses:


324

Vous avez essentiellement trois possibilités:

  1. Tunnel de localhostà host1:

    ssh -L 9999:host2:1234 -N host1
    

    Comme indiqué ci-dessus, la connexion de host1à host2ne sera pas sécurisée.

  2. Tunnel de localhostla host1et de host1la host2:

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

    Cela ouvrira un tunnel de localhostà host1et un autre tunnel de host1à host2. Cependant le port 9999à host2:1234peut être utilisé par tout le monde sur host1. Cela peut ou peut ne pas être un problème.

  3. Tunnel de localhostla host1et de localhostla host2:

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

    Cela ouvrira un tunnel de localhostà host1travers lequel le service SSH host2peut être utilisé. Ensuite, un deuxième tunnel est ouvert de localhostà host2travers le premier tunnel.

Normalement, je choisirais l'option 1. Si la connexion de host1à host2doit être sécurisée, choisissez l'option 2. L'option 3 est principalement utile pour accéder à un service host2accessible uniquement de host2lui-même.


17
L'option 3 était ce que je cherchais, merci!
Mala

1
Je veux faire la navigation de cette façon. Lequel est le meilleur? J'ai essayé le premier mais ça n'a pas marché. J'ai mis un proxy chaussettes dans mon navigateur localhost: 1234 mais pas de chance. :( s'il vous plaît aider ..
griffes

1
@prongs try option 3
Mala

6
@Noli Si vous utilisez ssh-agent (ce que vous devriez faire), vous pouvez le transférer via les connexions en utilisant l' -Aoption to ssh.
Mika Fischer

2
@musically_ut - c'est exact, la commande 2nd ssh est en arrière-plan sur host1. Par conséquent, si vous souhaitez vous reconnecter localement, il vous suffit de réexécuter la première partie. Si vous ajoutez -f, il sera immédiatement en arrière-plan localement, donc ssh -f -L 9999:localhost:9999 host1vous reconnecterez si vous avez appuyé sur ctrl-c dans le premier cas. Vous pouvez également ajouter -fà la commande double ssh originale lors de sa première exécution en arrière-plan immédiatement.
Mark Fisher

153

Il existe une excellente réponse expliquant l'utilisation de la ProxyCommanddirective de configuration pour SSH :

Ajoutez ceci à votre ~/.ssh/config(voir man 5 ssh_configpour plus de détails):

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

Puis ssh host2tunnel automatiquement à travers host1(fonctionne également avec le transfert X11, etc.).

Cela fonctionne également pour toute une classe d'hôtes, par exemple identifiée par le domaine:

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

Mise à jour

OpenSSH 7.3 introduit une ProxyJumpdirective, simplifiant le premier exemple pour

Host host2
  ProxyJump host1

3
Y a-t-il un moyen de le faire conditionnellement? Je veux seulement faire ça parfois. En outre, ceci est spécifiquement pour les commandes, mais je cherche quelque chose pour tout le port 22 (ssh, sftp, etc.).
Stéphane

1
@ Stephane que voulez-vous dire spécifiquement pour les commandes ? Votre configuration SSH est utilisée par tout ce qui utilise ssh, y compris git, sftpetc.
Kynan

1
@ Stéphane Je ne suis pas au courant d'un moyen de l'activer conditionnellement (par exemple, uniquement lorsque vous êtes en dehors du réseau de l'hôte cible). Je règle cette option pour tous les hôtes en question dans un bloc de configuration, puis commente (dé) commente la ligne si nécessaire. Pas parfait, mais ça marche.
Kynan

2
@Stephane vous: ssh -F /path/to/altconfig. Attention, ceci ignorera le système dans son ensemble /etc/ssh/ssh_config.
Kynan

19
Un moyen facile de définir des paramètres "conditionnels" consiste à définir deux hôtes différents dans .ssh / config, qui ont le même nom d'hôte. Connectez-vous à host2-tunnel lorsque vous souhaitez utiliser le tunnel et à host2 lorsque vous ne le souhaitez pas.
Steve Bennett

25

OpenSSH v7.3 prend en charge un -Jcommutateur et une ProxyJumpoption, qui autorisent un ou plusieurs hôtes de saut séparés par des virgules. Vous pouvez donc le faire maintenant:

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

ssh -J utilisateur1 @ hôte1 -YC4c arcfour, blowfish-cbc utilisateur2 @ hôte2 firefox -no-remote Cela accélérera l'obtention de Firefox de l'hôte2 à l'hôte local.
Jaur

20

Nous avons une passerelle SSH dans 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 devrais me connecter à la passerelle et de là à 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. Établissez un tunnel pour le protocole ssh (port 22) vers la machine privée.
  2. Seulement si cela réussit, ssh dans la machine privée en utilisant le tunnel. (L’opérateur && assure cela).
  3. Après la fermeture de la session ssh privée, je souhaite également que le tunnel ssh se ferme. Cela se fait via l'astuce "sommeil 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 à l'aide du tunnel. Par conséquent, la première commande ssh maintient le tunnel ouvert jusqu'à ce que les deux conditions suivantes soient remplies: la mise en veille 10 est terminée et le tunnel n'est plus utilisé.

1
Très intelligent!!! AIMER!
Hendy Irawan

18

Après avoir lu ce qui précède et tout coller ensemble, 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($_);
}

Usage:

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 autres que ceux par défaut et des utilisateurs différents:

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

Pour accéder à HOSTC via HOSTA et HOSTB et utiliser le transfert X:

mssh HOSTA HOSTB HOSTC -X

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

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
c'est génial
Mala

1
Je ne peux vraiment pas vous remercier assez, ce script me facilite la vie au quotidien. La seule chose que j'ai changée était d'ajouter int (rand (1000)) à iport, pour permettre à plusieurs instances de s'exécuter en même temps. Je vous dois certainement une bière.
Mala

Cela fonctionne vraiment bien. Une autre amélioration consisterait à résoudre HOSTB, HOSTC, etc. en utilisant les fichiers / etc / hosts et ~ / .ssh / config de localhost
Steve Bennett le

J'appuie également le commentaire de Mala. Sans le port aléatoire, si vous essayez ensuite, mssh HOSTA HOSTDvous vous retrouverez finalement sur HOSTB (et ne réaliserez peut-être pas ..)
Steve Bennett

8

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

Si vous avez installé netcat sur vos machines de saut, vous pouvez ajouter cet extrait à votre ~ / .ssh / config:

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

ensuite

ssh -D9999 host1+host2 -l username

fera ce que vous avez demandé.

Je suis venu ici à la recherche du lieu d'origine où j'ai lu cette astuce. Je posterai un lien quand je le trouverai.


2
Je crois que c'est l'origine de l'astuce: wiki.gentoo.org/wiki/SSH_jump_host
slm

5

J'ai fait ce que je pense que tu voulais faire avec

ssh -D 9999 -J host1 host2

On me demande les deux mots de passe, puis je peux utiliser localhost: 9999 pour un proxy SOCKS vers host2. Je pense le plus près de l'exemple que vous avez montré en premier lieu.


Cela a fonctionné parfaitement!
Arthur Silva le

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

-L 9999: hôte2: 80

Signifie bind to localhost: 9999 et tout paquet envoyé à localhost: 9999 le transfère à host2: 80

-R 9999: localhost: 9999

Désigne tout paquet reçu par host1: 9999 et renvoyé à localhost: 9999.


Brillante solution la plus simple pour créer un tunnel afin que vous puissiez accéder à l'application sur host2 directement à partir de localhost: 9999
dvtoever

Suite à cette réponse, je reçois un channel 3: open failed: administratively prohibited: open failed message d'erreur.
Franck Dernoncourt

2

vous devriez pouvoir utiliser la redirection de port pour accéder à un service host2depuis localhost. Un bon guide se trouve ici . Extrait:

Il existe deux types de transfert de port: le transfert local et le transfert distant. Ils sont également appelés tunnels sortants et entrants, respectivement. La redirection de port local transfère le trafic arrivant sur un port local à un port distant spécifié.

Par exemple, si vous exécutez la commande

ssh2 -L 1234:localhost:23 username@host

tout le trafic arrivant sur le port 1234 du client sera transféré sur le port 23 du serveur (hôte). Notez que localhost sera résolu par le serveur sshdserver une fois la connexion établie. Dans ce cas, localhost fait donc référence au serveur (hôte) lui-même.

Le transfert de port distant a l'effet inverse: il transfère le trafic arrivant sur un port distant à un port local spécifié.

Par exemple, si vous exécutez la commande

ssh2 -R 1234:localhost:23 username@host

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

Dans votre distribution, remplacez localhostdans l'exemple avec host2et hostavec host1.


selon cet article, la connexion ne sera sécurisée que jusqu'à l'ordinateur central (hôte1). Existe-t-il un moyen de s’assurer que tout reste en sécurité?
Mala

Je n'ai jamais essayé cela, mais si host1 et host2 sont tous deux des serveurs ssh, vous pourrez peut-être configurer un tunnel d'hôte1 à host2, puis un tunnel de localhost à host1 pour le même service (obtention de votre ports droit). Je ne sais pas si cela est possible dans une commande de localhost.
Fideli

1

Dans cette réponse, je vais passer à travers un exemple concret. Il vous suffit de remplacer les noms d'hôte, noms d'utilisateur et mots de passe des ordinateurs par les vôtres.

Déclaration du problème

Supposons que nous ayons la topologie de réseau suivante:

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

Pour des raisons concrètes, supposons que nous possédons les noms d'hôte, noms d'utilisateur et mots de passe des ordinateurs suivants:

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

Objectif: nous voulons configurer un proxy SOCKS qui écoute sur le port 9991de LocalPCsorte que chaque fois qu'une connexion LocalPCest établie à partir du port, 9991elle passe mit.eduensuite hec.edu.

Exemple de cas d'utilisation: hec.edudispose d'un serveur HTTP uniquement accessible 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 dans ~/.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 bobon mit.edu(ie dylan123), puis il vous demandera le mot de passe de johnon hec.edu(ie doe456).

À ce stade, le proxy SOCKS s'exécute maintenant sur le port 9991de LocalPC.

Par exemple, si vous souhaitez visiter une page Web sur l’ LocalPCutilisation du proxy SOCKS, vous pouvez le faire dans Firefox:

entrez la description de l'image ici

Quelques remarques:

  • in ~/.ssh/config, HECest le nom de la connexion: vous pouvez le changer en tout ce que vous voulez.
  • Le -D9991dit sshde configurer un proxy SOCKS4 sur le port 9991.

0

Si vous pouvez utiliser SSH sur les deux machines, jetez un œil à la directive ProxyCommand de ssh. Cela vous permettra de passer directement de localhost à host2 (en une seule commande si vous utilisez des clés publiques !!). Ensuite, vous pouvez faire ce que vous voulez avec host2.

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


0

L'option 2 de la meilleure réponse pourrait être utilisée avec des utilisateurs de ssh différents de l'actuel aka: utilisateur @ hôte

    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

Dans mon cas j'ai fait

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

host2:8890est exécuté sur un ordinateur portable Jupyter.

Ensuite, j'ai configuré Firefox pour l'utiliser en localhost:9999tant qu'hôte SOCKS.

Alors maintenant, le cahier est host2accessible sous Firefox à localhost:8890partir de mon ordinateur .


0

Les trois options mentionnées dans la réponse acceptée ne fonctionnaient pas du tout pour moi. Étant donné que je n'ai pas beaucoup de permission sur les deux hôtes, et que mon équipe DevOps a des règles assez strictes en matière d'authentification et de MFA. D'une certaine manière, les commandes ci-dessus ne peuvent pas bien fonctionner avec notre authentification.

Cependant, le contexte est très similaire aux réponses ci-dessus: je ne peux pas directement entrer en ssh dans le serveur de production et je dois faire 1 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, je lance les commandes sur chaque ordinateur , 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é à entrer un mot de passe, tapez-le.
  2. Maintenant, sur votre ordinateur portable, lancez ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Cela transmettra toute votre demande sur le port 6969 de votre ordinateur portable au serveur de saut. Puis tour à tour, puisque nous avons configuré à notre é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 "se bloquer" après avoir imprimé un message - cela signifie qu'ils fonctionnent! Une exception - vous ne devriez pas voir le message d'erreur comme Could not request local forwarding., si vous voyez ça, alors il ne fonctionne toujours pas :(. Vous pouvez maintenant essayer de lancer une requête sur le port 6969 à partir de votre ordinateur portable et voir si cela fonctionne.

Si tout va bien si vous êtes quelqu'un qui a échoué toutes les méthodes ci-dessus, peut-être que vous pouvez essayer ceci.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.