Наглядное руководство по SSH-туннелям

Kate

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

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

5bbc2386288383607baa91dbf486efb6.png

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

Здесь и далее все схемы взяты из оригинала статьи
Здесь и далее все схемы взяты из оригинала статьи
Сценарии использования туннелей*:

  • Предоставление зашифрованных каналов для протоколов, передающих данные «открытым текстом».
  • Открытие бэкдоров в частные сети.
  • Обход межсетевых экранов.
*. Примечание
Перечень дополняется. Не стесняйтесь предлагать примеры или исправления
на GitHub.

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

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

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

543846f87bf4140c318be4b0c8ff7c3f.png

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

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

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

c0af21f08bc3a36e6851a61e7eb3b122.png

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

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

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

95c3d64257da5ebf081e5e4b865358eb.png

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

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

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

sudo ssh -L 80:example.com:80 ssh-server
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
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
f7fa3dfae5ff89e85beef7f512e45613.png

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

ssh -L 192.168.0.1:5432:127.0.0.1:5432 ssh-server
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
b6e83cf22687fb2f21fe4e036772ab4a.png

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

ssh -R 1.2.3.4:8080:localhost:80 ssh-server
c79670176698bbb9c4c444042989b179.png

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

ssh -R 8080:example.org:80 ssh-server
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
d17b4c528329f7abcf9d8ea45921eb04.png

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

ssh -D 127.0.0.1:3000 ssh-server
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
ba0020bfd39836666f0abcd163fc61f5.png

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

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

ssh -J jump-host1,jump-host2 ssh-server
6ce9c45027c73f8e4e843e3354fedb14.png

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

ssh -o ProxyCommand="nc -X 5 -x localhost:3000 %h %p" user@remote-host
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
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-доступа к частной сети.

07196f3af1adff303b439e5dd3616c5b.png

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

ssh -L 127.0.0.1:22:127.0.0.1:2222 intermediate-host
4c142f4968eae4156941818e193032c2.png

Команда выше пробрасывает порт 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
b17511e738701b686b17d8a1accd1238.png

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


 
Сверху