Сегодня разберемся с пробросом портов через SSH 🔐: как с помощью прямых и обратных (реверс) туннелей организовать доступ к локальным и удалённым сервисам.
🖐️Эй!
Подписывайтесь на наш телеграм @r4ven_me📱, чтобы не пропустить новые публикации на сайте😉. А если есть вопросы или желание пообщаться по тематике — заглядывайте в Вороний чат @r4ven_me_chat🧐.

Прямой проброс порта
Например, нам нужно на локальной машине подключиться к БД💽, доступной, только на localhost интерфейсе удаленного сервера🖥.
Открыть SSH-туннель можно такой командой:
ssh -q -f -N -L 127.0.0.1:5432:localhost:5432 ivan@test.r4ven.meГде:
-q— тихий режим (выводить только ошибки);-f— запуск SSH в фоновом режиме;-N— без выполнения команд (только туннель);-L— проброс локального порта.
Сперва идёт адрес:порт локального хоста, затем адрес:порт удаленного.
Данная команда откроет туннель в фоновом режиме и будет работать перманентно.
Теперь можно подключаться к БД по адресу 127 0.0.1:5432
Если это ваша цель, то задача решена👌.
Но что, если вам необходимо открыть туннель только на время выполнения некоторых манипуляций? Например, при настройке автоматизации с помощью сценариев оболочки📝. Очевидно, в таком случае по завершении работы туннель необходимо закрыть🔒.
К сожалению у SSH нет встроенного функционала для предоставления PID ‘а фонового процесса😢.
Поэтому закрыть туннель можно топорным способом, через pkill 🎯, передав в качестве аргумента полную команду открытия туннеля😒.
Пример:
pkill -f "ssh -q -f -N -L 127.0.0.1:5432:localhost:5432 ivan@test.r4ven.me"
# или
pgrep -f "ssh -q -f -N -L 127.0.0.1:5432:localhost:5432 ivan@test.r4ven.me" | xargs killДанный способ хоть и рабочий, но не совсем элегантный. Да и небезопасен: если допустить ошибку в аргументе, можно случайно прибить другой рабочий процесс🤷♂
Поиск альтернативного решения занял долгое какое-то время⏳
И вот его пример:
ssh -q -f -L 127.0.0.1:5432:localhost:5432 test.r4ven.me sleep 60Суть его в том, что мы открываем туннель на 60 сек. В течение этого времени необходимо запустить нужную вам команду🐧.
Даже, если выполнение вашей прикладной команды займет больше минуты, соединение не прервется ДО её завершения😌. Другими словами, туннель корректно закроется автоматически👍.
Ниже приведу вариант команды, который я использовал в одном из скриптов:
ssh -q -f -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ExitOnForwardFailure=yes -L 127.0.0.1:5432:localhost:5432 test.r4ven.me -p 2222 -l ivan -i ~/.ssh/id_ed25519_test sleep 60Дополнительные опции:
StrictHostKeyChecking=no— отключает проверку host key (⚠️автоматизация в ущерб безопасности);UserKnownHostsFile=/dev/null— не сохранять host key в~/.ssh/known_hosts;ExitOnForwardFailure=yes— выход, если проброс порта не удался.
Возможно пригодится кому для своих shell 🐚 скриптов🙃.
Обратный проброс порта
Теперь погорим про реверс, они же обратные, туннели🌐.
Например нужно направить данные с порта 4443 удаленной машины на порт 5001 локальной:
ssh -q -f -N -R 127.0.0.1:4443:localhost:5001 ivan@test.r4ven.meНапомню, что:
-q— тихий режим (только ошибки);-f— запуск SSH в фоновом режиме;-N— без выполнения команд (только туннель);-R— обратный проброс (удалённого порта до локального).
Сперва идёт адрес:порт удалённого хоста, затем адрес:порт локального.
Если что, то закрыть соединение можно такой топорной командой:
pkill -ef 'ssh -q -f -N -R 127.0.0.1:4443:localhost:5001 ivan@test.r4ven.me'Как вы заметили, мы пробросили порт удаленной машины, ожидающий подключений на локальном интерфейсе. Для проброса не localhost интерфейса явно укажите его. Или пробросьте все интерфейсы:
ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.meС высокой вероятностью, если на удаленной машине выполнить:
ss -tln | grep 4443Вы увидите:
Local Address:Port
127.0.0.1:4443Удаленный порт все равно слушает локальный интерфейс🤔

Дело тут в сецуре шел, который не был бы сецуре, если не имел бы сецурных параметров по умолчанию😎.
В целях безопасности обратные пробросы портов на не локальном интерфейсе отключены. За это отвечает параметр GatewayPorts в конфгие SSH🔒.
Чтобы разрешить обратные пробросы открываем конфиг sshd:
nvim /etc/ssh/sshd_configИ устанавливаем параметр GatewayPorts в значение yes.
❗️ Осторожно
⚠️ Помните, что это потенциальная дыра в вашей системе!
После чего перезапускаем демон sshd:
sshd -t
systemctl restart sshd💡 Совет
☝️Если используете фаервол, не забудьте добавить разрешающее правило. Пример для ufw:
ufw allow 4443Переоткрываем туннель:
pkill -ef 'ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.me'
ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.meИ вновь проверяем порт на удаленной машине:
ss -tln | grep 4443Всё как надо:
Local Address:Port
0.0.0.0:4443Теперь при подключении с любого клиента вы будете перенаправлены на вашу локальную систему:
nc -zv test.r4ven.me 4443
curl test.r4ven.me:4443⚠️Предупреждение
❗️Не забудьте закрыть соединение после всех манипуляций на локальной машине:
pkill -ef 'ssh -q -f -N -R 0.0.0.0:4443:localhost:5001 ivan@test.r4ven.me'Хорошего дня!👋


