Привет! Хочу поделиться очередным способом проброса портов, теперь и на Node.JS!
Для чего это нужно? Представим, есть удалённый компьютер, к которому нужно подключиться, например, по ssh, rdp, http(s), proxy, vnc, и т.д. Но, увы, у него нет общедоступного IP по той или иной причине.
В этом примере предполагается, что у вашего устройства есть внешний IP.
Что в таком случае можно сделать? Подключиться из удалённого ПК к вашему, который слушает, например, порт 3000, пробросив определённый порт, например, 22. В результате зайдя по ssh на localhost:3000, мы зайдём на удалённый ПК.
Что же для этого нужно? На вашем и удалённом ПК:
Клонируем github.com/mgrybyk/node-tunnel и инсталим npm модули:
cd node-tunnel
npm install
Запускаем
На вашем ПК запускаем:
node server
в другом терминале:
node client
На удалённом ПК создаём файл .env, где мы укажем какой порт нужно пробросить:
N_T_AGENT_DATA_HOST=localhost
N_T_AGENT_DATA_PORT=22
node agent
На этом настройка закончена, можем подключиться:
ssh -p 8000 localhost
Сессия ssh к удалённой машине установлена!
Для начала (и чтобы поиграться) server, client и agent можно запустить на одном устройстве, тогда подключившись к localhost:8000 по ssh вы зайдёте к себе же. Вместо localhost можно указать другой хост; вместо ssh можно использовать другой TCP порт, например, http(s)
Но, что же делать, если у вас нет внешнего IP? Нужно найти промежуточную точку, где он имеется, например, бесплатный контейнер на AWS.
Суть примерно та же, для примера возьмём теперь порт rdp и дадим имена агенту и клиенту.
Имена отдельного агента и клиентов должны совпадать.
На удалённом ПК отредактируем .env файл, в этот раз укажем ещё и хост Windows PC внутри вашей сети:
N_T_SERVER_HOST=хост ПК с внешним IP
N_T_AGENT_DATA_HOST=Windows PC внутри удалённой сети
N_T_AGENT_DATA_PORT=3389
N_T_AGENT_NAME=test-rdp
И запустим:
node agent
На ПК с внешним IP просто запустим node server
предварительно склонив репозиторий и установив модули npm.
На вашем ПК создадим .env файл, так же указав порт, который клиент будет слушать:
N_T_SERVER_HOST=хост ПК с внешним IP
N_T_CLIENT_NAME=test-rdp
N_T_CLIENT_PORT=3388
Запустим node client
. Здорово! Теперь мы можем подключиться по RDP на localhost:3388 открыв rdp сессию к ПК внутри сети агента.
Больше клиентов?
Также, можно рассказать как настроить (создать .env) клиент другу. Запустив у себя клиент, он также сможет заходить по rdp туда же.
.env
Для удобства, можно создавать много файлов типа .env.ssh, .env.rdp, .env.proxy и т.д., после чего запускать agent/client/server передав имя файла как аргумент, например:
node client .env.rdp
При шифровании данных их длина растёт, из-за чего сообщение часто делится на два. После чего, на другой стороне их нужно склеить, перед тем как пускать дальше. Выглядит слишком криво :(
Ух, начну самое сложное, попытаюсь в двух словах объяснить как это работает.
Использовал стандартный модуль Net, который работает по TCP.
Клиенты и агенты подключаются к серверу, который перенаправляет трафик с агента — клиенту и обратно. Это и есть основная магия.
У клиента, сервера и агента есть важные две части.
Первая — сокет, для установки соединения, чтобы дать понять кто есть кто, назовём его — сервисный сокет.
Вторая — сокет для данных. Именно тут и происходит создание pipe'ов:
agentSocket.pipe(clientSocket)
clientSocket.pipe(agentSocket)
Пример с ssh
- Подключаюсь к клиенту
- Клиент перенаправляет трафик на сервер
- Сервер перенаправляет трафик на агента
- Агент создаёт соединение на указанный host:port и перенаправляет туда трафик
Ну и обратно. Ответ SSH сервера агенту, далее сервер, клиент, ssh клиент.
… немного по каждому отдельно
Server
- Сервер ждёт на клиентов и агентов
- Когда приходит агент — сервер создает отдельный сервер для него, через который будет в дальнейшем идти весь трафик
- При подключении клиента с тем же именем что и агент — сервер отправляет клиенту порт сервера для агента
- может быть много агентов, но имена должны быть уникальны
- может быть много клиентов для каждого агента, связка много к одному
Зная, кто есть кто, сервер, созданный для агента, перенаправляет трафик от агента к клиенту и обратно. Без шифрования! Работает примерно так:
- Приходит клиент, сохраняем ссылку на сокет
- Сервер уведомляет агента, что есть клиент и пора открывать соединение
- Когда агент приходит — сервер пайпит сокет агента на первый доступный сокет клиента и обратно
- Сервер уведомляет клиента, что пайп создан и пора форвардить трафик
В дальнейшем сервер не слушает событие «data» клиента и агента.
Agent
установив соединение с сервером агент ждёт команд от клиентов, как только приходит команда — агент устанавливает соединение на указанный host:port. После чего перенаправляет трафик с сервера на открытое соединение и обратно.
Client
клиент создаёт локальный сервер (порт N_T_CLIENT_PORT). При успешном соединении с удалённым сервером перенаправляет весь трафик с удалённого на локальный сервер и обратно.
Это всё!
Спасибо за прочтение.
Надеюсь, вам было хоть немного интересно, капельку понятно и, может быть, даже пригодиться это приложение, как пригодилось мне и моим коллегам.
→ Github
При его помощи можно вклиниться посреди цепочки из пайпов для различных целей, например, логирование, обработка ошибок, в конце концов, замены всех ключевых слов на свои или даже единичек на нолики :)
П.П.С.
Писал приложение изначально для себя и потому что просто было интересно что-то такое сделать на Node. Сам использую для ssh, rdp, proxy, vnc и других целей :)
Автор: xlenz