SSH-Туннели простыми словами
В какой-то момент у меня возникла необходимость разобраться с простыми SSH-туннелями: как из запускать и какие туннели могут помочь мне (обычному web-разработчику). Разобраться в этом удалось и я решил поделиться пояснениями в простой понятной форме.
Сразу важная мысль: основу для для этого поста в свою голову впитал из этой статьи, за что автору огромное спасибо! Эта статья чуть более хардкорная для понимания, но куда более емкая, чем моя. Мой вариант для тех, кто в этом почти ничего не понимает и хочет разобраться с картинками и деталями.
Если вам более удобен видео-формат, то можете посмотреть его на YouTube.
С чего возникла необходимость в туннелях?
Я применяю, так или иначе, SSH туннели регулярно в своей работе. Иногда необходимо получить доступ к ресурсу, базе данных или сервису, который находится в закрытой сети, но у меня есть SSH доступ к одному из серверов в этой сети.
Все описанное ниже является исключительно интерпретацией моего опыта и изучения различных статей по теме. Буду очень признателен, если вы найдете ошибку и сообщите мне об этом через в комментариях. Спасибо!
Мое первое знакомство с туннелями произошло при моей попытке развернуть разработанный проект на сервере клиента. Я приехал к заказчику в офис, заранее сохранив в заметки ссылки на наши репозитории, названия докер образов и тд.
Но меня ждал сюрприз. Клиент просто дал мне клавиатуру, показал монитор и сказал «на, разворачивай». По политике безопасности клиента я не мог получить удаленный доступ к этому серверу, чтобы удобно заливать все из своего офиса. Поэтому мне предстояло руками разворачивать все на этом сервере, вводя все ссылки и названия по букве.
Я с ужасом представил, как я буду руками вбивать каждую ссылку для репозитория и понял, что на это все уйдет ни один час.
Я решил не тратить на это ни минуты и потратил достаточно времени, чтобы подключить этот сервер к нашей openvpn сети, чтобы потом спокойно заходить на этот сервер удаленно. Это было достаточно муторно, но своей цели я добился и релизил потом уже удаленно.
Решив эту задачу я стал изучать другие, более упрощенные способы, решения этой задачи. Так я познакомился с туннелями.
Основная идея туннелей
Если нельзя напрямую, то почему бы не пойти в обход?
В целом суть туннеля наглядно изображена на анимации выше. Но дополню текстом:
У вас есть сервер, к которому вы хотите получить доступ, но не можете ходить на него напрямую. Назовем его сервер 1;
Между вами и целевым сервером есть сервер, который имеет доступ к целевому и к которому имеете SSH доступ вы. Назовем его сервер 2;
Вы можете зайти по SSH на сервер 2 и, через консоль, уже отправлять любые запрос на сервер 1. Также вы можете зайти по SSH на сервер 1 и двигаться дальше.
Примерно так и работает SSH-туннели. Только нам не всегда обязательно заходить на один сервер и с него уже слать запросы на следующий. Для упрощения этого процесса мы можем воспользоваться переадресацией портов (Port Forwarding).
Port forwarding на пальцах
У меня ушло какое-то количество мыслетоплива, чтобы понять суть работы переадресации портов.
Пишем порт 81, 80 в уме
Расширим ситуацию, описанную выше:
У нас есть сервер S1, к которому у нас есть доступ на любой порт;
Есть сервер S2, к 80 порту которого хотим получить доступ мы через наш S1;
Мы одной командой можем открыть на S1 80 порт, чтобы он пересылал эти запросы на 80 порт S2 и потом передавал нам ответ;
Также, например, одной командой, мы можем открыть 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 порту удаленного сервера. Подробнее что-есть-что в этой команде:
Первым параметром идет флаг »-L» который указывает на то, что нам необходимо открыть туннель и начать слушать порт.
Далее указываем интерфейс на котором мы хотим слушать (по-умолчанию localhost и в рамках данного примера он не указан). В нашем случае указываем порт 80 (на рис. «локальный порт»).
Далее указываем куда надо будет подключаться на удаленном сервере. В нашем примере 127.0.0.1, что значит, что туннель будет пробрасывать на сам же удаленный сервер. На что можно заменять это значение можно прочитать ниже.
Порт на удаленном сервере — в нашем случае это 80. Если бы nginx, например, видел бы на 8080, то мы бы указали 8080 в этом месте.
Адрес удаленного сервера. Указываем его тем же самым образом, что и при обычном подключении к SSH.
Теперь можно переходить на localhost в браузере и видеть то, что на сервере на 80 порту. В моем примере можно увидеть только ошибку nginx, которую выдает nginx-proxy на сервере.
Кейс с базой данных Postgres в докере
Теперь к еще одному полезному кейсу, с которым мы сталкиваемся во время разработки, так или иначе, регулярно.
Есть я и мой ноутбук
На ноутбуке есть код моего backend приложения
В основном я работаю с локальной копией базы данных во время разработки
По какой-то причине мне надо подключиться к базе данных, которая находится на сервере (тестовом или боевом) в изолированной сети Docker.
Окольными путями напрямую в базу
Когда мы говорим про работу через IDE для работы с БД, мы можем легко воспользоваться встроенным механизмом туннелей (инструкция по продуктам от JetBrains тут). Но трудности возникают если мы хотим из кода нашего приложения подключиться к этой базе.
Кстати, этот момент актуален не только для базы данных. Иногда нужно просто подключаться к разным сервисам, которые находятся в изолированной сети, куда мы можем иметь доступ через SSH. Так что, этот пример лишь показательный и область применения крайне обширна
Для решения этого вопроса достаточно вбить следующую команду:
ssh -L 5432:postgres-db:5432 remote.server.address
Тут ситуация аналогична предыдущей. Только вместо локального ip адреса самого сервера мы указываем ip адрес (в данном случае имя хоста postgres-db
) в качестве целевого адреса.
В случае работы с Docker я использую этот контейнер, чтобы знать адреса всех контейнеров на сервере. После запуска имя хоста каждого контейнера можно получить в файле
/etc/hosts
. И после перезапуска ip адрес меняется, а хост остается
Дальше я уже могу указывать localhost:5432
в качестве сервера postgres в моем приложении и подключусь уже напрямую к базе данных в docker контейнере на том сервере.
Таких туннелей можно запускать сколько угодно. Все запускаются по аналогии. Например можно построить такую схему:
несколько туннелей на через один сервер
Есть сервера S1, S2, S3.
S1 имеет доступ к S2 и S2
Я имею доступ только к S1 через SSH на 22 порту
На серверах S2 и S3 запущены Postgres на 5432 и HTTP на 80 портах соответственно.
Я запускаю 2 туннеля и работаю с этими серверами как будто с локальными. Команды для запуска туннелей ниже:
ssh -p 22 -l 5432:s2:5432 s1
ssh -p 22 -l 80:s3:80 s1
Обратные туннели
Ок, мы разобрались как получить доступ к удаленному серверу, который находится в одной сети с доступным нам сервером. Но иногда бывают ситуацию, обратные этой. Например:
Когда клиент хочет увидеть «прям ща», а ты в кафе сидишь кодишь…
Я работаю на своем ноутбуке и у меня крутится копия сайта клиента на 3000 порту
Нахожусь при этом в кафе с бесплатным wifi
Клиент находится у себя в офисе в закрытой сети
Я хочу показать текущую версию сайта клиента прямо со своего ноутбука
И я и клиент имеем доступ к одному серверу во внешней сети (например мой VPS)
Именно для такого кейса уже есть готовые решения — localtunnel или ngrok. Я сам ими пользуюсь, когда надо быстренько поднять и показать. Но иногда надо подключиться на удаленном сервере, например, к моей локальной базы для повторения какого-нибудь особого кейса.
Для решения этого вопроса нам как раз может помочь обратный туннель (Reverse port forwarding). Чтобы клиент мог видеть сайт на своем ПК, мне необходимо запустить следующую команду:
ssh -R 0.0.0.0:3001:127.0.0.1:3000 server.ru
Давайте разберемся с каждым параметром в этой команде:
Справа налево:
Адрес сервера, который доступен и мне и клиенту
Локальный адрес и порт, который я хочу сделать доступным (localhost:3000)
Адрес и порт на общем сервере, на котором будет отображаться мой сайт с 3000 порту
Флаг, передающий SSH серверу, что я намерен открыть обратный туннель на мой локальный порт
Что будет видеть клиент
Клиент, после запуска такого туннеля, может видеть наш сайт по адресу server.ru:3001.
Правда сделать это может быть чуть сложнее чем «просто вбить одну команду». Дело в том, что «из коробки» sshd демон не допускает подключения на внешний интерфейс.
Другими словами — то, что мы указали »0.0.0.0» как адрес для прослушивания, говорит серверу следующее: «принимай любые соединения на любой интерфейс на порт 3001, даже если запрос идет извне». Но эта настройка, обычно, выключена. Для того, чтобы внешние соединения заработали необходимо в файле /etc/ssh/sshd_config
добавить строчку GatewayPorts yes
Стоит быть крайне внимательным и аккуратным с этой настройкой. Ставя её в значение
yes
вы открываете порт всему миру и это может стать прекрасной дырой в безопасности для злоумышленников. ВСЕГДА ЗАКРЫВАЙТЕ ТУННЕЛИ после завершения работы через них.
Для чего еще можно использовать обратный туннель
Я использовал обратные туннели, также, при отладке проектов на PHP. В случае, когда код расположен на удаленном сервере и мы работаем с кодом через SFTP, бывает необходимость отладить код, который запущен на сервере, но через локальный XDebug. Тут на помощь также приходит SSH туннель, который запускается и дает доступ удаленному серверу к нашему ПК.
Вместо заключения
На этом все. Это была короткая сводка про те туннели, которые я использую в своей работе. Благодарю за внимание! Буду рад вашей подписки на меня в YouTube и Telegram