SSH-Туннели простыми словами

В какой-то момент у меня возникла необходимость разобраться с простыми SSH-туннелями: как из запускать и какие туннели могут помочь мне (обычному web-разработчику). Разобраться в этом удалось и я решил поделиться пояснениями в простой понятной форме.

Сразу важная мысль: основу для для этого поста в свою голову впитал из этой статьи, за что автору огромное спасибо! Эта статья чуть более хардкорная для понимания, но куда более емкая, чем моя. Мой вариант для тех, кто в этом почти ничего не понимает и хочет разобраться с картинками и деталями.

Если вам более удобен видео-формат, то можете посмотреть его на YouTube.

С чего возникла необходимость в туннелях?

Я применяю, так или иначе, SSH туннели регулярно в своей работе. Иногда необходимо получить доступ к ресурсу, базе данных или сервису, который находится в закрытой сети, но у меня есть SSH доступ к одному из серверов в этой сети.

Все описанное ниже является исключительно интерпретацией моего опыта и изучения различных статей по теме. Буду очень признателен, если вы найдете ошибку и сообщите мне об этом через в комментариях. Спасибо!

Мое первое знакомство с туннелями произошло при моей попытке развернуть разработанный проект на сервере клиента. Я приехал к заказчику в офис, заранее сохранив в заметки ссылки на наши репозитории, названия докер образов и тд.

Но меня ждал сюрприз. Клиент просто дал мне клавиатуру, показал монитор и сказал «на, разворачивай». По политике безопасности клиента я не мог получить удаленный доступ к этому серверу, чтобы удобно заливать все из своего офиса. Поэтому мне предстояло руками разворачивать все на этом сервере, вводя все ссылки и названия по букве.

Я с ужасом представил, как я буду руками вбивать каждую ссылку для репозитория и понял, что на это все уйдет ни один час.

757b1abf9febcc88ddcd1da26b375f76.png

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

Решив эту задачу я стал изучать другие, более упрощенные способы, решения этой задачи. Так я познакомился с туннелями.

Основная идея туннелей

Если нельзя напрямую, то почему бы не пойти в обход?

Если нельзя напрямую, то почему бы не пойти в обход?

В целом суть туннеля наглядно изображена на анимации выше. Но дополню текстом:

  1. У вас есть сервер, к которому вы хотите получить доступ, но не можете ходить на него напрямую. Назовем его сервер 1;

  2. Между вами и целевым сервером есть сервер, который имеет доступ к целевому и к которому имеете SSH доступ вы. Назовем его сервер 2;

  3. Вы можете зайти по SSH на сервер 2 и, через консоль, уже отправлять любые запрос на сервер 1. Также вы можете зайти по SSH на сервер 1 и двигаться дальше.

Примерно так и работает SSH-туннели. Только нам не всегда обязательно заходить на один сервер и с него уже слать запросы на следующий. Для упрощения этого процесса мы можем воспользоваться переадресацией портов (Port Forwarding).

Port forwarding на пальцах

У меня ушло какое-то количество мыслетоплива, чтобы понять суть работы переадресации портов.

Пишем порт 81, 80 в уме

Пишем порт 81, 80 в уме

Расширим ситуацию, описанную выше:

  1. У нас есть сервер S1, к которому у нас есть доступ на любой порт;

  2. Есть сервер S2, к 80 порту которого хотим получить доступ мы через наш S1;

  3. Мы одной командой можем открыть на S1 80 порт, чтобы он пересылал эти запросы на 80 порт S2 и потом передавал нам ответ;

  4. Также, например, одной командой, мы можем открыть 81 порт, который будет переадресовывать запрос на 80 порт другого сервера S3.

Именно эта «пересылка» портов и называется переадресацией портов или Port Forwarding. Теперь давайте перейдем к реальным кейсам использования туннелей.

Реальные примеры из практики

Кейс с nginx

Рассмотрим простой кейс. У нас есть сервер, который закрыт миру, но мы имеем к нему SSH доступ. На этом сервере запущен сервер nginx, который слушает 80 порт.

Для того, чтобы получить доступ к тому, что опубликовано на 80 порту сервера, мы можем открыть туннель, который подключится к серверу, и опубликовать на локальном 80 порту. То есть, мы будем стучаться на localhost:80 и видеть ровно то, что опубликовано на 80 порту удаленного сервера. Для этого вводим следующую команду:

ssh -L 80:127.0.0.1:80 remote.server.address

После ввода этой команды мы подключимся на сервер по SSH и, что важно, пока активно это соединение, у нас будет активен этот туннель. Теперь мы можем в браузере открыть localhost:80 и откроется то, что опубликовано на 80 порту удаленного сервера. Подробнее что-есть-что в этой команде:

6e5f100c23b3d9c6897fc32ff0dcc581.png

  1. Первым параметром идет флаг »-L» который указывает на то, что нам необходимо открыть туннель и начать слушать порт.

  2. Далее указываем интерфейс на котором мы хотим слушать (по-умолчанию localhost и в рамках данного примера он не указан). В нашем случае указываем порт 80 (на рис. «локальный порт»).

  3. Далее указываем куда надо будет подключаться на удаленном сервере. В нашем примере 127.0.0.1, что значит, что туннель будет пробрасывать на сам же удаленный сервер. На что можно заменять это значение можно прочитать ниже.

  4. Порт на удаленном сервере — в нашем случае это 80. Если бы nginx, например, видел бы на 8080, то мы бы указали 8080 в этом месте.

  5. Адрес удаленного сервера. Указываем его тем же самым образом, что и при обычном подключении к SSH.

Теперь можно переходить на localhost в браузере и видеть то, что на сервере на 80 порту. В моем примере можно увидеть только ошибку nginx, которую выдает nginx-proxy на сервере.

ca764fb2f7a5935dc60c0a11f124f319.png

Кейс с базой данных Postgres в докере

Теперь к еще одному полезному кейсу, с которым мы сталкиваемся во время разработки, так или иначе, регулярно.

  1. Есть я и мой ноутбук

  2. На ноутбуке есть код моего backend приложения

  3. В основном я работаю с локальной копией базы данных во время разработки

  4. По какой-то причине мне надо подключиться к базе данных, которая находится на сервере (тестовом или боевом) в изолированной сети Docker.

    Окольными путями напрямую в базу

    Окольными путями напрямую в базу

Когда мы говорим про работу через IDE для работы с БД, мы можем легко воспользоваться встроенным механизмом туннелей (инструкция по продуктам от JetBrains тут). Но трудности возникают если мы хотим из кода нашего приложения подключиться к этой базе.

Кстати, этот момент актуален не только для базы данных. Иногда нужно просто подключаться к разным сервисам, которые находятся в изолированной сети, куда мы можем иметь доступ через SSH. Так что, этот пример лишь показательный и область применения крайне обширна

Для решения этого вопроса достаточно вбить следующую команду:

ssh -L 5432:postgres-db:5432 remote.server.address

e60208b088882eab57bd2a1d785f484d.png

Тут ситуация аналогична предыдущей. Только вместо локального ip адреса самого сервера мы указываем ip адрес (в данном случае имя хоста postgres-db) в качестве целевого адреса.

В случае работы с Docker я использую этот контейнер, чтобы знать адреса всех контейнеров на сервере. После запуска имя хоста каждого контейнера можно получить в файле /etc/hosts. И после перезапуска ip адрес меняется, а хост остается

Дальше я уже могу указывать localhost:5432 в качестве сервера postgres в моем приложении и подключусь уже напрямую к базе данных в docker контейнере на том сервере.

Таких туннелей можно запускать сколько угодно. Все запускаются по аналогии. Например можно построить такую схему:

несколько туннелей на через один сервер

несколько туннелей на через один сервер

  1. Есть сервера S1, S2, S3.

  2. S1 имеет доступ к S2 и S2

  3. Я имею доступ только к S1 через SSH на 22 порту

  4. На серверах S2 и S3 запущены Postgres на 5432 и HTTP на 80 портах соответственно.

  5. Я запускаю 2 туннеля и работаю с этими серверами как будто с локальными. Команды для запуска туннелей ниже:

ssh -p 22 -l 5432:s2:5432 s1
ssh -p 22 -l 80:s3:80 s1

Обратные туннели

Ок, мы разобрались как получить доступ к удаленному серверу, который находится в одной сети с доступным нам сервером. Но иногда бывают ситуацию, обратные этой. Например:

Когда клиент хочет увидеть

Когда клиент хочет увидеть «прям ща», а ты в кафе сидишь кодишь…

  1. Я работаю на своем ноутбуке и у меня крутится копия сайта клиента на 3000 порту

  2. Нахожусь при этом в кафе с бесплатным wifi

  3. Клиент находится у себя в офисе в закрытой сети

  4. Я хочу показать текущую версию сайта клиента прямо со своего ноутбука

  5. И я и клиент имеем доступ к одному серверу во внешней сети (например мой VPS)

Именно для такого кейса уже есть готовые решения — localtunnel или ngrok. Я сам ими пользуюсь, когда надо быстренько поднять и показать. Но иногда надо подключиться на удаленном сервере, например, к моей локальной базы для повторения какого-нибудь особого кейса.

Для решения этого вопроса нам как раз может помочь обратный туннель (Reverse port forwarding). Чтобы клиент мог видеть сайт на своем ПК, мне необходимо запустить следующую команду:

ssh -R 0.0.0.0:3001:127.0.0.1:3000 server.ru

2cdd7ee6e858467ba2ba1d73c63f3c53.gif

Давайте разберемся с каждым параметром в этой команде:

cb3b9e333fcaf7900262b2506a8959bc.png

Справа налево:

  1. Адрес сервера, который доступен и мне и клиенту

  2. Локальный адрес и порт, который я хочу сделать доступным (localhost:3000)

  3. Адрес и порт на общем сервере, на котором будет отображаться мой сайт с 3000 порту

  4. Флаг, передающий SSH серверу, что я намерен открыть обратный туннель на мой локальный порт

Что будет видеть клиент

Что будет видеть клиент

Клиент, после запуска такого туннеля, может видеть наш сайт по адресу server.ru:3001.

Правда сделать это может быть чуть сложнее чем «просто вбить одну команду». Дело в том, что «из коробки» sshd демон не допускает подключения на внешний интерфейс.

Другими словами — то, что мы указали »0.0.0.0» как адрес для прослушивания, говорит серверу следующее: «принимай любые соединения на любой интерфейс на порт 3001, даже если запрос идет извне». Но эта настройка, обычно, выключена. Для того, чтобы внешние соединения заработали необходимо в файле /etc/ssh/sshd_config добавить строчку GatewayPorts yes

Стоит быть крайне внимательным и аккуратным с этой настройкой. Ставя её в значение yes вы открываете порт всему миру и это может стать прекрасной дырой в безопасности для злоумышленников. ВСЕГДА ЗАКРЫВАЙТЕ ТУННЕЛИ после завершения работы через них.

Для чего еще можно использовать обратный туннель

Я использовал обратные туннели, также, при отладке проектов на PHP. В случае, когда код расположен на удаленном сервере и мы работаем с кодом через SFTP, бывает необходимость отладить код, который запущен на сервере, но через локальный XDebug. Тут на помощь также приходит SSH туннель, который запускается и дает доступ удаленному серверу к нашему ПК.

Вместо заключения

На этом все. Это была короткая сводка про те туннели, которые я использую в своей работе. Благодарю за внимание! Буду рад вашей подписки на меня в YouTube и Telegram

© Habrahabr.ru