Для чего это нужно? Представим, есть удалённый компьютер, к которому нужно подключиться, например, по 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
При его помощи можно вклиниться посреди цепочки из пайпов для различных целей, например, логирования, обработки ошибок, в конце концов, замены всех ключевых слов на свои или даже единичек на нолики :)
P.P.S.
Изначально приложение было написано для себя, так как было просто интересно что-то подобное сделать на Node. Сам использую для ssh, rdp, proxy, vnc и других целей :)
Комментарии (12)
alexevil
12.07.2017 10:48-1Фу фу фу, туннели на javascript. Полагаю расход памяти/процессора на такое в десятки раз больше, чем чем при использовании нормальных инструментов.
xlenz
12.07.2017 13:58На глаз сравнил с ssh на винде, по процессору разницы не заметил (0), по памяти — есть. Каждый инстанс ноды — пирмерно 10 метров памяти (сервер, агент, клиент).
Путти — примерно 3 метра памяти, bitvise ssh server метров 10.Igelko
12.07.2017 16:30-1вот только putty — это один несчастный бинарник, а это приличного размера ворох файлов, которому подавай на машине nodejs, и возможно ещё компилятор.
В общем я клоню к тому, что в бородатые года написал Спольски про .Net http://russian.joelonsoftware.com/Articles/PleaseSirMayIHaveaLinker.html
xlenz
12.07.2017 18:05Спасибо за ссылку. Я с вами полностью согласен. Можно было написать ни си, или использовать готовое решение, проверенное годами.
Мне хотелось сделать что-то подобное, но на node.js. На серьёзное решение претендовать не может.
Для простого проброса портов ссш и рдп в домашних условиях работает отлично на винде, маке и линухах. К тому же очень удобно, что не нужно каждый раз запускать всё заново при дисконнекте. Плюс можно поиграться с подменой данных, преобразовав их (зип, шифрование), заменив ключевое слово, или, например:
for (let i = 0; i < chunk.length; i++) { chunk[i] = chunk[i] + shiftValue }
ice2heart
14.07.2017 09:05node.js достаточно быстрая штука.
Ощущение что JS медленный возникает из-за того что в большинстве случаев он ворочает DOM.
А так нода достаточно быстра, плюс всегда есть возможность ботлнек переписать на сях.
А ещё можно собрать бинарник с архивом программы и минимальной ноды.xlenz
14.07.2017 20:11Сейчас боттлнек: создание соединений и шифрование (которого ещё нет).
Для ссш, рдп проблем нет, а вот хттп и т.д., где нужно много раз открывать/закрывать соединения — не хорошо.
Пока проблемы скорее в реализации, нежели в ноде или джс.
Идея с бинарником хороша, попробую как-то, спасибо
ice2heart
14.07.2017 09:02+1А почему не использовать STUN между компами?
А агента использовать только для сигналинга. Тогда одна машина с сигналером сможет обслуживать множество сетей.
Правда придется писать TCPoverUPD.
Чтоб не гонять трафик через сервер.xlenz
14.07.2017 20:06Думал над этим, но не знаю как это сделать. Обязательно позже займусь. Эта фича и шифрование — главный фокус. Проблемы в том числе и с шифрованием пока. В данный момент не могу понять почему после декрипта — данные другие, точнее, некоторые символы.
Wexter
*картинка с троллейбусом.jpg*
поздравляю, вы изобрели vpn/ssh tunnel, только зачем?
xlenz
В сети, которую мне нужно попасть, запрещён VPN, а также SSH port forwarding (не знаю каким образом). К тому же, чем больше выбор — тем лучше. Возможно, кому-то ещё поможет.
Плюс, есть возможность менять данные (на стороне агента) перед отправкой дальше, например, зашифровав, сжав и т.д. (тестирую эту возможность). Уже сейчас использую для обхода блокировок. Искренне надеюсь, что ВПН, прокси т.д. не запретят, а если и запретят, то чем больше решений — тем лучше.
ivlis
Как он может быть запрещен, если это делается локально.
xlenz
Не только локально. Даже простой роутер может блокировать доступ к определённым айпи, а корпоративные решения могут много чего интересного.