Прямой ответ
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 висит молча. Ключи и их перенос — по правилам статьи про секреты.
Типовые ошибки
- «bind [localhost]:5433: Address already in use» — локальный порт занят (часто — забытым прошлым туннелем):
lsof -i :5433/Get-NetTCPConnection -LocalPort 5433. - «channel open failed: administratively prohibited» — на сервере выключен
AllowTcpForwardingили (jump-сценарий) промежуточный хост не разрешает проброс. - Туннель «отваливается» через минуту простоя — нет keepalive:
ServerAliveInterval 30. - Приложение не видит туннель — оно резолвит
localhostв IPv6::1, а ssh слушает IPv4: используйте-L 127.0.0.1:5433:...явно или-6/-4. - 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 (ссылки в шапке).