Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → SSH-туннели и port forwarding: доступ к удалённым сервисам безопасно

SSH-туннели для разработчика: локальный, удалённый и динамический forwarding на примерах

Как пользоваться SSH-туннелями: -L для доступа к удалённой БД как к локальной, -R для демонстрации, SOCKS-прокси, мультиплексирование соединений, безопасность и типовые ошибки.

Прямой ответ

SSH-туннель — это проброс порта через зашифрованное SSH-соединение, и у разработчика три рабочих сценария. Локальный forwarding (-L): удалённый сервис становится «локальным» — ssh -L 5433:db.internal:5432 dev@server, и ваша БД за файрволом доступна на localhost:5433 без единого открытого наружу порта. Удалённый forwarding (-R): ваш локальный сервис виден на сервере — быстрый способ показать коллеге dev-сервер без ngrok-подобных сервисов. Динамический (-D): SOCKS-прокси для всего трафика через сервер. Всё это — штатная функция OpenSSH, не требующая ни настроек сервера сверх AllowTcpForwarding (включён по умолчанию), ни дополнительных инструментов.

Локальный forwarding: доступ к сервисам за файрволом

Классика — база данных, которая по соображениям безопасности не смотрит в интернет:

ssh -L 5433:db.internal:5432 dev@bastion -N
psql -h localhost -p 5433 -U app appdb

Разбор аргументов: 5433 — порт на вашей машине (не 5432, чтобы не конфликтовать с локальным PostgreSQL из статьи про локальную БД); db.internal:5432 — цель с точки зрения сервера; -N — «туннель без shell» (ничего не исполнять удалённо). Полезные добавки: -f (увести в фон), и запись в ~/.ssh/config:

Host bastion-tunnel
  HostName bastion.example.com
  User dev
  LocalForward 5433 db.internal:5432
  ExitOnForwardFailure yes
  ServerAliveInterval 30

Теперь туннель поднимается командой ssh bastion-tunnel. ExitOnForwardFailure yes важен: без него ssh при занятом локальном порту молча продолжит сессию без проброса, и вы будете отлаживать «подключение к БД», которого нет.

Удалённый forwarding: показать локальное наружу

Обратная задача — ваш dev-сервер на localhost:3000 должен быть доступен на сервере (для вебхука, демонстрации, интеграции):

ssh -R 8080:localhost:3000 dev@server -N
# на сервере: curl  → ваш ноутбук

Нюансы: порт 8080 слушает на loopback сервера (доступен только с него) — для доступа извне нужен GatewayPorts в sshd и осознание рисков; соединение живёт, пока жив ssh-процесс (для постоянства — autossh или systemd-юнит на сервере). Сценарий с вебхуками: сервер-посредник с публичным адресом получает вызовы и через туннель отдаёт их на вашу машину — приватнее публичных туннель-сервисов.

Динамический forwarding и SOCKS

ssh -D 1080 dev@server -N
# браузер/CLI через SOCKS5 localhost:1080
curl --socks5-hostname localhost:1080 

Весь трафик идёт через сервер — удобно для доступа к внутренним ресурсам из кофе; помните, что это не VPN (приложения должны уметь SOCKS), и что трафик до сервера шифрован, а от сервера к целям — как обычно.

Мультиплексирование: одно соединение на всё

Каждый ssh — новое соединение с рукопожатием и, возможно, 2FA. ControlMaster решает:

Host *
  ControlMaster auto
  ControlPath ~/.ssh/cm-%r@%h:%p
  ControlPersist 10m

Первое соединение создаёт мастер-сокет, последующие (scp, git, новые туннели, VS Code Remote) переиспользуют его мгновенно. На практике это меняет опыт работы с удалённой машиной: git push не спрашивает пароль заново, туннели открываются за секунду. Общая настройка подключений и remote-сценарии — в статье про SSH и VS Code Remote.

Безопасность: коротко и по делу

Туннель наследует безопасность SSH: ключи вместо паролей, passphrase на ключе, актуальный OpenSSH. Специфические правила: не оставляйте -R-туннели на публичные интерфейсы без нужды (GatewayPorts no — дефолт и пусть им остаётся); -L-туннель даёт любому процессу на вашей машине доступ к удалённому сервису — учитывайте, если ноутбук скомпрометирован; в общих сетях включайте ServerAliveInterval — мёртвый туннель без keepalive висит молча. Ключи и их перенос — по правилам статьи про секреты.

Типовые ошибки

  1. «bind [localhost]:5433: Address already in use» — локальный порт занят (часто — забытым прошлым туннелем): lsof -i :5433 / Get-NetTCPConnection -LocalPort 5433.
  2. «channel open failed: administratively prohibited» — на сервере выключен AllowTcpForwarding или (jump-сценарий) промежуточный хост не разрешает проброс.
  3. Туннель «отваливается» через минуту простоя — нет keepalive: ServerAliveInterval 30.
  4. Приложение не видит туннель — оно резолвит localhost в IPv6 ::1, а ssh слушает IPv4: используйте -L 127.0.0.1:5433:... явно или -6/-4.
  5. Git через туннель — remote вида ssh://git@localhost:2222/... работает только пока жив туннель; для постоянной схемы держите профиль в ssh config, а не ad-hoc команды.

FAQ

Туннель или VPN — что выбрать? Для доступа к 2–3 сервисам туннель проще и точнее (проброшены конкретные порты, остальной трафик не затрагивается). VPN нужен, когда требуется вся сеть целиком (обнаружение сервисов, multicast, произвольные адреса).

autossh — обязателен ли? Для интерактивной работы нет (ControlPersist + ServerAliveInterval покрывают большинство случаев). Для «вечно живущего» туннеля — да, либо systemd-сервис с Restart=always.

Как пробросить несколько портов сразу? Повторяйте -L: ssh -L 5433:db:5432 -L 6380:cache:6379 dev@server -N, либо несколько строк LocalForward в ssh config для хоста.

Работает ли это на Windows? Да: штатный OpenSSH-клиент Windows 10/11 поддерживает все режимы; конфиг — %USERPROFILE%\.ssh\config, синтаксис тот же.

Jump-хосты: туннель через промежуточный сервер

Частый корпоративный топологический случай: целевой сервис доступен только с bastion-сервера, а на него — только по SSH с вашей машины. Современные версии OpenSSH решают это одной опцией без ручных цепочек:

Host target
  HostName 10.0.5.20
  User app
  ProxyJump dev@bastion.example.com

ssh target — и вы на целевой машине через bastion прозрачно; -L-пробросы работают поверх той же схемы (ssh -L 5433:localhost:5432 target через jump). Преимущества против ручной цепочки ssh -t bastion ssh target: один набор ключей, работающий scp/rsync/git поверх, совместимость с VS Code Remote. Для постоянных цепочек добавьте ControlMaster — рукопожатие через bastion дорогое, мультиплексирование делает повторные подключения мгновенными.

Понимание этой схемы полезно ещё и для безопасности: bastion остаётся единственной точкой входа, целевые машины не торчат наружу, а весь трафик шифрован end-to-end от вашего ноутбука до цели — промежуточный сервер не видит содержимое.

Как сделать туннель, который переживёт перезагрузку ноутбука? Опишите подключение в ssh config, а автоподъём оформите системным механизмом: systemd-юнит с Restart=always на Linux, launchd на macOS, служба через NSSM или Планировщик задач на Windows. Для простых случаев хватает привычки «утром поднял» — автоматизация нужна, когда туннель критичен для фоновых процессов.

Видит ли работодатель мой трафик через туннель? Туннель шифрует содержимое, но факт соединения с сервером виден сетевому администратору; на корпоративной машине трафик может логироваться до уровня процессов. Использовать туннели для обхода корпоративных политик — плохая идея и технически, и организационно.

Источники: man ssh/ssh_config OpenSSH, RFC 4254 (ссылки в шапке).