Прим. переводчика: автор статьи рассматривает практические сценарии и примеры организации SSH-туннелей. А для более наглядного объяснения того, как это работает, графически показывает потоки трафика.

Туннели SSH — это зашифрованные TCP-соединения между клиентами и серверами SSH. Трафик входит с одной стороны туннеля и прозрачно выходит с другой. Изначально этот термин относился к туннелям на виртуальных сетевых интерфейсах TUN/TAP, однако сейчас так обычно называют проброс портов SSH.

Например, вот так будет выглядеть схема обратного туннеля, в котором обращение к порту 80 SSH-клиента выполняется через SSH-сервер с определенного IP-адреса (1.2.3.4):

Здесь и далее все схемы взяты из оригинала статьи
Здесь и далее все схемы взяты из оригинала статьи

Сценарии использования туннелей*:

  • Предоставление зашифрованных каналов для протоколов, передающих данные «открытым текстом».

  • Открытие бэкдоров в частные сети.

  • Обход межсетевых экранов.

*. Примечание

Перечень дополняется. Не стесняйтесь предлагать примеры или исправления
на GitHub.

Проброс портов

Перенаправляет порт из одной системы (локальной или удаленной) в другую.

Проброс локального порта

Проброс локальных портов позволяет перенаправить трафик с SSH-клиента на целевой хост через SSH-сервер. Другими словами, можно получить доступ к удаленным сервисам по зашифрованному соединению так, как будто они локальные. Примеры использования:

  • доступ к удаленному сервису (Redis, Memcached и т. д.) по внутреннему IP-адресу;

  • локальный доступ к ресурсам, размещенным в частной сети;

  • прозрачное проксирование запроса к удаленному сервису.

Проброс удаленного порта

Проброс удаленного порта позволяет направить трафик с SSH-сервера в пункт назначения либо через SSH-клиент, либо через другой удаленный хост. Открывает пользователям в публичных сетях доступ к ресурсам в частных сетях. Примеры использования:

  • открытие доступа к локальному серверу разработки через публичную сеть;

  • предоставление доступа с ограничением по IP-адресу к удаленному ресурсу в частной сети.

Динамический проброс портов

При динамической переадресации портов на SSH-клиенте поднимается SOCKS-прокси для перенаправления TCP-трафика через SSH-сервер на удаленный хост.

Пересылка с системных (privileged) портов

Для пересылки трафика с системных портов (1–1023) необходимо запустить SSH с правами суперпользователя в системе, на которой открывается порт.

sudo ssh -L 80:example.com:80 ssh-server

Флаги командной строки SSH

Ниже приведены несколько полезных флагов при создании туннелей:

-f # переводит процесс ssh в фоновый режим;
-n # предотвращает чтение из STDIN;
-N # не выполнять удаленные команды. Полезно, если нужно только перенаправить порты;
-T # отменяет переназначение терминала.

Вот пример команды для создания SSH-туннеля в фоновом режиме и проброса локального порта через SSH-сервер:

ssh -fnNT -L 127.0.0.1:8080:example.org:80 ssh-server

Для краткости примеры ниже приводятся без флагов командной строки.

Проброс локального порта

Перенаправление соединений с порта локальной системы на порт удаленного хоста:

ssh -L 127.0.0.1:8080:example.org:80 ssh-server

Перенаправляет локальные подключения к 127.0.0.1:8080 на 80-й порт example.org через SSH-сервер. Трафик между локальной системой и SSH-сервером идет по SSH-туннелю. Трафик между SSH-сервером и example.org — нет. С точки зрения example.org трафик идет от SSH-сервера.

ssh -L 8080:example.org:80 ssh-server
ssh -L *:8080:example.org:80 ssh-server

Команды выше открывают доступ к example.org:80 через SSH-туннель для подключений на порт 8080 на всех интерфейсах локальной системы.

ssh -L 192.168.0.1:5432:127.0.0.1:5432 ssh-server

Переадресует соединения с 192.168.0.1:5432 в локальной системе на 127.0.0.1:5432 на SSH-сервере. Обратите внимание, что здесь 127.0.0.1 — localhost с точки зрения SSH-сервера.

Конфигурации SSH

Убедитесь, что на SSH-сервере включена переадресация TCP (по умолчанию так и должно быть). Проверьте запись в файле /etc/ssh/sshd_config:

AllowTcpForwarding yes

При переадресации портов на интерфейсы, отличные от 127.0.0.1, в локальной системе необходимо включить GatewayPorts/etc/ssh/ssh_config или в командной строке):

GatewayPorts yes

Сценарии использования

Подходит для случаев, когда требуется защищенное соединение для доступа к удаленному сервису, который работает с незашифрованными данными. Например, Redis и Memcached используют протоколы на основе plaintext-данных.

В случае если защищенный доступ к одному из таких сервисов на удаленном сервере организован по общедоступным сетям, можно настроить туннель от локальной системы до этого сервера.

Проброс удаленного порта

Пробрасывает порт на удаленной системе в другую систему.

ssh -R 8080:localhost:80 ssh-server

Перенаправляет трафик с порта 8080 SSH-сервера для всех интерфейсов на порт localhost:80 на локальном компьютере. Если один из интерфейсов смотрит в Интернет, трафик, адресуемый на порт 8080, будет перенаправляться на локальную систему.

ssh -R 1.2.3.4:8080:localhost:80 ssh-server

Переадресует трафик с порта 8080 SSH-сервера на localhost:80 в локальной системе, при этом доступ ко входу в SSH-туннель со стороны сервера разрешен только с IP-адреса 1.2.3.4 (обратите внимание: директиву GatewayPorts необходимо установить в clientspecified).

Прим. переводчика: здесь, возможно, ошибка. Флаг -R — это binding_address, выбор IP адреса на машине, на котором SSH-сервер будет слушать порт. Соответственно вместо одного человечка с IP должны быть человечки, а вместо ssh_server:8080 должно быть 1.2.3.4:8080.

ssh -R 8080:example.org:80 ssh-server

Переадресует трафик с порта 8080 SSH-сервера для всех интерфейсов на localhost:80 в локальной системе. Далее трафик из локальной системы направляется на example.org:80. С точки зрения example.org источником трафика выступает локальная система.

Конфигурация SSH-сервера

SSH-сервер конфигурируется в файле /etc/ssh/sshd_config.

По умолчанию переадресуемые порты недоступны для доступа из Интернета. Для переадресации публичного интернет-трафика на локальный компьютер добавьте в sshd_config на удаленном сервере такую строку:

GatewayPorts yes

Также в sshd_config можно указать, каким клиентам разрешен доступ:

GatewayPorts clientspecified

Динамический проброс портов

Перенаправление трафика с нескольких портов на удаленный сервер.

ssh -D 3000 ssh-server

Команда выше поднимает SOCKS-прокси на порту 3000 для всех интерфейсов в локальной системе. Теперь трафик, отправленный через прокси-сервер на SSH-сервер, можно адресовать на любой порт или конечный хост. По умолчанию используется протокол SOCKS5, поддерживающий TCP и UDP.

ssh -D 127.0.0.1:3000 ssh-server

Поднимает SOCKS-прокси на 127.0.0.1:3000 в локальной системе.

При работающем SOCKS-прокси можно настроить браузер на его использование для доступа к ресурсам так, как будто соединения исходят от SSH-сервера. Например, если у SSH-сервера есть доступ к другим серверам в частной сети, с помощью SOCKS-прокси можно заходить на эти серверы локально (словно вы находитесь в той же сети), и не нужно настраивать VPN.

Работу SOCKS-прокси можно проверить командой:

curl -x socks5://127.0.0.1:12345 https://example.org

Конфигурация SSH-клиента

SSH-клиент настраивается в файле /etc/ssh/ssh_config.

Включите GatewayPorts в системе, чтобы открыть доступ другим интерфейсам к SOCKS-прокси:

GatewayPorts yes

Поскольку GatewayPorts конфигурируется на SSH-клиенте, вместо ssh_config для настройки можно использовать параметры командной строки:

ssh -o GatewayPorts=yes -D 3000 ssh-server

Jump-хосты и команды прокси-сервера

Прозрачное подключение к удаленному хосту через промежуточные.

ssh -J user1@jump-host user2@remote-host
ssh -o "ProxyJump user1@jump-host" user2@remote-host

Устанавливает SSH-соединение с jump-хостом и переадресуют трафик на удаленный хост. 

Подключается к удаленному хосту через промежуточный jump-хост. Приведенная выше команда должна сработать сразу, если у jump-хоста уже есть SSH-доступ к удаленному хосту. Если нет, можно использовать agent forwarding для проброса SSH-ключа с локального компьютера на удаленный хост.

ssh -J jump-host1,jump-host2 ssh-server

Можно указать несколько jump-хостов через запятую.

ssh -o ProxyCommand="nc -X 5 -x localhost:3000 %h %p" user@remote-host

Подключение к удаленному серверу через SOCKS5 с использованием netcat. С точки зрения сервера, IP-адрес отправителя принадлежит прокси-хосту. При этом для SSH-соединения используется сквозное шифрование, так что прокси-хост видит только зашифрованный трафик между локальной системой и удаленным хостом.

Конфигурация SSH-клиента

SSH-клиент конфигурируется в файле /etc/ssh/ssh_config.

Для подключения agent forwarding можно добавить локальный SSH-ключ к локальному SSH-агенту с помощью ssh-add:

ssh-add

Надежные SSH-туннели

Перечисленные выше команды работают на разовой основе: вы сразу получаете конкретный результат, и на этом их работа заканчивается. Поэтому, чтобы обеспечить защиту SSH-туннелей от сбоев в сети или ненадежных соединений, придется выполнить дополнительную настройку.

По умолчанию TCP-соединение, используемое для создания SSH-туннеля, может разрываться после некоторого периода бездействия. Чтобы это предотвратить, нужно настроить сервер (/etc/ssh/sshd_config) на отправку heartbeat-сообщений:

ClientAliveInterval 15
ClientAliveCountMax 4

На отсылку таких сообщений также можно настроить клиент (/etc/ssh/ssh_config):

ServerAliveInterval 15
ServerAliveCountMax 4

Использование AutoSSH

Указанные выше настройки могут предотвратить разрывы из-за неактивности, но они не в состоянии восстановить прерванные соединения. Для автоматического перезапуска SSH-туннеля можно воспользоваться утилитой autossh — она создает SSH-туннель и отслеживает его работоспособность.

AutoSSH принимает те же аргументы для настройки переадресации портов, что и SSH:

autossh -R 2222:localhost:22 ssh-server

Эта команда создает туннель, способный восстанавливаться после сетевых сбоев. По умолчанию AutoSSH открывает дополнительные порты на SSH-клиенте/сервере для проверки работоспособности (healthcheck). Если трафик не проходит между healthcheck-портами, AutoSSH перезапускает туннель.

autossh -R 2222:localhost:22 \
  -M 0 \
  -o "ServerAliveInterval 10" -o "ServerAliveCountMax 3" \
  remote-host

Флаг -M 0 отключает healthcheck-порты (за проверку работоспособности в этом случае отвечает SSH-клиент). В примере выше сервер должен посылать сигналы каждые 10 секунд. Если не проходят три подряд сигнала, SSH-клиент завершает работу, а AutoSSH поднимает новый туннель.

Дополнительные примеры и сценарии использования

Прозрачный доступ к удаленному ресурсу в частной сети.

Предположим, в частной сети есть Git-репозиторий, доступ к которому возможен только через выделенный сервер в этой сети. Сервер не подключен к Интернету. Есть прямой доступ к серверу, но нет VPN-доступа к частной сети.

Требуется открыть доступ к Git-репозиторию, как если бы подключение к нему шло напрямую из локальной системы. Если есть SSH-доступ к другому серверу, который доступен как с локального компьютера, так и с выделенного сервера, можно создать SSH-туннель и воспользоваться директивами ProxyCommand.

ssh -L 127.0.0.1:22:127.0.0.1:2222 intermediate-host

Команда выше пробрасывает порт 2222 на промежуточном хосте на порт 22 выделенного сервера. Теперь можно подключиться к SSH-серверу на выделенном сервере, несмотря на то, что он недоступен из Интернета.

ssh -p 2222 user@localhost

Для большего удобства можно добавить несколько директив в локальный ~/.ssh/config:

Host git.private.network
  HostName git.private.network
  ForwardAgent yes
  ProxyCommand ssh private nc %h %p

Host private
  HostName localhost
  Port 2222
  User private-user
  ForwardAgent yes
  ProxyCommand ssh tunnel@intermediate-host nc %h %p

В результате пользователь получает доступ к локальному Git-репозиторию, как если бы находился в частной сети.

P.S.

Читайте также в нашем блоге:

Комментарии (14)


  1. garbagecollected
    07.10.2022 09:53
    -2

    Для автоматического перезапуска SSH-туннеля можно воспользоваться утилитой autossh — она создает SSH-туннель и отслеживает его работоспособность.

    И где этот autossh брать и чем он лучше mosh?


    1. shurup
      07.10.2022 10:26
      +4

      $ autossh
      Команда «autossh» не найдена, но может быть установлена с помощью:
      sudo snap install autossh  # version 1.4, or
      sudo apt  install autossh  # version 1.4g-1
      See 'snap info autossh' for additional versions.

      (Это про первую часть вопроса.)


    1. evg_krsk
      07.10.2022 10:27

      mosh совсем о другом. Почитайте сайт проекта


      Кто использует autossh для поддержания туннелей — расскажите, каков он в сравнении с запуском через systemd/launchd ?


      1. WVitek
        08.10.2022 00:02

        Использую autossh несколько лет под debian на BananaPi M1, прокидывает 443й порт с ультрадешевой VPS со статическим IP на домашний микросервер с динамическим IP.

        Ну как использую... Настроил автозапуск в рутовом crontab и оно работает.

        При указанном использовании никаких подводных камней или проблем за несколько лет. В debian ставится одной командой.


      1. 13werwolf13
        10.10.2022 06:35

        не использую, но ответить могу

        autossh будет пытаться возродить сессию, в то время как ssh будет создавать новую. тоесть в system юните можно использовать autossh вместо ssh и это будет ещё надёжнее (в теории)


    1. garwall
      07.10.2022 10:32

      ну брать, видимо, в репозиториях, чем лучше - не требует установки на "другой стороне"


  1. Abyss777
    07.10.2022 10:52

    Можно ли для опции JumpHost указать путь к приватному ключу не через конфиг, а как опцию к команде ssh ?


    1. Ul3ainee
      07.10.2022 11:06

      -i identity_file
                   Selects a file from which the identity (private key) for public key authentication is read.  You can also specify a public key file to use the corresponding private
                   key that is loaded in ssh-agent(1) when the private key file is not present locally.  The default is ~/.ssh/id_rsa, ~/.ssh/id_ecdsa, ~/.ssh/id_ecdsa_sk,
                   ~/.ssh/id_ed25519, ~/.ssh/id_ed25519_sk and ~/.ssh/id_dsa.  Identity files may also be specified on a per-host basis in the configuration file.  It is possible to
                   have multiple -i options (and multiple identities specified in configuration files).  If no certificates have been explicitly specified by the CertificateFile di‐
                   rective, ssh will also try to load certificate information from the filename obtained by appending -cert.pub to identity filenames.

      То есть через «multiple -i options».


      1. Abyss777
        07.10.2022 12:28

        Это для конечного хоста, на Jump он не используется.


  1. shadovv76
    07.10.2022 11:34

    что-то можете сказать про скорость ssh туннеля?

    можно ли через него прямое соединение пускать для SteamLink? там скорости от 15 мбит/с и пинг критичны.

    для примера такой коннект

    Узел 1 Узел 2 Узел 3 Узел 4

    192.168.5.0/24 -> роутер с инетом и серым IP -> роутер с инетом и белым IP -> 192.168.2.0/24

    получаются варианты ssh-тунеля:

    1. Узел 1..Узел 3

    2. Узел 2..Узел 3

    Есть ли смысл рассматривать ssh? OpenVPN пинг дает не стабильный. WireGuard прокинул но еще не собрал статистику.


    1. karavan_750
      07.10.2022 12:41
      +3

      Есть ли смысл рассматривать ssh?

      Нет. SSH-туннели - это не про скорость, а про удобный доступ в труднодоступные места.

      OpenVPN пинг дает не стабильный. WireGuard прокинул но еще не собрал статистику.

      Оставайтесь на wireguard


  1. kay
    07.10.2022 12:41
    +4

    Я себе сделал fork утилиты, которая через SSH SOCKS поднимает TUN туннель. Эдакий sshuttle, но намного быстрее, отсутствует баг https://github.com/sshuttle/sshuttle/issues/563 и работает на всех популярных OS. Для Windows требуется TUN драйвер от Wireguard.
    Использую ежедневно, сбоев не замечено. Кому интересно: https://github.com/kayrus/go-tun2socks


  1. mlhi
    08.10.2022 16:27

    Спасибо за перевод очень полезной статьи.

    Но у меня возникла пара вопросов:

    Вопрос 1:

    В разделе Проброс удаленного порта есть команда ssh -R 8080:example.org:80 ssh-server и в описании говорится

    "Переадресует трафик с порта 8080 SSH-сервера для всех интерфейсов на localhost:80 в локальной системе. Далее трафик из локальной системы направляется на example.org:80."

    Не понятно откуда тут появляется локальная система, или тут имеется ввиду что ssh-server и локальная система это один и тот же хост?

    Вопрос 2:

    В разделе Дополнительные примеры и сценарии использования

    В команде

    ssh -L 127.0.0.1:22:127.0.0.1:2222 intermediate-host  

    Похоже перепутаны местами порты. А в описании говорится

    Команда выше пробрасывает порт 2222 на промежуточном хосте на порт 22 выделенного сервера. Теперь можно подключиться к SSH-серверу на выделенном сервере, несмотря на то, что он недоступен из Интернета.

    Но в таком виде если судить по разделу "Проброс локального порта" получается мы пробрасываем 2222 порт локальной системы на 22 порт промежуточной, но не как не 2222 порт промежуточной на 22 порт приватного сервера.


  1. n_bogdanov
    08.10.2022 17:12
    +1

    В примерах упустили ещё один важный вид пробросов - проброс порта для дебаггер. Тот же xdebug.

    Кроме того есть ssh vpn в том же Network Manager.