
SSH — Проброс портов: прямые и обратные туннели
Приветствую!
Сегодня разберемся с пробросом портов через 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'
Хорошего дня!👋