termal.in

← Блог

Обратные SSH-туннели (ssh -R): проброс локального порта через удалённый сервер

· Termalin Team sshport-forwardingtunnelingtutorial

У вас на ноутбуке что-то работает — 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 и менеджером ключей — скачайте его или посмотрите, как он безопасно обращается с ключами.

Попробуйте на одном хосте.

Termalin — быстрый SSH-клиент для вас и ваших агентов.

Бесплатный тариф · 14 дней Pro бесплатно · цены