Un tunnel SSH tramite più salti


332

Il tunneling dei dati su SSH è piuttosto semplice:

ssh -D9999 username@example.com

imposta la porta 9999 sul tuo localhostcome tunnel example.com, ma ho un'esigenza più specifica:

  • Sto lavorando localmente su localhost
  • host1 è accessibile a localhost
  • host2 accetta solo connessioni da host1
  • Devo creare un tunnel da localhostahost2

In effetti, voglio creare un tunnel SSH "multi-hop". Come posso fare questo? Idealmente, mi piacerebbe farlo senza bisogno di essere un superutente su nessuna delle macchine.


2
Per cosa l'hai usato? Voglio usarlo per le calze proxy. Funzionerà?
regge il

2
Sì, dovresti essere in grado di utilizzare la connessione tunnel come proxy SOCKS, a meno che non host2rifiuti di inoltrare
Mala

Stavo pensando di creare un wrapper su SSH che lo avrebbe impostato usando un uso multiplo di ProxyCommand.
Pavel Šimerda,

@prongs Sei riuscito a usarlo per il proxy SOCKS (tutti quegli anni fa)?
Drux l'

Risposte:


324

Fondamentalmente hai tre possibilità:

  1. Tunnel da localhosta host1:

    ssh -L 9999:host2:1234 -N host1
    

    Come notato sopra, la connessione da host1a host2non sarà protetta.

  2. Tunnel da localhosta host1e da host1a host2:

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

    Questo aprirà un tunnel da localhosta host1e un altro tunnel da host1a host2. Tuttavia la porta 9999a host2:1234può essere utilizzato da chiunque su host1. Questo può o meno essere un problema.

  3. Tunnel da localhosta host1e da localhosta host2:

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

    Questo aprirà un tunnel dal localhostal host1attraverso cui il servizio on SSH host2può essere utilizzato. Quindi un secondo tunnel viene aperto da localhosta host2attraverso il primo tunnel.

Normalmente, andrei con l'opzione 1. Se la connessione da host1a host2deve essere protetta, andare con l'opzione 2. L'opzione 3 è principalmente utile per accedere a un servizio host2raggiungibile solo da host2se stesso.


17
l'opzione 3 era quello che stavo cercando, grazie!
Mala,

1
Voglio fare la navigazione in questo modo. Qual è il migliore? Ho provato il primo ma non ha funzionato. Ho impostato un proxy calzini nel mio localhost del browser: 1234 ma senza fortuna. :(, per favore aiutatemi ..
recita il

1
@prongs provare l'opzione 3
Mala

6
@Noli Se usi ssh-agent (che dovresti), puoi inoltrarlo attraverso le connessioni usando l' -Aopzione a ssh.
Mika Fischer,

2
@musically_ut - è corretto, il secondo comando ssh è in background su host1, quindi se si desidera riconnettersi localmente ad esso, è necessario rieseguire solo la prima parte. Se aggiungi -f, verrà immediatamente messo in background localmente, quindi ssh -f -L 9999:localhost:9999 host1si riconnetterà se hai premuto ctrl-c nel primo caso. Oppure aggiungi -fil comando double ssh originale quando lo esegui per la prima volta in background tutto immediatamente.
Mark Fisher,

153

C'è un'eccellente risposta che spiega l'uso della ProxyCommanddirettiva di configurazione per SSH :

Aggiungi questo al tuo ~/.ssh/config(vedi man 5 ssh_configper i dettagli):

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

Quindi ssh host2eseguirà il tunneling automatico host1(funziona anche con l'inoltro X11 ecc.).

Questo funziona anche per un'intera classe di host, ad esempio identificati dal dominio:

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

Aggiornare

OpenSSH 7.3 introduce una ProxyJumpdirettiva, semplificando il primo esempio di

Host host2
  ProxyJump host1

3
C'è un modo per farlo condizionatamente? Voglio solo farlo a volte. Inoltre, questo è specificamente per i comandi, ma sto cercando qualcosa per tutta la porta 22 (ssh, sftp, ecc.).
Stephane,

1
@Stephane cosa intendi specificamente per i comandi ? La tua configurazione SSH viene utilizzata da qualsiasi cosa ssh, incluso git, sftpecc. Afaik.
kynan,

1
@Stephane Non sono a conoscenza di un modo per abilitare questo in modo condizionale (ad esempio solo quando sei fuori dalla rete dell'host di destinazione). Ho impostato questa opzione per tutti gli host in questione in un blocco di configurazione e quindi (un) commento la riga secondo necessità. Non perfetto, ma funziona.
kynan,

2
@Stephane sicuro: ssh -F /path/to/altconfig. Attenzione, questo ignorerà l'intero sistema /etc/ssh/ssh_config.
kynan,

19
Un modo semplice per rendere le condizioni "condizionate" è definire due host diversi in .ssh / config, che hanno lo stesso nome host. Connettiti a host2-tunnel quando vuoi il tunnel e host2 quando non lo fai.
Steve Bennett,

25

OpenSSH v7.3 in poi supporta uno -Jswitch e ProxyJumpun'opzione, che consentono uno o più host jump separati da virgola, quindi puoi semplicemente farlo ora:

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

ssh -J user1 @ host1 -YC4c arcfour, blowfish-cbc user2 @ host2 firefox -no-remote Questo accelererà il trasferimento di firefox da host2 a localhost.
Jaur,

21

Abbiamo un gateway ssh nella nostra rete privata. Se sono fuori e voglio una shell remota su una macchina all'interno della rete privata, dovrei accedere al gateway e da lì alla macchina privata.

Per automatizzare questa procedura, utilizzo il seguente script:

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

Che cosa sta succedendo:

  1. Stabilire un tunnel per il protocollo ssh (porta 22) alla macchina privata.
  2. Solo se questo ha successo, usa ssh nella macchina privata usando il tunnel. (l'operatore && lo assicura).
  3. Dopo aver chiuso la sessione SSH privata, voglio anche chiudere il tunnel SSH. Questo viene fatto tramite il trucco "sleep 10". Di solito, il primo comando ssh si chiudeva dopo 10 secondi, ma durante questo periodo il secondo comando ssh avrebbe stabilito una connessione usando il tunnel. Di conseguenza, il primo comando ssh mantiene aperto il tunnel fino a quando non vengono soddisfatte le seguenti due condizioni: il sonno 10 è terminato e il tunnel non viene più utilizzato.

1
Molto intelligente!!! LO ADORO!
Hendy Irawan,

18

Dopo aver letto quanto sopra e incollato tutto insieme, ho creato il seguente script Perl (salvalo come mssh in / usr / bin e rendilo eseguibile):

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

Uso:

Per accedere a HOSTC tramite HOSTA e HOSTB (stesso utente):

mssh HOSTA HOSTB HOSTC

Per accedere a HOSTC tramite HOSTA e HOSTB e utilizzare portnumbers SSH non predefiniti e utenti diversi:

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

Per accedere a HOSTC tramite HOSTA e HOSTB e utilizzare l'inoltro X:

mssh HOSTA HOSTB HOSTC -X

Per accedere alla porta 8080 su HOSTC tramite HOSTA e HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
è fantastico
Mala

1
Sinceramente non posso ringraziarti abbastanza, questa sceneggiatura mi semplifica la vita quotidianamente. L'unica cosa che ho cambiato è stata aggiungere int (rand (1000)) a iport, per consentire l'esecuzione simultanea di più istanze. Ti devo sicuramente una birra.
Mala,

Funziona davvero bene. Un ulteriore miglioramento sarebbe quello di risolvere HOSTB, HOSTC ecc. Utilizzando localhost / etc / hosts e ~ / .ssh / config
Steve Bennett,

Anche io secondo il commento di Mala. Senza la porta randomizzata, se poi provi a mssh HOSTA HOSTDfinire effettivamente su HOSTB (e forse non ti accorgerai ..)
Steve Bennett,

8

Questa risposta è simile a Kynan, in quanto comporta l'uso di ProxyCommand. Ma è più conveniente usare IMO.

Se hai netcat installato nelle macchine hop, puoi aggiungere questo frammento al tuo ~ / .ssh / config:

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

Poi

ssh -D9999 host1+host2 -l username

farà quello che hai chiesto.

Sono venuto qui alla ricerca del posto originale in cui ho letto questo trucco. Pubblicherò un link quando lo troverò.


2
Credo che questa sia l'origine del trucco: wiki.gentoo.org/wiki/SSH_jump_host
slm

5

Ho fatto quello che penso tu volessi fare

ssh -D 9999 -J host1 host2

Sono richieste entrambe le password, quindi posso usare localhost: 9999 per un proxy SOCKS su host2. È il più vicino a cui riesco a pensare all'esempio che hai mostrato in primo luogo.


Questo ha funzionato perfettamente!
Arthur Silva,

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

-L 9999: host2: 80

I mezzi si legano a localhost: 9999 e qualsiasi pacchetto inviato a localhost: 9999 lo inoltra a host2: 80

-R 9999: localhost: 9999

Indica qualsiasi pacchetto ricevuto da host1: 9999 inoltralo nuovamente a localhost: 9999


Risposta brillante e molto semplice per creare un tunnel in modo da poter accedere all'applicazione su host2 direttamente da localhost: 9999
dvtoever

Dopo questa risposta, ricevo un channel 3: open failed: administratively prohibited: open failed messaggio di errore.
Franck Dernoncourt,

2

dovresti essere in grado di utilizzare il port forwarding per accedere a un servizio host2da localhost. Una buona guida si trova qui . Estratto:

Esistono due tipi di port forwarding: locale e remoto. Sono anche chiamati tunnel in uscita e in entrata, rispettivamente. Il port forwarding locale inoltra il traffico proveniente da una porta locale verso una porta remota specificata.

Ad esempio, se si emette il comando

ssh2 -L 1234:localhost:23 username@host

tutto il traffico in arrivo sulla porta 1234 sul client verrà inoltrato alla porta 23 sul server (host). Si noti che localhost verrà risolto dallo sshdserver dopo aver stabilito la connessione. In questo caso localhost si riferisce quindi al server (host) stesso.

Il port forwarding remoto fa il contrario: inoltra il traffico proveniente da una porta remota verso una porta locale specificata.

Ad esempio, se si emette il comando

ssh2 -R 1234:localhost:23 username@host

tutto il traffico che arriva alla porta 1234 sul server (host) verrà inoltrato alla porta 23 sul client (localhost).

Nel cast, sostituire localhostnell'esempio con host2e hostcon host1.


secondo tale articolo, la connessione sarà protetta solo fino alla macchina centrale (host1). C'è un modo per assicurarsi che tutto rimanga sicuro?
Mala,

Non l'ho mai provato, ma se host1 e host2 sono entrambi server SSH, potresti essere in grado di impostare un tunnel da host1 a host2, quindi impostare un tunnel da localhost a host1 per lo stesso servizio (ottenere il tuo locale e remoto porte a destra). Non so se sia possibile in un comando da localhost.
fideli,

1

In questa risposta farò un esempio concreto. Devi solo sostituire i nomi host, i nomi utente e le password dei computer con i tuoi.

Dichiarazione problema

Supponiamo di avere la seguente topologia di rete:

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

Per ragioni di concretezza, supponiamo di avere i seguenti nomi host, nomi utente e password dei computer:

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

Obiettivo: vogliamo impostare un proxy SOCKS in ascolto sulla porta 9991di LocalPCmodo che ogni volta che una connessione su LocalPCviene avviata dal porto 9991passa attraverso mit.eduallora hec.edu.

Esempio di caso d'uso: hec.eduha un server HTTP accessibile solo su http://127.0.0.1:8001 , per motivi di sicurezza. Vorremmo poter visitare http://127.0.0.1:8001 aprendo un browser web LocalPC.


Configurazione

In LocalPC, aggiungi ~/.ssh/config:

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

Quindi nel terminale di LocalPC, esegui:

ssh -D9991 HEC

Ti chiederà la password di bobon mit.edu(es. dylan123), Quindi ti chiederà la password di johnon hec.edu(es doe456.).

A quel punto, il proxy SOCKS è ora in esecuzione sulla porta 9991di LocalPC.

Ad esempio, se vuoi visitare una pagina web LocalPCusando il proxy SOCKS, puoi farlo in Firefox:

inserisci qui la descrizione dell'immagine

Alcune osservazioni:

  • in ~/.ssh/config, HECè il nome della connessione: puoi cambiarlo in qualsiasi cosa tu voglia.
  • La -D9991dice sshper impostare un proxy SOCKS4 sulla porta 9991.

0

Se puoi SSH su entrambe le macchine, dai un'occhiata alla direttiva ProxyCommand di ssh. Questo ti permetterà di passare direttamente da localhost a host2 (con un semplice comando se usi le chiavi pubbliche !!). Quindi puoi fare quello che vuoi con host2.

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


0

L'opzione 2 della migliore risposta potrebbe essere utilizzata con utenti ssh diversi da quello attuale, ovvero 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

Nel mio caso l'ho fatto

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

dove host2:8890è in esecuzione su un notebook Jupyter.

Quindi ho configurato Firefox per l'utilizzo localhost:9999come host SOCKS.

Quindi ora ho il notebook in esecuzione host2accessibile da Firefox localhost:8890sul mio computer.


0

Le tre opzioni menzionate nella risposta accettata non hanno funzionato affatto per me. Dal momento che non ho molti permessi su entrambi gli host, e sembra che il nostro team DevOps abbia delle regole piuttosto rigide quando si tratta di autenticazione e sta facendo MFA. In qualche modo i comandi sopra non possono giocare bene con la nostra autenticazione.

Il contesto è molto simile alle risposte precedenti però: non riesco a collegarmi direttamente al server di produzione e devo fare 1 hop usando un jump server.

Ancora un'altra soluzione - ingenua

Ho finito per farlo in un modo molto ingenuo: invece di provare a eseguire tutti i comandi sul mio laptop, eseguo i comandi su ciascuna macchina , come di seguito:

  1. SSH nel tuo jump server, quindi corri ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Se ti viene richiesto di inserire una password, digita.
  2. Ora sul tuo laptop, corri ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Questo inoltrerà qualsiasi tua richiesta sulla porta 6969 sul tuo laptop, al server di salto. Quindi, a turno, poiché abbiamo configurato nel passaggio precedente, il server di salto inoltrerà nuovamente le richieste della porta 6969 alla porta 2222 sul server di destinazione protetto.

Dovresti vedere il comando "si blocca" lì dopo aver stampato un messaggio - significa che stanno funzionando! Un'eccezione: non dovresti vedere un messaggio di errore simile Could not request local forwarding., se lo vedi, quindi non funziona ancora :(. Ora puoi provare a lanciare la richiesta sulla porta 6969 dal tuo laptop e vedere se funziona.

Spero che se sei qualcuno che ha fallito tutti i metodi sopra, forse puoi provare questo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.