Как создать SSH-туннелирование или переадресацию портов в Linux?

Опубликовано: 30 Сентября, 2022

SSH — это стандартная клиентская утилита безопасной оболочки для Linux. Он используется для установки безопасных соединений с удаленными (или даже локальными) серверами ssh. Но некоторые программы не разработаны достаточно гибкими, чтобы их можно было обрабатывать с помощью ssh тривиальным способом: программа может работать только с локальными соединениями или некоторые связанные сетевые адреса трудно определить в коде. Вот почему SSH содержит несколько опций для перенаправления защищенного трафика в соответствии с такими вариантами использования. Давайте перейдем от случая к случаю, чтобы увидеть, как это работает.

Перенаправление порта на локальном хосте

Первый случай: у нас есть приложение, которое жестко закодировано для подключения к локальному серверу с указанным портом. Наша цель — вместо этого использовать удаленный сервер с другим портом. Еще один момент здесь: передавать данные безопасным (зашифрованным) способом. Далее следует код для тестирования локального хоста со стороны клиента и сервера.

Обзор локального перенаправления:

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

Порт переадресации в теле скрипта localhost:

#!/usr/bin/env bash
nc -l 9000 &
PID0=$!
ssh alexey@localhost -L 5000:localhost:9000 sleep 4 &
PID1=$!
sleep 1
echo done | nc -N localhost 5000
sleep 1
kill -9 $PID0 $PID1 2> /dev/null

Перенаправление порта в консоли выполнения скрипта localhost:

В первой строке мы создаем TCP-сервер на порту 9000 в фоновом режиме (смотрите, команда заканчивается символом '&'). Затем мы сохраняем PID сервера (идентификатор процесса) в локальной переменной PID0 . Затем мы запускаем сеанс ssh на наш собственный хост с параметрами, которые перенаправляют соединения, входящие на локальный порт 5000 , на удаленный хост 9000 . Синтаксис опции -L : -L <порт локального хоста>:<удаленный хост>:<порт удаленного хоста для перенаправления соединений с порта локального хоста> . Мы запускаем сеанс с командой «sleep 4» в фоновом режиме, чтобы включить перенаправление на 4 секунды, а затем завершить сеанс. Затем мы сохраняем PID сеанса ssh в переменной PID1 . В следующей строке мы засыпаем на 1 секунду, чтобы убедиться, что сервер запустился и сеанс ssh начался. В следующей строке мы отправляем текст «Готово» на порт 5000 с помощью команды nc и закрываем сеанс с помощью -N. Далее мы спим 1 секунду, чтобы обработать перенаправление трафика по защищенному туннелю и получить на выходе «готово». Затем последовало нормальное уничтожение запущенного сервера и туннелированного сеанса ssh. Как видите, в этом скрипте можно использовать любой <удаленный сервер> против <localhost> — чтобы иметь возможность безопасно работать с удаленным сервером. Даже если клиентское приложение не предназначено для работы с ним.

Перенаправление порта на удаленном хосте

В случае, если удаленные серверы не имеют «белого» адреса и работают через NAT (преобразование сетевых адресов), нам все равно нужен безопасный туннель. Но удаленные пункты назначения в этом случае могут быть не обнаружены. Вместо этого можно организовать безопасное соединение с известным центральным хостом. И перенаправить порты на центральном узле, чтобы эти клиенты могли подключаться к одноранговым узлам через NAT. Разница заключается в том, чтобы сделать порт 5000 на удаленном хосте доступным для перенаправления на локальный порт 9000 на нашем хосте.

Порт переадресации в теле сценария удаленного хоста:

#!/usr/bin/env bash
nc -l 9000 &
PID0=$!
ssh alexey@localhost -L 5000:localhost:9000 sleep 4 &
PID1=$!
sleep 1
echo done | nc -N localhost 5000
sleep 1
kill -9 $PID0 $PID1 2> /dev/null

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

Все действия в скрипте аналогичны предыдущему случаю. Но смысл перенаправления другой. Мы перенаправляем порт удаленного хоста 5000 на порт локального хоста 9000 и создаем безопасный туннель для передачи соединений с порта удаленного хоста 5000 на порт локального хоста 9000. Это позволяет безопасно работать даже с хостами через NAT.

Туннелирование интерфейса Ssh

Другим вариантом использования ssh является раскрытие IP-адреса безопасно подключенного однорангового узла путем туннелирования интерфейсов с использованием at ssh. Мы выставляем IP-адрес через интерфейс tun на локальном хосте для работы с сервером на удаленном хосте. Следующий пример откроет детали.

Скрипт туннелирования интерфейса Ssh:

#!/usr/bin/env bash
(ssh -tt root@192.168.144.207 -w 2:2 << EOF
ifconfig tun2 10.1.1.1/24 pointopoint 10.1.1.2 up
nc -l 10.1.1.1 9000
EOF
) 2>/dev/null | grep ok &
PID=$!
sleep 1
ifconfig tun2 10.1.1.2/24 pointopoint 10.1.1.1 up
echo ok | nc -N 10.1.1.1 9000

Консоль выполнения сценария туннелирования интерфейса Ssh:

В первой строке мы создаем ssh-соединение с удаленным хостом. Очень важно иметь права root при запуске скрипта: он создаст в системе виртуальные туннелирующие ( tun) интерфейсы. Только root может сделать это легальным. Опция -w имеет следующий синтаксис: -w <индекс интерфейса tun на локальном хосте, индекс интерфейса tun на удаленном порту> . Эти конкретные интерфейсы сопоставляются друг с другом безопасным способом — сессия приходит к интерфейсу tun локального хоста в порту, который безопасно перенаправляется на интерфейс tun, сопоставленный с удаленным хостом, на том же порту. Следующей строкой мы используем утилиту ifconfig, чтобы настроить некоторые внутренние адреса для удаленных tun-ов и включить их. Второй адрес ( pointopoint ) указывает на одноранговый IP-адрес, который будет использоваться при туннелировании. Следующей строкой мы запускаем TCP-сервер nc на порту 9000 . Обратите внимание на ip-адрес сервера: он совпадает с адресом соответствующего интерфейса tun. В следующей строке мы игнорируем ошибки и выбираем только ok , содержащий сообщение, если таковое имеется. Остальные строки сценария используются на локальной стороне. Спим 1 секунду, чтобы убедиться в полноте туннеля. Затем мы устанавливаем IP-адреса для нашего локального интерфейса tun , чтобы они соответствовали параметрам IP-адреса однорангового узла (первый и второй адреса совпадают для однорангового узла и локального хоста). Этой же командой мы включаем интерфейс. Следующая команда запускает tcp-клиент, который устанавливает соединение с локальным интерфейсом tun на определенном порту и отправляет сообщение «ok». Насколько мы помним — сервер стартовал на другом хосте на том же порту. Последняя строка в скрипте: убиваем ssh с туннелем и сервером. Если у вас есть несколько серверов на одном адресе — этот подход может быть полезен, так как его нужно настроить только один раз для нескольких портов.