21 октября 2026 г.

SSH-туннель: как достать закрытый порт сервера, не открывая его в интернет

База или админка слушает только localhost, и это правильно. Как открыть её у себя на экране через ssh -L, не публикуя порт.

Правильная база данных на сервере слушает только 127.0.0.1: снаружи её не видно, и подобрать пароль к ней некому. Но однажды её надо открыть с ноутбука: посмотреть таблицу, снять дамп, зайти в админку. Открывать порт в фаерволе ради этого не нужно. Достаточно SSH-туннеля, и соединение пойдёт через тот же канал, что и ваш вход на сервер.

Коротко. ssh -N -L 9000:127.0.0.1:8000 user@сервер делает так, что порт 9000 на вашем компьютере ведёт к порту 8000 на сервере. Дальше открываете http://127.0.0.1:9000 и работаете, как будто сервис стоит у вас. Порт на сервере остаётся закрытым для интернета. Ключ -D превращает сервер в SOCKS-прокси, -J проводит через промежуточный узел, а ServerAliveInterval и ExitOnForwardFailure помогают держать туннель живым.

Зачем нужен туннель

Допустим, на сервере работает внутренний сервис: админка, база, Redis, мониторинг. Он слушает только 127.0.0.1 (адрес «сам на себя»), и это сознательно: кто не может подключиться, тот не может и атаковать. Проверить, на каком адресе слушает порт, можно командой ss, о которой мы подробно писали в статье про ss:

ss -tln | grep ":8000"

Нас интересует адрес в четвёртом столбце. У нашего тестового сервиса вышло LISTEN 0 5 127.0.0.1:8000 0.0.0.0:*. Это значит «слушаю только свой компьютер». Попробуем достучаться снаружи, с другой машины, по публичному адресу:

curl -m 6 http://203.0.113.10:8000/

Ответа нет, запрос заканчивается по таймауту (код 28). Так и задумано. Задача - попасть к сервису самим, не открывая порт всему миру.

Шаг 1. Строим туннель командой ssh -L

Ключ -L (local) связывает порт на вашем компьютере с адресом, который видит сервер. Команда выглядит так:

ssh -N -L 9000:127.0.0.1:8000 [email protected]

Разберём по частям, потому что именно эту строку все путают:

  • -N - не запускать на сервере оболочку, только держать туннель. Окно «зависнет» без приглашения; это нормально, туннель работает, пока вы не нажмёте Ctrl+C.
  • 9000 - порт на вашем компьютере. Любой свободный выше 1024.
  • 127.0.0.1:8000 - куда идти с точки зрения сервера. Здесь 127.0.0.1 означает сам сервер, а не ваш ноутбук. Это главный источник путаницы.
  • [email protected] - обычный адрес для входа по SSH.

Не закрывая это окно, откройте второе и обратитесь к порту 9000 у себя:

curl -i http://127.0.0.1:9000/

Страница пришла с сервера, а порт 8000 так и остался закрытым для интернета. Тот же приём годится для любого сервиса. Например, чтобы подключиться к PostgreSQL с ноутбука, пробросьте порт и направьте клиент на локальный конец туннеля:

ssh -N -L 15432:127.0.0.1:5432 [email protected]

В соседнем окне psql -h 127.0.0.1 -p 15432 -U имя_пользователя имя_базы подключится к базе на сервере. Локальный порт мы взяли другой (15432), чтобы не спорить с PostgreSQL на самом ноутбуке, если он там стоит.

Если туннель не работает

Две ошибки встречаются чаще остальных, и обе читаются с одного взгляда.

Порт на вашем компьютере занят. Предыдущий туннель ещё жив, или порт держит другая программа. Запустите ssh с -o ExitOnForwardFailure=yes, и он честно скажет, что не вышло, вместо того чтобы молча сидеть без туннеля:

Выберите другой порт или найдите старый процесс ssh и завершите его.

На сервере по этому адресу ничего не слушает. Туннель поднялся, curl получает разрыв соединения (код 56), а сам ssh пишет в окно, где работает туннель:

channel 2: open failed: connect failed: Connection refused

Сообщение значит «до сервера я дошёл, а сервис на указанном порту не отвечает». Проверьте номер порта и то, что сервис запущен, командой ss -tln на самом сервере. Ошибка «Connection refused» относится к дальнему концу, не к вашему компьютеру.

Держим туннель живым и запоминаем настройки

Туннель ломается тихо: домашний роутер сбросил соединение, ноутбук заснул, и окно с ssh -N вроде открыто, а толку нет. Три настройки решают проблему. Вместо длинной команды запишите их в ~/.ssh/config (на Windows файл лежит в C:\Users\имя\.ssh\config):

Теперь туннель поднимается командой ssh -N db-tunnel, а добавив -f, вы отправите его в фон. Что делают строки:

  • LocalForward - то же, что -L, только записанное в файл.
  • ServerAliveInterval 30 - каждые 30 секунд посылать серверу «ты тут?». Если трижды (ServerAliveCountMax 3) никто не ответил, соединение закрывается, и вы сразу видите обрыв, а не часами смотрите на мёртвый туннель.
  • ExitOnForwardFailure yes - если порт не удалось занять, ssh завершается с ошибкой, а не работает вхолостую.

Другие виды туннелей: -D, -R и -J

Ключ -L закрывает большинство задач, но у ssh есть ещё три приёма, и их полезно знать:

Ключ

Что делает

Когда нужен

-L

Порт у вас ведёт к порту, который видит сервер

База, админка, мониторинг на сервере

-R

Порт на сервере ведёт к порту у вас

Показать коллеге локальный сервис или принять вебхук на ноутбук

-D

Локальный SOCKS-прокси: весь трафик приложения идёт через сервер

Выйти в сеть с адреса сервера

-J

Войти на сервер через промежуточный (прыжковый) узел

Внутренние серверы без публичного адреса

Покажем -D в деле. Эта команда поднимает прокси на порту 1080 у вас на компьютере:

ssh -f -N -D 1080 [email protected]

Дальше любая программа, которую научили ходить через SOCKS, выйдет в сеть с адреса сервера. curl умеет это с ключом --socks5-hostname; он же отправляет через сервер и DNS-запросы:

curl --socks5-hostname 127.0.0.1:1080 https://ifconfig.me

Вернулся адрес сервера, 203.0.113.10, а не ваш домашний. Это тоже удобный способ проверить, как сайт выглядит из другой страны, если сервер стоит там.

Для -J (ProxyJump) удобнее описать оба узла в конфиге, потому что ключ -i с командной строки на промежуточный узел не передаётся:

Теперь ssh inner сам зайдёт на jump и оттуда на 10.0.0.5. Ключ не нужно копировать на промежуточный сервер: соединение с внутренним узлом ssh строит на вашем компьютере, а прыжковый хост только передаёт байты.

Безопасность: что туннель может и чего не должен

  • Привязка к 127.0.0.1. По умолчанию локальный конец туннеля слушает только ваш компьютер. Не пишите -L 0.0.0.0:9000:..., иначе порт увидят все в вашей сети.
  • Туннель - доступ ко всему, что видит сервер. Через -L можно дойти и до других машин во внутренней сети сервера. Давайте SSH-доступ только тем, кому доверяете.
  • Ограничения на сервере. За проброс отвечает параметр AllowTcpForwarding в sshd_config. Посмотреть действующие значения можно командой sudo sshd -T | grep -Ei "allowtcpforwarding|gatewayports|permitopen".

У нас вышло:

gatewayports no означает, что порты, открытые ключом -R, слушают только сам сервер. Это правильная настройка по умолчанию, не меняйте её без причины. Остальные меры защиты свежего сервера описаны в инструкции по защите нового VPS.

Частые вопросы

Как сделать SSH-туннель к серверу?

Выполните ssh -N -L локальный_порт:127.0.0.1:порт_на_сервере user@адрес, не закрывайте окно и обращайтесь к 127.0.0.1:локальный_порт. Остановить туннель можно через Ctrl+C.

Что делают ключи -L, -R и -D?

-L ведёт порт на вашем компьютере к сервису, который видит сервер. -R делает обратное: порт на сервере ведёт к вашему компьютеру. -D поднимает SOCKS-прокси, и весь трафик приложения идёт через сервер.

Как подключиться к базе через SSH-туннель?

Пробросьте порт базы, например -L 15432:127.0.0.1:5432, и направьте клиент на 127.0.0.1:15432 с обычным логином и паролем базы. Саму базу в фаерволе открывать не нужно.

Как держать туннель постоянно?

Запишите туннель в ~/.ssh/config с ServerAliveInterval 30 и ExitOnForwardFailure yes, запускайте с ключом -f. Для автоматического переподключения после обрыва используют отдельную программу или службу systemd, которая перезапускает ssh.

Безопасен ли проброс портов через SSH?

Да, трафик шифруется, пока вы не привязали локальный порт к 0.0.0.0 и не выдали SSH-доступ посторонним. Туннель даёт доступ ко всему, что видит сервер, поэтому ключи и пользователей нужно ограничивать.

Итог

  • Закрытый от интернета сервис на сервере достаётся одной командой ssh -N -L локальный:127.0.0.1:удалённый user@сервер.
  • 127.0.0.1 в середине команды означает сервер, а не ваш компьютер.
  • «Address already in use» - занят локальный порт, «Connection refused» - на сервере по этому порту никто не слушает.
  • ServerAliveInterval, ExitOnForwardFailure и запись в ~/.ssh/config делают туннель надёжным и коротким в вызове.
  • -D даёт SOCKS-прокси, -J проводит через промежуточный узел; порты привязывайте к 127.0.0.1.
PUBLISHED
21 октября 2026 г.
AUTHOR
HIP-HOSTING
LANGUAGES
EN · RU