У вас на ноутбуке что-то работает — dev-сервер, API, домашняя машина за NAT — и до этого «чего-то» нужно достучаться с другого конца интернета. Белого IP нет, доступа к роутеру нет, времени поднимать VPN тоже нет. Для этого и нужен обратный SSH-туннель, ssh -R: он делает так, что нужный порт появляется на сервере, до которого вы достаёте, и незаметно перекидывает всё, что в него прилетает, обратно на вашу машину.
Этот флаг часто понимают задом наперёд, поэтому сначала выстроим правильную модель в голове, а затем разберём реальные случаи.
Модель в голове: -L наоборот
Любой SSH-туннель открывает слушающий порт на одной машине и переправляет всё, что в него приходит, туда, куда достаёт другая машина. При локальном пробросе (-L) порт открывается на вашей машине, а трафик выходит на стороне сервера. Обратный туннель — его точное зеркальное отражение:
При -R слушающий порт открывается на удалённом сервере, а подключения к нему выходят на вашей стороне.
Вот и весь фокус. Машина, которая не может принимать входящие подключения, вместо этого сама устанавливает исходящее SSH-соединение — и по нему впускает подключения обратно. Если -L и -R всё ещё путаются, полный семейный портрет собран в статье Проброс портов в SSH, разбор.
Синтаксис слева направо
ssh -R [bind_address:]remote_port:localhost:local_port user@server
Читается так: на сервере слушай remote_port; всё, что придёт, переправляй на localhost:local_port — как его видит моя машина. То есть:
ssh -R 8080:localhost:3000 deploy@vps.example.com
открывает порт 8080 на VPS, и каждое подключение к нему попадает на порт 3000 вашего ноутбука. Назначением не обязан быть сам ноутбук — -R 8080:192.168.1.50:80 перенаправит на другую машину в вашей локальной сети, используя ноутбук как точку выхода.
Полезный приём: -R 0:localhost:3000 позволяет серверу выбрать любой свободный порт и печатает Allocated port 43022 for remote forward, чтобы вы знали, какой именно достался.
Три вещи, которые на нём реально строят
Показать клиенту локальный dev-сервер. Ваша незавершённая работа крутится на localhost:3000. Вместо того чтобы разворачивать её где-то ради одного показа:
ssh -N -R 8080:localhost:3000 deploy@demo.example.com
Теперь каждый, кто достаёт до demo.example.com:8080, видит приложение на вашем ноутбуке в реальном времени. (-N отключает удалённую оболочку — вам нужен только туннель. Сможет ли подключиться кто угодно или только сам сервер, зависит от GatewayPorts — об этом в следующем разделе.)
Дотянуться до домашней машины за NAT. Ваш домашний компьютер не может принимать подключения, но может их устанавливать. Пусть он держит туннель наружу — к небольшому VPS:
# on the home machine
ssh -N -R 2222:localhost:22 tunnel@vps.example.com
Теперь порт 2222 на VPS — это и есть SSH-порт домашней машины, и откуда угодно:
ssh -J vps.example.com -p 2222 you@localhost
прыгает через VPS прямо на машину за NAT. Это классический self-hosted-ответ на вопрос как подключиться по SSH к домашнему серверу.
Доставить вебхуки на ноутбук. Платёжный провайдер или git-хостинг должен слать POST-запросы в ваш разрабатываемый обработчик. Пробросьте их достижимый эндпоинт обратно к вам:
ssh -N -R 9000:localhost:9000 deploy@vps.example.com
Направьте вебхук на VPS. Если туннель привязан к loopback-интерфейсу сервера (по умолчанию так и есть), поставьте на VPS nginx или любой обратный прокси перед 127.0.0.1:9000 — вы бесплатно получите терминацию TLS и ни разу не выставите наружу голый порт туннеля.
GatewayPorts: почему до вашего туннеля не достаёт никто другой
Сюрприз номер один: туннель работает, когда вы проверяете его с самого сервера (curl localhost:8080 отвечает), но снаружи подключиться не может никто. Так и задумано — по умолчанию sshd привязывает удалённо проброшенные порты только к 127.0.0.1, какой бы адрес привязки вы ни запросили.
Чтобы открыть туннель наружу, в /etc/ssh/sshd_config сервера нужно:
GatewayPorts clientspecified
затем перезапустить sshd и явно запросить привязку: ssh -R 0.0.0.0:8080:localhost:3000 .... (GatewayPorts yes привязывает к всем интерфейсам каждый удалённый проброс — это шире, чем обычно нужно.) Это решение на стороне сервера, и с клиента его не переопределить — намеренная настройка безопасности по умолчанию: обратный туннель — это дыра, пробитая изнутри, и всё, что он открывает, фактически смотрит в интернет. Относитесь к этому соответственно.
Ещё два серверных переключателя могут вас укусить: AllowTcpForwarding no полностью отключает туннели, а PermitListen может ограничивать, какие порты вправе занимать -R.
Как не дать ему умереть
Обратный туннель — это одно TCP-соединение, а простаивающие TCP-соединения умирают. Для всего, что дольше одного показа:
ssh -N -R 2222:localhost:22 \
-o ServerAliveInterval=30 \
-o ExitOnForwardFailure=yes \
tunnel@vps.example.com
ServerAliveInterval 30поддерживает трафик, чтобы записи в NAT и таймауты простоя не прибили соединение.ExitOnForwardFailure yesзаставляет ssh громко завершиться, если удалённый порт не удалось занять, вместо того чтобы всё равно подключиться и ничего не пробрасывать.autosshоборачивает команду и перезапускает туннель при каждом обрыве — стандартный инструмент для туннелей, которые должны переживать перезагрузки и нестабильную сеть; обычно запускается какautossh -M 0 -N -R ...под systemd-юнитом.
А когда туннель заслужил постоянное место, перенесите его в ~/.ssh/config строкой RemoteForward, чтобы он путешествовал вместе с записью хоста — структуру файла разбирает гайд по ssh_config (на английском):
Host homelink
HostName vps.example.com
User tunnel
RemoteForward 2222 localhost:22
ServerAliveInterval 30
ExitOnForwardFailure yes
-L, -R и -D вкратце
| Флаг | Порт открывается на | Трафик выходит на | Типичная задача |
|---|---|---|---|
-L | вашей машине | стороне сервера | подтянуть удалённую БД/дашборд на localhost |
-R | сервере | вашей стороне | открыть локальный сервис; дотянуться до машины за NAT |
-D | вашей машине | стороне сервера, любое назначение | SOCKS-прокси в сеть |
Когда обратный туннель — не тот инструмент
Обратный туннель — великолепный временный ответ: показы, отладка вебхуков, спасение машины за NAT. И плохой постоянный. Если то, что вы открываете, должно работать месяцами, обслуживать реальных пользователей или переживать сон ноутбука — это уже ingress, а ingress заслуживает настоящей инфраструктуры: обратного прокси на сервере, VPN или overlay-сети, нормального развёртывания. Прелесть туннеля в том, что это одна команда без постоянного следа в системе; как только вы пишете для него мониторинг — вы из него выросли.
Разбираем типичные сбои
Warning: remote port forwarding failed for listen port 8080— порт на сервере занят. Часто виновник — ваш собственный прошлый туннель: старый процесс sshd держит порт, пока мёртвое соединение не отвалится по таймауту. Keepalive-пакеты не дают зомби появиться;-R 0:...обходит конфликт;ExitOnForwardFailureпревращает предупреждение в ошибку, которую не пропустишь.- Работает с сервера, но снаружи отказ — слушатель сидит на loopback-интерфейсе сервера. Это умолчание
GatewayPortsделает своё дело: либо настройте его, либо прикройте порт обратным прокси. - Туннель подключается, но сервис не отвечает — проверьте локальную половину: действительно ли что-то слушает на указанном вами локальном порту?
-R 8080:localhost:3000без ничего на вашем:3000даст connection refused каждому посетителю. - Само SSH-соединение не устанавливается — это другая проблема со своим чек-листом: connection refused против timed out, разобрано.
Когда туннели становятся мебелью
Обратный туннель, который вы запускаете один раз, можно и набрать руками. Но тот, что держит вашу домашнюю лабораторию, демо-машину и эндпоинт для вебхуков, — это состояние, которое вы заново поднимаете каждое утро после каждого сна ноутбука и теряете из виду к четвергу. Менеджер сохранённых туннелей в Termalin держит локальные, удалённые и динамические пробросы привязанными к своему хосту: запускайте их кликом, с одного взгляда видьте, какие из них живы, и позволяйте им подниматься вместе с подключением, а не перенабираться из истории команд. Список хостов — вместе с ключами, jump-хостами, keepalive и туннелями — синхронизируется между вашими машинами со сквозным шифрованием. А когда машина оседает за NAT навсегда, её работающий только на исходящих Connector полностью заменяет самодельный обратный туннель — без входящего порта и без systemd-юнита autossh, за которым нужно присматривать. Командная строка учит, что значит -R; клиент избавляет туннели, на которые вы полагаетесь, от зависимости от вашей памяти.
Termalin — бесплатный кроссплатформенный SSH-клиент со встроенными туннелями, SFTP и менеджером ключей — скачайте его или посмотрите, как он безопасно обращается с ключами.