Terowongan SSH melalui banyak hop


332

Tunneling data melalui SSH cukup mudah:

ssh -D9999 username@example.com

mengatur port 9999 pada Anda localhostsebagai terowongan example.com, tetapi saya memiliki kebutuhan yang lebih spesifik:

  • Saya bekerja secara lokal localhost
  • host1 dapat diakses oleh localhost
  • host2 hanya menerima koneksi dari host1
  • Saya perlu membuat terowongan dari localhostkehost2

Secara efektif, saya ingin membuat terowongan SSH "multi-hop". Bagaimana saya bisa melakukan ini? Idealnya, saya ingin melakukan ini tanpa perlu menjadi pengguna super pada salah satu mesin.


2
Untuk apa Anda menggunakannya? Saya ingin menggunakannya untuk proxy kaus kaki. Apakah ini akan berhasil?
Cabang

2
Ya, Anda harus dapat menggunakan koneksi tunneled sebagai proxy SOCKS, kecuali jika host2menolak penerusan
Mala

Saya sedang berpikir tentang membuat pembungkus SSH yang akan mengatur bahwa menggunakan beberapa penggunaan ProxyCommand.
Pavel Šimerda

@prongs Sudahkah Anda berhasil menggunakan ini untuk proksi SOCKS (bertahun-tahun yang lalu)?
Drux

Jawaban:


324

Anda pada dasarnya memiliki tiga kemungkinan:

  1. Terowongan dari localhostke host1:

    ssh -L 9999:host2:1234 -N host1
    

    Seperti disebutkan di atas, koneksi dari host1ke host2tidak akan diamankan.

  2. Terowongan dari localhostke host1dan dari host1ke host2:

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

    Ini akan membuka terowongan dari localhostke host1dan terowongan lain dari host1ke host2. Namun port 9999untuk host2:1234dapat digunakan oleh siapa saja host1. Ini mungkin atau mungkin tidak menjadi masalah.

  3. Terowongan dari localhostke host1dan dari localhostke host2:

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

    Ini akan membuka terowongan dari localhostke host1melalui mana pada layanan SSH host2dapat digunakan. Kemudian terowongan kedua dibuka dari localhostke host2melalui terowongan pertama.

Biasanya, saya akan pergi dengan opsi 1. Jika koneksi dari host1ke host2perlu diamankan, lanjutkan dengan opsi 2. Opsi 3 terutama berguna untuk mengakses layanan host2yang hanya dapat dijangkau dari host2dirinya sendiri.


17
opsi 3 adalah apa yang saya cari, terima kasih!
Mala

1
Saya ingin melakukan penelusuran dengan cara ini. Mana yang terbaik? Saya mencoba yang pertama tetapi tidak berhasil. Saya menetapkan proxy kaus kaki di browser saya localhost: 1234 tetapi tidak berhasil. :( tolong bantu ..
cabang

1
@prong coba opsi 3
Mala

6
@Noli Jika Anda menggunakan ssh-agent (yang seharusnya), Anda dapat meneruskannya melalui koneksi menggunakan -Aopsi ke ssh.
Mika Fischer

2
@musically_ut - itu benar, perintah ssh ke-2 dilatar belakangi pada host1, jadi jika Anda ingin menghubungkan kembali secara lokal, Anda hanya perlu menjalankan kembali bagian pertama. Jika Anda menambahkan -f, itu akan segera latar belakang secara lokal, sehingga ssh -f -L 9999:localhost:9999 host1akan menyambung kembali jika Anda menekan ctrl-c dalam kasus pertama. Atau tambahkan -fke perintah ssh ganda asli saat pertama kali menjalankannya untuk segera mem-background semuanya.
Mark Fisher

153

Ada jawaban yang sangat bagus menjelaskan penggunaan ProxyCommanddirektif konfigurasi untuk SSH :

Tambahkan ini ke Anda ~/.ssh/config(lihat man 5 ssh_configuntuk detail):

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

Kemudian ssh host2akan secara otomatis menerobos host1(juga berfungsi dengan penerusan X11 dll.).

Ini juga berfungsi untuk seluruh kelas host misalnya diidentifikasi oleh domain:

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

Memperbarui

OpenSSH 7.3 memperkenalkan sebuah ProxyJumpdirektif, menyederhanakan contoh pertama yang

Host host2
  ProxyJump host1

3
Apakah ada cara untuk melakukan ini secara kondisional? Saya hanya ingin melakukan ini kadang-kadang. Juga, ini khusus untuk perintah, tapi saya mencari sesuatu untuk semua port 22 (ssh, sftp, dll).
Stephane

1
@Stephane apa yang Anda maksud dengan perintah khusus ? Konfigurasi SSH Anda digunakan oleh apa pun yang menggunakan ssh, termasuk git, sftpdll. Afaik.
kynan

1
@Stephane Saya tidak mengetahui cara mengaktifkan kondisi ini (mis. Hanya ketika Anda berada di luar jaringan host target). Saya mengatur opsi ini untuk semua host yang dimaksud dalam blok konfigurasi dan kemudian (tidak) berkomentar baris yang diperlukan. Tidak sempurna, tetapi berhasil.
kynan

2
@Stephane pasti: ssh -F /path/to/altconfig. Waspadalah ini akan mengabaikan sistem yang luas /etc/ssh/ssh_config.
kynan

19
Cara mudah untuk membuat pengaturan "kondisional" adalah dengan mendefinisikan dua host berbeda di .ssh / config, yang memiliki HostName yang sama. Sambungkan ke host2-tunnel saat Anda menginginkan terowongan, dan host2 saat Anda tidak menginginkannya.
Steve Bennett

25

OpenSSH v7.3 dan seterusnya mendukung sebuah -Jsakelar dan ProxyJumpopsi, yang memungkinkan satu atau lebih host melompat yang dipisahkan koma, jadi, Anda bisa melakukan ini sekarang:

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

ssh -J user1 @ host1 -YC4c arcfour, blowfish-cbc user2 @ host2 firefox -no-remote Ini akan mempercepat mendapatkan firefox dari host2 ke localhost.
Jaur

21

Kami memiliki satu ssh gateway ke jaringan pribadi kami. Jika saya di luar dan ingin shell jarak jauh pada mesin di dalam jaringan pribadi, saya harus ssh ke gateway dan dari sana ke mesin pribadi.

Untuk mengotomatiskan prosedur ini, saya menggunakan skrip berikut:

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

Apa yang terjadi:

  1. Buat terowongan untuk protokol ssh (port 22) ke mesin pribadi.
  2. Hanya jika ini berhasil, ssh ke mesin pribadi menggunakan terowongan. (operator && memastikan ini).
  3. Setelah menutup sesi ssh pribadi, saya ingin terowongan ssh ditutup juga. Ini dilakukan melalui trik "sleep 10". Biasanya, perintah ssh pertama akan ditutup setelah 10 detik, tetapi selama waktu ini, perintah ssh kedua akan membuat koneksi menggunakan terowongan. Akibatnya, perintah ssh pertama membuat terowongan tetap terbuka sampai dua kondisi berikut terpenuhi: sleep 10 selesai dan terowongan tidak lagi digunakan.

1
Sangat pintar!!! SUKA!
Hendy Irawan

18

Setelah membaca di atas dan menempelkan semuanya, saya telah membuat skrip Perl berikut (simpan sebagai mssh di / usr / bin dan buat itu dapat dieksekusi):

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

Pemakaian:

Untuk mengakses HOSTC melalui HOSTA dan HOSTB (pengguna yang sama):

mssh HOSTA HOSTB HOSTC

Untuk mengakses HOSTC melalui HOSTA dan HOSTB dan menggunakan SSH-portnumber dan pengguna yang berbeda:

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

Untuk mengakses HOSTC melalui HOSTA dan HOSTB dan menggunakan penerusan X:

mssh HOSTA HOSTB HOSTC -X

Untuk mengakses port 8080 di HOSTC melalui HOSTA dan HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
ini luar biasa
Mala

1
Aku benar-benar tidak bisa cukup berterima kasih, naskah ini membuat hidupku lebih mudah setiap hari. Satu-satunya hal yang saya ubah adalah menambahkan int (rand (1000)) ke iport, untuk memungkinkan beberapa instance berjalan pada saat yang sama. Aku jelas berutang bir padamu.
Mala

Ini bekerja dengan sangat baik. Peningkatan lebih lanjut adalah menyelesaikan HOSTB, HOSTC dll menggunakan localhost's / etc / hosts dan ~ / .ssh / config
Steve Bennett

Saya juga komentar kedua Mala. Tanpa port acak, jika Anda mencoba melakukannya mssh HOSTA HOSTDAnda akan benar-benar berakhir di HOSTB (dan mungkin tidak akan menyadari ..)
Steve Bennett

8

Jawaban ini mirip dengan kynan, karena melibatkan penggunaan ProxyCommand. Tetapi lebih nyaman menggunakan IMO.

Jika Anda menginstal netcat di mesin hop Anda, Anda dapat menambahkan snippet ini ke ~ / .ssh / config Anda:

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

Kemudian

ssh -D9999 host1+host2 -l username

akan melakukan apa yang Anda minta.

Saya datang ke sini mencari tempat asli di mana saya membaca trik ini. Saya akan memposting tautan ketika saya menemukannya.


2
Saya percaya ini adalah asal mula
slm

5

Saya melakukan apa yang saya pikir ingin Anda lakukan

ssh -D 9999 -J host1 host2

Saya diminta untuk kedua kata sandi, lalu saya dapat menggunakan localhost: 9999 untuk proxy SOCKS ke host2. Ini yang terdekat yang bisa saya pikirkan dengan contoh yang Anda tunjukkan di tempat pertama.


Ini bekerja dengan sempurna!
Arthur Silva

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

-L 9999: host2: 80

Berarti mengikat ke localhost: 9999 dan paket apa pun yang dikirim ke localhost: 9999 meneruskannya ke host2: 80

-R 9999: localhost: 9999

Berarti setiap paket yang diterima oleh host1: 9999 meneruskannya kembali ke localhost: 9999


Cemerlang, jawaban paling sederhana untuk membuat terowongan sehingga Anda dapat mengakses aplikasi pada host2 langsung dari localhost: 9999
dvtoever

Mengikuti jawaban ini, saya mendapatkan channel 3: open failed: administratively prohibited: open failed pesan kesalahan.
Franck Dernoncourt

2

Anda harus dapat menggunakan penerusan port untuk mengakses layanan host2dari localhost. Panduan yang bagus ada di sini . Kutipan:

Ada dua jenis penerusan porta: penerusan lokal dan jarak jauh. Mereka juga disebut terowongan keluar dan masuk. Port forwarding lokal meneruskan lalu lintas yang datang ke port lokal ke port jarak jauh yang ditentukan.

Misalnya, jika Anda mengeluarkan perintah

ssh2 -L 1234:localhost:23 username@host

semua lalu lintas yang datang ke port 1234 pada klien akan diteruskan ke port 23 di server (host). Perhatikan bahwa localhost akan diselesaikan oleh sshdserver setelah koneksi dibuat. Dalam hal ini localhost karena itu merujuk ke server (host) itu sendiri.

Penerusan port jarak jauh melakukan yang sebaliknya: itu meneruskan lalu lintas yang datang ke port jarak jauh ke port lokal yang ditentukan.

Misalnya, jika Anda mengeluarkan perintah

ssh2 -R 1234:localhost:23 username@host

semua lalu lintas yang datang ke port 1234 di server (host) akan diteruskan ke port 23 pada klien (localhost).

Dalam gips Anda, ganti localhostdalam contoh dengan host2dan hostdengan host1.


menurut artikel itu, koneksi hanya akan diamankan sampai mesin tengah (host1). Apakah ada cara untuk memastikan semuanya tetap aman?
Mala

Saya belum pernah mencoba ini, tetapi jika host1 dan host2 keduanya adalah ssh server, Anda mungkin dapat mengatur terowongan dari host1 ke host2, lalu mengatur terowongan dari localhost ke host1 untuk layanan yang sama (mendapatkan lokal dan jarak jauh Anda port kanan). Saya tidak tahu apakah itu mungkin dalam satu perintah dari localhost.
fideli

1

Dalam jawaban ini saya akan memberikan contoh konkret. Anda hanya perlu mengganti nama host, nama pengguna, dan kata sandi komputer dengan milik Anda.

Pernyataan masalah

Mari kita asumsikan kita memiliki topologi jaringan berikut:

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

Demi konkret, anggap kita memiliki nama host, nama pengguna, dan kata sandi komputer berikut:

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

Tujuan: kami ingin mengatur proxy SOCKS yang mendengarkan pada port 9991dari LocalPCsehingga setiap kali sambungan pada LocalPCdimulai dari pelabuhan 9991itu melewati mit.edukemudian hec.edu.

Contoh kasus penggunaan: hec.edumemiliki server HTTP yang hanya dapat diakses di http://127.0.0.1:8001 , untuk alasan keamanan. Kami ingin dapat mengunjungi http://127.0.0.1:8001 dengan membuka browser web pada LocalPC.


Konfigurasi

Dalam LocalPC, tambahkan ~/.ssh/config:

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

Kemudian di terminal LocalPC, jalankan:

ssh -D9991 HEC

Ia akan menanyakan kata sandi bobon mit.edu(yaitu, dylan123), lalu ia akan menanyakan kata sandi johnon hec.edu(yaitu, doe456).

Pada saat itu, SOCKS proxy sekarang berjalan pada port 9991dari LocalPC.

Misalnya, jika Anda ingin mengunjungi halaman web LocalPCmenggunakan proxy SOCKS, Anda bisa melakukannya di Firefox:

masukkan deskripsi gambar di sini

Beberapa komentar:

  • di ~/.ssh/config, HECadalah nama koneksi: Anda dapat mengubahnya ke apa pun yang Anda inginkan.
  • Ia -D9991memberitahu sshuntuk mengatur proxy SOCKS4 pada port 9991.

0

Jika Anda dapat SSH ke kedua mesin, lihat arahan ProxyCommand ssh. Ini akan membiarkan Anda langsung dari localhost ke host2 (dalam satu perintah mudah jika Anda menggunakan kunci publik !!). Maka Anda dapat melakukan apa pun yang Anda inginkan dengan host2.

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


0

Opsi 2 dari jawaban terbaik dapat digunakan dengan pengguna ssh yang berbeda dari yang sekarang 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

Dalam kasus saya, saya lakukan

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

di mana host2:8890berjalan di Notebook Jupyter.

Kemudian saya mengkonfigurasi Firefox untuk digunakan localhost:9999sebagai host SOCKS.

Jadi sekarang saya dapat menjalankan notebook yang host2dapat diakses oleh Firefox di localhost:8890komputer saya.


0

Tiga opsi yang disebutkan dalam jawaban yang diterima sama sekali tidak bekerja untuk saya. Karena saya tidak memiliki banyak izin atas kedua host, dan sepertinya tim DevOps kami memiliki aturan yang cukup ketat ketika datang ke otentikasi dan melakukan MFA. Entah bagaimana perintah di atas tidak bisa berjalan dengan baik dengan otentikasi kami.

Konteksnya benar-benar mirip dengan jawaban di atas: saya tidak dapat ssh ke server produksi secara langsung, dan harus melakukan 1 hop menggunakan server lompat.

Solusi Lain - solusi naif

Saya akhirnya melakukannya dengan cara yang sangat naif: alih-alih mencoba menjalankan semua perintah di laptop saya, saya menjalankan perintah pada setiap mesin , seperti di bawah ini:

  1. SSH ke server lompatan Anda, lalu jalankan ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Jika Anda diminta memasukkan kata sandi, ketikkan.
  2. Sekarang di laptop Anda, jalankan ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Ini akan meneruskan segala permintaan Anda pada port 6969 di laptop Anda, ke server lompat. Kemudian, karena kita mengkonfigurasi pada langkah kita sebelumnya, server lompatan akan kembali meneruskan permintaan port 6969 ke port 2222 pada server tujuan yang dilindungi.

Anda akan melihat perintah "hang" di sana setelah mencetak beberapa pesan - itu berarti mereka berfungsi! Satu pengecualian - Anda seharusnya tidak melihat pesan kesalahan seperti Could not request local forwarding., jika Anda melihat itu, maka itu masih tidak berfungsi :(. Anda sekarang dapat mencoba memecat permintaan pada port 6969 dari laptop Anda, dan melihat apakah itu berfungsi.

Semoga jika Anda adalah seseorang yang gagal semua metode di atas, mungkin Anda bisa mencoba ini.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.