Тунель SSH через декілька стрибків


332

Дані про тунелювання через SSH досить прямолінійні:

ssh -D9999 username@example.com

встановлює порт 9999 на ваш localhostяк тунель example.com, але у мене є більш конкретна потреба:

  • Я працюю на місцях localhost
  • host1 є доступним для localhost
  • host2 приймає лише з'єднання від host1
  • Мені потрібно створити тунель від localhostдоhost2

Ефективно, я хочу створити тунель SSH "мульти-хоп". Як я можу це зробити? В ідеалі, я хотів би зробити це, не маючи потреби бути надрукованим на будь-якій із машин.


2
Для чого ти ним користувався? Я хочу використовувати його для проксі-шкарпеток. Чи буде це працювати?
зуби

2
Так, ви повинні мати можливість використовувати тунельне з'єднання як проксі-сервер SOCKS, якщо тільки не host2заперечує пересилання
Мала

Я думав над створенням обгортки через SSH, який би встановив це, використовуючи багаторазове використання ProxyCommand.
Павло Шімерда

@prongs Вам вдалося використовувати це для проксі-сервера SOCKS (усі ці роки тому)?
Друкс

Відповіді:


324

У вас є три можливості:

  1. Тунель від localhostдо host1:

    ssh -L 9999:host2:1234 -N host1
    

    Як зазначалося вище, з'єднання від host1до host2не буде забезпеченим.

  2. Тунель від localhostдо host1і від host1до host2:

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

    Це відкриє тунель від localhostдо host1та інший тунель від host1до host2. Однак порт 9999для host2:1234може бути використаний ким - або на host1. Це може бути або не бути проблемою.

  3. Тунель від localhostдо host1і від localhostдо host2:

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

    Це дозволить відкрити тунель від localhostдо , host1через який на SSH сервіс host2може бути використаний. Потім другий тунель відкритий з localhostв host2протягом першого тунелю.

Як правило, я б пішов із варіантом 1. Якщо з’єднання з host1до host2потрібно забезпечити, перейдіть до опції 2. Варіант 3 в основному корисний для доступу до служби, host2яка доступна лише для host2себе.


17
варіант 3 був те, що я шукав, дякую!
Мала

1
Я хочу зробити перегляд таким чином. Який найкращий? Я спробував перший, але це не вийшло. Я встановив проксі-носок у своєму браузері localhost: 1234, але не пощастило. :( будь ласка допоможіть ..
prongs

1
@prongs спробуйте варіант 3
Мала

6
@Noli Якщо ви використовуєте ssh-агент (який вам слід), ви можете переслати його через з'єднання, скориставшись -Aопцією ssh.
Міка Фішер

2
@musically_ut - це правильно, 2-а команда ssh є фоновою програмою host1, тому якщо ви хочете знову підключитися локально до неї, вам потрібно лише знову повторити першу частину. Якщо ви додасте -f, він негайно буде фоновим фоном, тим самим ssh -f -L 9999:localhost:9999 host1з’єднається, якщо ви натиснули ctrl-c у першому випадку. Або додайте -fдо оригінальної команди подвійного ssh при першому запуску, щоб одразу перетворити все.
Марк Фішер

153

Є чудова відповідь, що пояснює використання ProxyCommandдирективи конфігурації для SSH :

Додайте це до свого ~/.ssh/config(див. man 5 ssh_configПодробиці):

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

Потім ssh host2автоматично пройде тунель host1(також працює з переадресацією X11 тощо).

Це також працює для цілого класу хостів, наприклад, ідентифікованих за доменом:

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

Оновлення

OpenSSH 7.3 вводить в ProxyJumpдирективу, спрощуючи перший приклад

Host host2
  ProxyJump host1

3
Чи є спосіб це зробити умовно? Мені хочеться це робити іноді. Крім того, це спеціально для команд, але я шукаю щось для всього порту 22 (ssh, sftp тощо).
Стефан

1
@Stephane, що ти маєш на увазі під командами ? Ваш SSH конфігурації використовується що - або , використовуючи ssh, в тому числі git, і sftpт.д. AFAIK.
kynan

1
@Stephane Я не знаю, як умовно ввімкнути це (наприклад, лише коли ви знаходитесь поза мережею цільового хоста). Я встановив цю опцію для всіх проблемних хостів у блоці конфігурації, а потім (не) прокоментуйте рядок за потребою. Не ідеально, але це працює.
kynan

2
@Stephane впевнений: ssh -F /path/to/altconfig. Остерігайтеся, що це буде ігнорувати систему в цілому /etc/ssh/ssh_config.
kynan

19
Найпростіший спосіб зробити настройки "умовними" - це визначити два різних хости в .ssh / config, які мають однакове HostName. Підключіться до хост2-тунелю, коли потрібно тунель, а хост2, коли цього не потрібно.
Стів Беннетт

25

OpenSSH v7.3 далі підтримує -Jперемикач і ProxyJumpопцію, яка дозволяє одному або декільком відокремленим комами хостам стрибків, так що ви можете просто зробити це зараз:

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

ssh -J user1 @ host1 -YC4c arcfour, blowfish-cbc user2 @ host2 firefox -no-remote Це пришвидшить отримання Firefox з host2 на localhost.
Яур

21

У нас є один шлюз ssh в нашу приватну мережу. Якщо я перебуваю на вулиці і хочу віддалену оболонку на машині всередині приватної мережі, мені доведеться зайти в шлюз і звідти в приватну машину.

Для автоматизації цієї процедури я використовую такий сценарій:

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

Що відбувається:

  1. Встановіть тунель для протоколу ssh (порт 22) до приватної машини.
  2. Тільки якщо це вдасться, вставте в приватну машину, використовуючи тунель. (оператор && забезпечує це).
  3. Після закриття приватного сеансу ssh я теж хочу, щоб ssh тунель закрився. Це робиться за допомогою трюку "спати 10". Зазвичай перша команда ssh закриється через 10 секунд, але за цей час друга команда ssh встановить з'єднання за допомогою тунелю. Як результат, перша команда ssh утримує тунель відкритим, поки не будуть виконані дві наступні умови: сон 10 закінчений і тунель більше не використовується.

1
Дуже розумний!!! ЛЮБИТИ ЇЇ!
Хенді Іраван

18

Прочитавши вищезазначене і склеївши все разом, я створив такий сценарій Perl (збережіть його як mssh в / usr / bin та зробіть його виконуваним):

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

Використання:

Щоб отримати доступ до HOSTC через HOSTA та HOSTB (той самий користувач):

mssh HOSTA HOSTB HOSTC

Для доступу до HOSTC через HOSTA та HOSTB та використання номерів SSH-портів за замовчуванням та різних користувачів:

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

Щоб отримати доступ до HOSTC через HOSTA та HOSTB та скористатися переадресацією X:

mssh HOSTA HOSTB HOSTC -X

Щоб отримати доступ до порту 8080 на HOSTC через HOSTA та HOSTB:

mssh HOSTA HOSTB -L8080:HOSTC:8080

1
це дивовижно
Мала

1
Я серйозно не можу вам подякувати достатньо, цей сценарій полегшує моє життя щодня. Єдине, що я змінив - це додати int (rand (1000)) до iport, щоб дозволити одночасно працювати декілька екземплярів. Я обов'язково тобі завдячую пивом.
Мала

Це працює дуже добре. Подальше вдосконалення було б вирішити HOSTB, HOSTC тощо за допомогою localhost / etc / hosts та ~ / .ssh / config
Стів Беннет

Також я другий коментар Мала. Без рандомізованого порту, якщо ви спробуєте, mssh HOSTA HOSTDви насправді опинитесь у HOSTB (а може, і не зрозумієте ..)
Стів Беннетт

8

Ця відповідь схожа на kynan, оскільки передбачає використання ProxyCommand. Але зручніше використовувати ІМО.

Якщо у вас в хоп-машинах встановлений netcat, ви можете додати цей фрагмент до свого ~ / .ssh / config:

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

Тоді

ssh -D9999 host1+host2 -l username

зробить те, про що ви просили.

Я прийшов сюди шукати оригінальне місце, де я читав цю хитрість. Я опублікую посилання, коли знайду його.


2
Я вважаю, що в цьому полягає трюк: wiki.gentoo.org/wiki/SSH_jump_host
slm

5

Я зробив те, що, на вашу думку, ти хотів зробити

ssh -D 9999 -J host1 host2

Мені запропоновано обидва паролі, тоді я можу використовувати localhost: 9999 для проксі-сервера SOCKS до host2. Це найближчий, який я можу придумати, до прикладу, який ви показали в першу чергу.


Це спрацювало чудово!
Артур Сільва

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

-L 9999: хост2: 80

Значить прив'язувати до localhost: 9999 і будь-який пакет, надісланий до localhost: 9999 пересилає його до host2: 80

-R 9999: localhost: 9999

Значить будь-який пакет, отриманий хостом1: 9999, пересилає його назад до localhost: 9999


Блискуча, найпростіша відповідь зробити тунель, щоб ви могли отримати доступ до програми на хості2 безпосередньо з localhost: 9999
dvtoever

Після цієї відповіді я отримую channel 3: open failed: administratively prohibited: open failed повідомлення про помилку.
Франк Дернонкурт

2

ви повинні мати можливість використовувати переадресацію портів для доступу до послуги host2з localhost. Хороший гід знаходиться тут . Витяг:

Існує два види переадресації портів: локальне та віддалене переадресація. Їх також називають відповідно вихідними та вхідними тунелями. Локальне переадресація порту пересилає трафік, що надходить до локального порту, на вказаний віддалений порт.

Наприклад, якщо ви видаєте команду

ssh2 -L 1234:localhost:23 username@host

весь трафік, що надходить на порт 1234 на клієнті, буде перенаправлений на порт 23 на сервері (хості). Зауважте, що localhost буде вирішений sshdserver після встановлення з'єднання. Тому в цьому випадку localhost посилається на сам сервер (хост).

Віддалене переадресація портів робить навпаки: воно пересилає трафік, що надходить до віддаленого порту, на вказаний локальний порт.

Наприклад, якщо ви видаєте команду

ssh2 -R 1234:localhost:23 username@host

весь трафік, який надходить до порту 1234 на сервері (хості), буде перенаправлений на порт 23 клієнта (localhost).

У своєму складі замініть localhostв прикладі на host2і hostз host1.


відповідно до цієї статті, з'єднання буде захищене лише до середньої машини (хоста1). Чи є спосіб переконатися, що вся справа залишається в безпеці?
Мала

Я ніколи цього не пробував, але якщо host1 і host2 обидва ssh-сервери, ви, можливо, зможете налаштувати тунель від host1 до host2, а потім налаштувати тунель від localhost до host1 для тієї ж послуги (отримання локального та віддаленого сервера порти справа). Я не знаю, чи можливо це в одній команді з localhost.
fideli

1

У цій відповіді я пройду конкретний приклад. Вам просто потрібно замінити імена хостів, імена користувачів і паролі комп'ютерів вашими.

Постановка проблеми

Припустимо, у нас є така мережа топологія:

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

Для конкретності припустимо, що у нас є імена хостів, імена користувачів і паролі таких комп’ютерів:

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

Мета: ми хочемо створити SOCKS проксі - сервер , який прослуховує порт 9991з LocalPCтак , що кожен раз , коли з'єднання по LocalPCініціюють з порту 9991він проходить через mit.eduте hec.edu.

Приклад використання: hec.eduмає HTTP-сервер, який доступний лише на http://127.0.0.1:8001 з міркувань безпеки. Ми хотіли б мати можливість відвідати http://127.0.0.1:8001 , відкривши веб-браузер на LocalPC.


Конфігурація

В LocalPC, додайте ~/.ssh/config:

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

Потім у терміналі LocalPCзапустіть:

ssh -D9991 HEC

Він попросить вас пароль bobна mit.edu(тобто dylan123), тоді він запитає пароль johnна hec.edu(тобто doe456).

У цей момент, SOCKS проксі тепер працює на порту 9991з LocalPC.

Наприклад, якщо ви хочете відвідати веб-сторінку про LocalPCвикористання проксі-сервера SOCKS, ви можете зробити це у Firefox:

введіть тут опис зображення

Деякі зауваження:

  • в ~/.ssh/config, HECце ім'я з'єднання: ви можете змінити його на все, що завгодно.
  • -D9991Каже sshпро створення SOCKS4 проксі на порту 9991.

0

Якщо ви можете SSH на обох машинах, погляньте на директиву ProxyCommand ssh. Це дозволить вам перейти прямо з localhost в host2 (за допомогою однієї простої команди, якщо ви використовуєте відкриті ключі !!). Тоді ви можете робити все, що завгодно, з хостом2.

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


0

Варіант 2 найкращої відповіді може використовуватися для інших користувачів ssh, ніж поточний ака: 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

У моєму випадку я це зробив

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

де host2:8890працює на Jupyter Notebook.

Тоді я налаштував Firefox використовувати localhost:9999як хост SOCKS.

Тож тепер у мене на моїй машині працює ноутбук, host2доступний Firefox localhost:8890.


0

Три варіанти, згадані у прийнятій відповіді, для мене взагалі не спрацювали. Оскільки я не маю великого дозволу на обидва хости, і, схоже, наша команда DevOps має досить суворі правила щодо аутентифікації та виконання МЗС. Команди, описані вище, не можуть добре відтворюватись з нашою автентифікацією.

Контекст насправді схожий на відповіді вище, хоча: я не можу пряму ssh на виробничий сервер, і я повинен робити 1 скачок за допомогою стрибкового сервера.

Ще одне рішення - наївне

Я в кінцевому підсумку робив це дуже наївно: замість того, щоб намагатися запускати всі команди на своєму ноутбуці, я запускаю команди на кожній машині , як показано нижче:

  1. SSH на ваш сервер стрибків, а потім запустіть ssh -v -L 6969:localhost:2222 -N your-protected.dest.server. Якщо вам буде запропоновано ввести пароль, введіть його.
  2. Тепер на своєму ноутбуку запустіть ssh -v -L 6969:localhost:6969 -N your-jump-server.host.name. Це передасть будь-який ваш запит на порт 6969 на вашому ноутбуці на сервер стрибків. Потім, по черзі, оскільки ми налаштували на попередньому кроці, сервер стрибків знову направить запити порту 6969 на порт 2222 на захищений сервер призначення.

Ви повинні побачити команду "висить" там після друку якогось повідомлення - це означає, що вони працюють! Один виняток - ви не повинні бачити повідомлення про помилку на зразок Could not request local forwarding., якщо ви бачите це, то воно все ще не працює :(. Тепер ви можете спробувати запустити запит на порт 6969 зі свого ноутбука та побачити, чи працює він.

Будемо сподіватися, що якщо ви хтось, хто не вдався до всіх вищевказаних методів, можливо, ви можете спробувати це.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.