Прим. переводчика: автор статьи рассматривает практические сценарии и примеры организации SSH-туннелей. А для более наглядного объяснения того, как это работает, графически показывает потоки трафика.
Туннели SSH — это зашифрованные TCP-соединения между клиентами и серверами SSH. Трафик входит с одной стороны туннеля и прозрачно выходит с другой. Изначально этот термин относился к туннелям на виртуальных сетевых интерфейсах TUN/TAP, однако сейчас так обычно называют проброс портов SSH.
![](https://habrastorage.org/getpro/habr/upload_files/5bb/c23/862/5bbc2386288383607baa91dbf486efb6.png)
Например, вот так будет выглядеть схема обратного туннеля, в котором обращение к порту 80 SSH-клиента выполняется через SSH-сервер с определенного IP-адреса (1.2.3.4
):
![Здесь и далее все схемы взяты из оригинала статьи Здесь и далее все схемы взяты из оригинала статьи](https://habrastorage.org/getpro/habr/upload_files/666/9ec/099/6669ec099fd3b71f94cb9bb6d2b27507.png)
Сценарии использования туннелей*:
Предоставление зашифрованных каналов для протоколов, передающих данные «открытым текстом».
Открытие бэкдоров в частные сети.
Обход межсетевых экранов.
*. Примечание
Перечень дополняется. Не стесняйтесь предлагать примеры или исправления
на GitHub.
Проброс портов
Перенаправляет порт из одной системы (локальной или удаленной) в другую.
Проброс локального порта
![](https://habrastorage.org/getpro/habr/upload_files/543/846/f87/543846f87bf4140c318be4b0c8ff7c3f.png)
Проброс локальных портов позволяет перенаправить трафик с SSH-клиента на целевой хост через SSH-сервер. Другими словами, можно получить доступ к удаленным сервисам по зашифрованному соединению так, как будто они локальные. Примеры использования:
доступ к удаленному сервису (Redis, Memcached и т. д.) по внутреннему IP-адресу;
локальный доступ к ресурсам, размещенным в частной сети;
прозрачное проксирование запроса к удаленному сервису.
Проброс удаленного порта
![](https://habrastorage.org/getpro/habr/upload_files/c0a/f21/f08/c0af21f08bc3a36e6851a61e7eb3b122.png)
Проброс удаленного порта позволяет направить трафик с SSH-сервера в пункт назначения либо через SSH-клиент, либо через другой удаленный хост. Открывает пользователям в публичных сетях доступ к ресурсам в частных сетях. Примеры использования:
открытие доступа к локальному серверу разработки через публичную сеть;
предоставление доступа с ограничением по IP-адресу к удаленному ресурсу в частной сети.
Динамический проброс портов
![](https://habrastorage.org/getpro/habr/upload_files/95c/3d6/425/95c3d64257da5ebf081e5e4b865358eb.png)
При динамической переадресации портов на SSH-клиенте поднимается SOCKS-прокси для перенаправления TCP-трафика через SSH-сервер на удаленный хост.
Пересылка с системных (privileged) портов
Для пересылки трафика с системных портов (1–1023) необходимо запустить SSH с правами суперпользователя в системе, на которой открывается порт.
sudo ssh -L 80:example.com:80 ssh-server
![](https://habrastorage.org/getpro/habr/upload_files/616/8f1/239/6168f1239634e038387e6f1e2a661594.png)
Флаги командной строки 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
![](https://habrastorage.org/getpro/habr/upload_files/3ca/80e/5d4/3ca80e5d46d252347cac20abdabaac1e.png)
Перенаправляет локальные подключения к 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
![](https://habrastorage.org/getpro/habr/upload_files/f7f/a3d/fae/f7fa3dfae5ff89e85beef7f512e45613.png)
Команды выше открывают доступ к example.org:80 через SSH-туннель для подключений на порт 8080 на всех интерфейсах локальной системы.
ssh -L 192.168.0.1:5432:127.0.0.1:5432 ssh-server
![](https://habrastorage.org/getpro/habr/upload_files/c74/4b6/06b/c744b606b489b1f30db86ecd011d019d.png)
Переадресует соединения с 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
![](https://habrastorage.org/getpro/habr/upload_files/b6e/83c/f22/b6e83cf22687fb2f21fe4e036772ab4a.png)
Перенаправляет трафик с порта 8080 SSH-сервера для всех интерфейсов на порт localhost:80 на локальном компьютере. Если один из интерфейсов смотрит в Интернет, трафик, адресуемый на порт 8080, будет перенаправляться на локальную систему.
ssh -R 1.2.3.4:8080:localhost:80 ssh-server
![](https://habrastorage.org/getpro/habr/upload_files/c79/670/176/c79670176698bbb9c4c444042989b179.png)
Переадресует трафик с порта 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
![](https://habrastorage.org/getpro/habr/upload_files/a1d/ff8/c63/a1dff8c630aef3775b6ca2c0f705ecdf.png)
Переадресует трафик с порта 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
![](https://habrastorage.org/getpro/habr/upload_files/d17/b4c/528/d17b4c528329f7abcf9d8ea45921eb04.png)
Команда выше поднимает SOCKS-прокси на порту 3000 для всех интерфейсов в локальной системе. Теперь трафик, отправленный через прокси-сервер на SSH-сервер, можно адресовать на любой порт или конечный хост. По умолчанию используется протокол SOCKS5, поддерживающий TCP и UDP.
ssh -D 127.0.0.1:3000 ssh-server
![](https://habrastorage.org/getpro/habr/upload_files/2c3/82f/26f/2c382f26f04f0717776c8461aee9f75e.png)
Поднимает 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
![](https://habrastorage.org/getpro/habr/upload_files/ba0/020/bfd/ba0020bfd39836666f0abcd163fc61f5.png)
Устанавливает SSH-соединение с jump-хостом и переадресуют трафик на удаленный хост.
Подключается к удаленному хосту через промежуточный jump-хост. Приведенная выше команда должна сработать сразу, если у jump-хоста уже есть SSH-доступ к удаленному хосту. Если нет, можно использовать agent forwarding
для проброса SSH-ключа с локального компьютера на удаленный хост.
ssh -J jump-host1,jump-host2 ssh-server
![](https://habrastorage.org/getpro/habr/upload_files/6ce/9c4/502/6ce9c45027c73f8e4e843e3354fedb14.png)
Можно указать несколько jump-хостов через запятую.
ssh -o ProxyCommand="nc -X 5 -x localhost:3000 %h %p" user@remote-host
![](https://habrastorage.org/getpro/habr/upload_files/7a6/a69/0a0/7a6a690a0597228d6e261b90025ed4f1.png)
Подключение к удаленному серверу через 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
![](https://habrastorage.org/getpro/habr/upload_files/090/238/e28/090238e289234f1d1d560e7c278b337e.png)
Эта команда создает туннель, способный восстанавливаться после сетевых сбоев. По умолчанию 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-доступа к частной сети.
![](https://habrastorage.org/getpro/habr/upload_files/071/96f/3af/07196f3af1adff303b439e5dd3616c5b.png)
Требуется открыть доступ к Git-репозиторию, как если бы подключение к нему шло напрямую из локальной системы. Если есть SSH-доступ к другому серверу, который доступен как с локального компьютера, так и с выделенного сервера, можно создать SSH-туннель и воспользоваться директивами ProxyCommand.
ssh -L 127.0.0.1:22:127.0.0.1:2222 intermediate-host
![](https://habrastorage.org/getpro/habr/upload_files/4c1/42f/496/4c142f4968eae4156941818e193032c2.png)
Команда выше пробрасывает порт 2222 на промежуточном хосте на порт 22 выделенного сервера. Теперь можно подключиться к SSH-серверу на выделенном сервере, несмотря на то, что он недоступен из Интернета.
Прим. переводчика: возможно, что здесь тоже ошибка: перепутаны местами 22 и 2222 порты в команде.
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
![](https://habrastorage.org/getpro/habr/upload_files/b17/511/e73/b17511e738701b686b17d8a1accd1238.png)
В результате пользователь получает доступ к локальному Git-репозиторию, как если бы находился в частной сети.
P.S.
Читайте также в нашем блоге: