Прим. переводчика: автор статьи рассматривает практические сценарии и примеры организации 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 (по умолчанию так и должно быть). Проверьте запись в файле : ssh/sshd_config
AllowTcpForwarding yes
При переадресации портов на интерфейсы, отличные от 127.0.0.1, в локальной системе необходимо включить GatewayPorts
(в или в командной строке): 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-сервер конфигурируется в файле . 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-клиент настраивается в файле . 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-клиент конфигурируется в файле . ssh/ssh_config
Для подключения agent forwarding
можно добавить локальный SSH-ключ к локальному SSH-агенту с помощью ssh-add
:
ssh-add
Надежные SSH-туннели
Перечисленные выше команды работают на разовой основе: вы сразу получаете конкретный результат, и на этом их работа заканчивается. Поэтому, чтобы обеспечить защиту SSH-туннелей от сбоев в сети или ненадежных соединений, придется выполнить дополнительную настройку.
По умолчанию TCP-соединение, используемое для создания SSH-туннеля, может разрываться после некоторого периода бездействия. Чтобы это предотвратить, нужно настроить сервер () на отправку heartbeat-сообщений: ssh/sshd_config
ClientAliveInterval 15
ClientAliveCountMax 4
На отсылку таких сообщений также можно настроить клиент (): 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-репозиторий, доступ к которому возможен только через

Требуется открыть доступ к 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.
Читайте также в нашем блоге:
Автор:
duckhawk