Tunel SSH przez wiele przeskoków


332

Tunelowanie danych przez SSH jest dość proste:

ssh -D9999 username@example.com

ustawia port 9999 na twoim localhosttunelu do example.com, ale mam bardziej konkretną potrzebę:

  • Pracuję lokalnie localhost
  • host1 jest dostępny dla localhost
  • host2 akceptuje tylko połączenia z host1
  • Muszę stworzyć tunel od localhostdohost2

Skutecznie chcę utworzyć tunel SSH „multi-hop”. Jak mogę to zrobić? Idealnie, chciałbym to zrobić bez konieczności bycia superużytkownikiem na żadnej z maszyn.


2
Do czego go używałeś? Chcę go używać do proxy skarpet. Czy to zadziała?
ćwierkają

2
Tak, powinieneś być w stanie używać połączenia tunelowanego jako proxy SOCKS, chyba że host2odmówi przekazania
Mala

Myślałem o stworzeniu opakowania za pośrednictwem SSH, które skonfigurowałoby to za pomocą wielokrotnego użycia ProxyCommand.
Pavel Šimerda

@prongs Czy udało Ci się użyć tego do proxy SOCKS (wszystkie lata temu)?
Drux,

Odpowiedzi:


324

Zasadniczo masz trzy możliwości:

  1. Tunel od localhostdo host1:

    ssh -L 9999:host2:1234 -N host1
    

    Jak wspomniano powyżej, połączenie od host1do host2nie będzie zabezpieczone.

  2. Tunel od localhostdo host1i od host1do host2:

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

    Spowoduje to otwarcie tunelu od localhostdo host1i kolejnego tunelu od host1do host2. Jednak z portu, 9999z którego host2:1234może korzystać każdy host1. To może, ale nie musi stanowić problemu.

  3. Tunel od localhostdo host1i od localhostdo host2:

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

    Spowoduje to otwarcie tunelu od localhostcelu host1, przez którą na usługi SSH host2może być używany. Następnie otwierany jest drugi tunel od localhostdo host2pierwszego tunelu.

Zwykle wybrałbym opcję 1. Jeśli połączenie z host1do host2musi być zabezpieczone, przejdź do opcji 2. Opcja 3 jest głównie przydatna, aby uzyskać dostęp do usługi, host2która jest dostępna tylko od niej host2.


17
opcja 3 była tym, czego szukałem, dzięki!
Mala

1
Chcę przeglądać w ten sposób. Który jest najlepszy? Próbowałem pierwszy, ale to nie zadziałało. Ustawiłem proxy skarpet w mojej przeglądarce localhost: 1234, ale bez powodzenia. :( proszę o pomoc ..
zęby

1
@prongs wypróbuj opcję 3
Mala

6
@Noli Jeśli używasz ssh-agent (co powinieneś), możesz przesłać go przez połączenia za pomocą -Aopcji ssh.
Mika Fischer,

2
@musically_ut - to prawda, druga komenda ssh znajduje się w tle na hoście 1, więc jeśli chcesz ponownie połączyć się z nią lokalnie, wystarczy ponownie uruchomić pierwszą część. Jeśli dodasz -f, natychmiast rozpocznie się lokalnie w tle, a zatem ssh -f -L 9999:localhost:9999 host1połączy się ponownie, jeśli w pierwszym przypadku naciśniesz ctrl-c. Lub dodaj -fdo oryginalnej podwójnej komendy ssh przy pierwszym uruchomieniu, aby natychmiast wszystko było w tle.
Mark Fisher

153

Istnieje doskonała odpowiedź wyjaśniająca zastosowanie ProxyCommanddyrektywy konfiguracyjnej dla SSH :

Dodaj to do ~/.ssh/config( man 5 ssh_configszczegóły):

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

Następnie ssh host2automatycznie tuneluje host1(działa również z przekazywaniem X11 itp.).

Działa to również dla całej klasy hostów, np. Identyfikowanych według domeny:

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

Aktualizacja

OpenSSH 7.3 wprowadza się ProxyJumpdyrektywę, upraszczając pierwszy przykład do

Host host2
  ProxyJump host1

3
Czy można to zrobić warunkowo? Czasami chcę to robić. Dotyczy to również poleceń, ale szukam czegoś dla całego portu 22 (ssh, sftp itp.).
Stephane,

1
@Stephane, co masz na myśli mówiąc konkretnie o poleceniach ? Twój SSH config jest używany przez coś użyciu ssh, włączając git, sftpitp AFAIK.
kynan

1
@Stephane Nie znam sposobu na włączenie tego warunkowo (np. Tylko wtedy, gdy jesteś poza siecią hosta docelowego). Ustawiam tę opcję dla wszystkich hostów, o których mowa w bloku konfiguracji, a następnie (w razie potrzeby) komentuję wiersz. Nie idealnie, ale działa.
kynan

2
@Stephane pewne: ssh -F /path/to/altconfig. Uwaga, spowoduje to zignorowanie całego systemu /etc/ssh/ssh_config.
kynan

19
Łatwym sposobem, aby ustawienia były „warunkowe”, jest zdefiniowanie dwóch różnych hostów w .ssh / config, które mają tę samą nazwę hosta. Połącz się z host2-tunnel, gdy chcesz tunel, i host2, gdy nie chcesz.
Steve Bennett,

25

OpenSSH w wersji 7.3 i nowszej obsługuje -Jprzełącznik i ProxyJumpopcję, które pozwalają jednemu lub większej liczbie hostów skoku oddzielonych przecinkami, więc możesz po prostu zrobić to teraz:

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

ssh -J użytkownik1 @ host1 -YC4c arcfour, blowfish-cbc użytkownik2 @ host2 firefox -no-remote Przyspieszy to pobieranie firefoxa z host2 do localhost.
Jaur

21

Mamy jedną bramę ssh do naszej prywatnej sieci. Jeśli jestem na zewnątrz i chcę zdalnej powłoki na maszynie w sieci prywatnej, musiałbym ssh w bramie, a stamtąd na maszynie prywatnej.

Aby zautomatyzować tę procedurę, używam następującego skryptu:

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

Co się dzieje:

  1. Utwórz tunel dla protokołu ssh (port 22) na maszynę prywatną.
  2. Tylko jeśli to się powiedzie, ssh na prywatną maszynę za pomocą tunelu. (zapewnia to operator &&).
  3. Po zamknięciu prywatnej sesji ssh chcę również zamknąć tunel ssh. Odbywa się to za pomocą sztuczki „spać 10”. Zwykle pierwsze polecenie ssh zamyka się po 10 sekundach, ale w tym czasie drugie polecenie ssh nawiąże połączenie za pomocą tunelu. W rezultacie pierwsze polecenie ssh utrzymuje tunel otwarty, dopóki nie zostaną spełnione następujące dwa warunki: uśpienie 10 jest zakończone i tunel nie jest już używany.

1
Bardzo mądry!!! KOCHAM TO!
Hendy Irawan

18

Po przeczytaniu powyższego i sklejeniu wszystkiego razem, stworzyłem następujący skrypt Perla (zapisz go jako mssh w / usr / bin i spraw, aby był wykonywalny):

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

Stosowanie:

Aby uzyskać dostęp do HOSTC przez HOSTA i HOSTB (ten sam użytkownik):

mssh HOSTA HOSTB HOSTC

Aby uzyskać dostęp do HOSTC za pośrednictwem HOSTA i HOSTB i używać innych niż domyślne numerów portów SSH i różnych użytkowników:

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

Aby uzyskać dostęp do HOSTC przez HOSTA i HOSTB i użyć przekazywania X:

mssh HOSTA HOSTB HOSTC -X

Aby uzyskać dostęp do portu 8080 na HOSTC przez HOSTA i HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
to jest niesamowite
Mala

1
Naprawdę nie mogę ci wystarczająco podziękować, ten skrypt ułatwia mi codzienne życie. Jedyną rzeczą, którą zmieniłem, było dodanie int (rand (1000)) do iport, aby umożliwić jednoczesne działanie wielu instancji. Zdecydowanie jestem ci winien piwo.
Mala

To działa naprawdę dobrze. Kolejnym ulepszeniem byłoby rozwiązanie HOSTB, HOSTC itp. Przy użyciu /host / localhost hosta / etc / hosts i ~ / .ssh / config
Steve Bennett

Popieram także komentarz Mali. Bez losowego portu, jeśli spróbujesz wtedy mssh HOSTA HOSTDskończyć w HOSTB (i być może nie zdasz sobie sprawy ...)
Steve Bennett

8

Ta odpowiedź jest podobna do kynan, ponieważ wymaga użycia ProxyCommand. Ale wygodniej jest korzystać z IMO.

Jeśli masz NetCata zainstalowanego na swoich komputerach chmielowych, możesz dodać ten fragment do ~ / .ssh / config:

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

Następnie

ssh -D9999 host1+host2 -l username

zrobi to, o co prosiłeś.

Przybyłem tutaj, szukając oryginalnego miejsca, w którym czytałem tę sztuczkę. Kiedy go znajdę, opublikuję link.


2
Sądzę, że to jest początek sztuczki: wiki.gentoo.org/wiki/SSH_jump_host
slm

5

Zrobiłem to, co moim zdaniem chcesz zrobić

ssh -D 9999 -J host1 host2

Zostanie wyświetlony monit o podanie obu haseł, a następnie mogę użyć localhost: 9999 dla serwera proxy SOCKS do host2. To najbliższy mi przykład do pokazanego przez ciebie przykładu.


To działało idealnie!
Arthur Silva

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

-L 9999: host2: 80

Środki wiążą się z hostem lokalnym: 9999, a każdy pakiet wysłany do hosta lokalnego: 9999 przekazuje go do hosta2: 80

-R 9999: host lokalny: 9999

Oznacza, że ​​każdy pakiet otrzymany przez host1: 9999 przekazuje go z powrotem do localhost: 9999


Genialna, najprostsza odpowiedź, aby utworzyć tunel, aby uzyskać dostęp do aplikacji na hoście 2 bezpośrednio z localhost: 9999
dvtoever

Po tej odpowiedzi pojawia się channel 3: open failed: administratively prohibited: open failed komunikat o błędzie.
Franck Dernoncourt

2

powinieneś móc skorzystać z przekierowania portów, aby uzyskać dostęp do usługi host2z localhost. Dobry przewodnik znajduje się tutaj . Fragment:

Istnieją dwa rodzaje przekierowania portów: przekierowanie lokalne i zdalne. Są one również nazywane odpowiednio tunelami wyjściowymi i wejściowymi. Lokalne przekierowanie portów przekazuje ruch przychodzący do portu lokalnego do określonego portu zdalnego.

Na przykład, jeśli wydasz polecenie

ssh2 -L 1234:localhost:23 username@host

cały ruch przychodzący do portu 1234 na kliencie zostanie przekierowany do portu 23 na serwerze (hoście). Zauważ, że localhost zostanie rozwiązany przez sshdserver po ustanowieniu połączenia. W tym przypadku localhost odnosi się zatem do samego serwera (hosta).

Zdalne przekierowanie portów działa odwrotnie: przekazuje ruch przychodzący do portu zdalnego do określonego portu lokalnego.

Na przykład, jeśli wydasz polecenie

ssh2 -R 1234:localhost:23 username@host

cały ruch przychodzący do portu 1234 na serwerze (hoście) zostanie przekierowany do portu 23 na kliencie (localhost).

W swojej obsadzie, wymienić localhostna przykład host2i hostz host1.


zgodnie z tym artykułem połączenie zostanie zabezpieczone tylko do środkowej maszyny (host1). Czy istnieje sposób, aby upewnić się, że całość pozostanie bezpieczna?
Mala

Nigdy tego nie próbowałem, ale jeśli host1 i host2 są serwerami ssh, możesz skonfigurować tunel z hosta1 na host2, a następnie skonfigurować tunel z hosta lokalnego na host1 dla tej samej usługi (uzyskanie lokalnego i zdalnego porty po prawej). Nie wiem, czy jest to możliwe w jednym poleceniu z hosta lokalnego.
fideli

1

W tej odpowiedzi przedstawię konkretny przykład. Musisz tylko zastąpić nazwy hostów komputerów, nazwy użytkowników i hasła.

Opis problemu

Załóżmy, że mamy następującą topologię sieci:

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

Ze względu na konkretność załóżmy, że mamy następujące nazwy hostów, nazwy użytkowników i hasła komputerów:

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

Cel: chcemy, aby skonfigurować serwer proxy SOCKS, który nasłuchuje na porcie 9991o LocalPCtak, że za każdym razem gra na LocalPCinicjowane jest z portu 9991to przechodzi mit.edupotem hec.edu.

Przykład zastosowania: hec.eduma serwer HTTP, który jest dostępny tylko ze strony http://127.0.0.1:8001 ze względów bezpieczeństwa. Chcielibyśmy móc odwiedzić stronę http://127.0.0.1:8001 , otwierając przeglądarkę internetową LocalPC.


Konfiguracja

W LocalPCdodaj ~/.ssh/config:

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

Następnie w terminalu LocalPCuruchom:

ssh -D9991 HEC

Poprosi Cię o hasło bobon mit.edu(tj. dylan123), A następnie poprosi o hasło johnon hec.edu(tj doe456.).

W tym momencie, proxy SOCKS jest teraz uruchomiony na porcie 9991o LocalPC.

Na przykład, jeśli chcesz odwiedzić stronę internetową za LocalPCpomocą proxy SOCKS, możesz to zrobić w przeglądarce Firefox:

wprowadź opis zdjęcia tutaj

Kilka uwag:

  • in ~/.ssh/config, HECto nazwa połączenia: możesz zmienić ją na dowolną.
  • -D9991Mówi sshzałożyć SOCKS4 proxy na porcie 9991.

0

Jeśli możesz SSH na obu komputerach, spójrz na dyrektywę ProxyCommand ssh. To pozwoli ci przejść bezpośrednio z localhost do host2 (jednym prostym poleceniem, jeśli używasz kluczy publicznych !!). Następnie możesz zrobić, co chcesz z host2.

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


0

Opcja 2 najlepszej odpowiedzi może być używana z innymi użytkownikami ssh niż obecna aka: użytkownik @ 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

W moim przypadku tak zrobiłem

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

gdzie host2:8890działa na notatniku Jupyter.

Następnie skonfigurowałem Firefoksa localhost:9999jako host SOCKS.

Więc teraz mam notebooka host2dostępnego na Firefoxie localhost:8890na moim komputerze.


0

Trzy opcje wymienione w zaakceptowanej odpowiedzi w ogóle mi nie działały. Ponieważ nie mam dużego pozwolenia na oba hosty i wydaje się, że nasz zespół DevOps ma dość surowe zasady dotyczące uwierzytelniania i przeprowadzania uwierzytelniania makrofinansowego. Jakoś powyższe polecenia nie działają dobrze z naszym uwierzytelnieniem.

Kontekst jest jednak bardzo podobny do powyższych odpowiedzi: nie mogę ssh bezpośrednio do serwera produkcyjnego i muszę wykonać 1 przeskok przy użyciu serwera skoku.

Jeszcze inne rozwiązanie - naiwne

Skończyło się to na bardzo naiwny sposób: zamiast próbować uruchamiać wszystkie polecenia na moim laptopie, uruchamiam polecenia na każdym komputerze , jak poniżej:

  1. SSH do serwera skoku, a następnie uruchom ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Jeśli pojawi się monit o podanie hasła, wpisz je.
  2. Teraz uruchom na laptopie ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Spowoduje to przekazanie dowolnego żądania dotyczącego portu 6969 na laptopie do serwera skoku. Następnie, ponieważ skonfigurowaliśmy w poprzednim kroku, serwer skoku ponownie przekaże żądania z portu 6969 do portu 2222 na chronionym serwerze docelowym.

Powinieneś zobaczyć polecenie „zawiesza się” po wydrukowaniu wiadomości - oznacza to, że działają! Jeden wyjątek - nie powinieneś widzieć komunikatu o błędzie Could not request local forwarding., jeśli to zobaczysz, to nadal nie działa :(. Możesz teraz spróbować uruchomić żądanie na porcie 6969 z laptopa i sprawdzić, czy działa.

Mam nadzieję, że jeśli jesteś kimś, kto zawiódł wszystkie powyższe metody, być może możesz spróbować.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.