База или админка слушает только 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). Так и задумано. Задача - попасть к сервису самим, не открывая порт всему миру.
Ключ -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 завершается с ошибкой, а не работает вхолостую.Ключ -L закрывает большинство задач, но у ssh есть ещё три приёма, и их полезно знать:
Ключ | Что делает | Когда нужен |
|---|---|---|
| Порт у вас ведёт к порту, который видит сервер | База, админка, мониторинг на сервере |
| Порт на сервере ведёт к порту у вас | Показать коллеге локальный сервис или принять вебхук на ноутбук |
| Локальный SOCKS-прокси: весь трафик приложения идёт через сервер | Выйти в сеть с адреса сервера |
| Войти на сервер через промежуточный (прыжковый) узел | Внутренние серверы без публичного адреса |
Покажем -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 строит на вашем компьютере, а прыжковый хост только передаёт байты.
-L 0.0.0.0:9000:..., иначе порт увидят все в вашей сети.-L можно дойти и до других машин во внутренней сети сервера. Давайте SSH-доступ только тем, кому доверяете.AllowTcpForwarding в sshd_config. Посмотреть действующие значения можно командой sudo sshd -T | grep -Ei "allowtcpforwarding|gatewayports|permitopen".У нас вышло:
gatewayports no означает, что порты, открытые ключом -R, слушают только сам сервер. Это правильная настройка по умолчанию, не меняйте её без причины. Остальные меры защиты свежего сервера описаны в инструкции по защите нового VPS.
Выполните ssh -N -L локальный_порт:127.0.0.1:порт_на_сервере user@адрес, не закрывайте окно и обращайтесь к 127.0.0.1:локальный_порт. Остановить туннель можно через Ctrl+C.
-L ведёт порт на вашем компьютере к сервису, который видит сервер. -R делает обратное: порт на сервере ведёт к вашему компьютеру. -D поднимает SOCKS-прокси, и весь трафик приложения идёт через сервер.
Пробросьте порт базы, например -L 15432:127.0.0.1:5432, и направьте клиент на 127.0.0.1:15432 с обычным логином и паролем базы. Саму базу в фаерволе открывать не нужно.
Запишите туннель в ~/.ssh/config с ServerAliveInterval 30 и ExitOnForwardFailure yes, запускайте с ключом -f. Для автоматического переподключения после обрыва используют отдельную программу или службу systemd, которая перезапускает ssh.
Да, трафик шифруется, пока вы не привязали локальный порт к 0.0.0.0 и не выдали SSH-доступ посторонним. Туннель даёт доступ ко всему, что видит сервер, поэтому ключи и пользователей нужно ограничивать.
ssh -N -L локальный:127.0.0.1:удалённый user@сервер.127.0.0.1 в середине команды означает сервер, а не ваш компьютер.ServerAliveInterval, ExitOnForwardFailure и запись в ~/.ssh/config делают туннель надёжным и коротким в вызове.-D даёт SOCKS-прокси, -J проводит через промежуточный узел; порты привязывайте к 127.0.0.1.