Возникла необходимость организовать трафик к внешнему сервису из сегмента сети с ограничением на исходящие соединения. Этот внешний сервис работал одновременно со множеством tcp/udp сокетов. При беглом обзоре существующих утилит не обнаружил готовое решение инкапсуляции множества сокетов с поддержкой «обратного» соединения.

Решил сделать свое решение, основными требованиями которого являются:

  1. Использовать один белый IP адрес с одним tcp портом, по умолчанию 80 порт http.

  1. Транспорт туннеля по websocket или http, перфоманс или доступность. Идеально если будет автоматический выбор лучшего из доступных протоколов обмена.

  1. Туннелирование одновременно множества tcp и udp сокетов.

  1. Кросс-платформенное консольное приложение, конфигурируемое параметрами запуска.

 Исходя из пунктов 2 и 4 были выбраны .NET7 и библиотека SignalR. С этим набором я создал open-source проект, репозиторий https://github.com/viordash/TuToDataTunnel. В результате получилось два приложения TutoProxy.Server и TutoProxy.Client, в артефактах GitHub actions хранятся скомпилированные бинарники (x64), для linux и windows.

SignalR - это отличная библиотека для real-time транспорта данных, которая умеет автоматически выбирать протокол обмена: если недоступен websocket, то она переключается на http (long polling).

Приложение TutoProxy.Server - это сервер входящих подключений для клиентов туннелирования и также конечных tcp/udp клиентов. 

Аргументы его запуска:

  • <host>,  адрес сервера, например http://200.100.10.1:8088 

  • --tcp <tcp>,  список прослушиваемых tcp-портов, например --tcp=80,81,443,8000-8100. Опционально, при условии наличия параметра --udp.

  • --udp <udp>, список прослушиваемых udp-портов, например --udp=700-900,65500. Опционально, при условии наличия параметра --tcp.

  • --clients <clients>  опциональный список разрешенных клиентов, например --clients=Client1,Client2 если этот параметр опущен, то не будет проверок доступа для подключаемого клиента

Например, строка запуска входного туннелирования около 50-ти tcp/udp портов на три клиента будет выглядеть так:

TutoProxy.Server http://200.100.10.1:8088 --tcp=3389,8071-8073,10000-10010,20000-20010 --udp=5000-5010,7000-7010 --clients=Client0Linux,ClientSecLinux,Client3Win

 
Приложение TutoProxy.Client - это клиент туннелирования выходного трафика. 

Аргументы его запуска:

  • <server>, адрес сервера TutoProxy.Server, например http://200.100.10.1:8088 

  • <sendto>, IP получателя данных, например 127.0.0.1

  • --id <id>, ID клиента, например --id=Client1

  • --tcp <tcp>,  список tcp-портов, например --tcp=80,81,443,8000-8100. Опционально, при условии наличия параметра --udp.

  • --udp <udp>, список udp-портов, например --udp=700-900,65500. Опционально, при условии наличия параметра --tcp.

Например, строка запуска выходного туннелирования 5-ти tcp и 3-х udp портов будет выглядеть так:

TutoProxy.Client http://200.100.10.1:8088 127.0.0.1 --tcp=8071,10000,20004-20006 --udp=7000-7002 --id= Client0Linux.

Важно учесть то, что порты у различных TutoProxy.Client не должны пересекаться, т.е. каждый клиент обслуживает уникальный набор сокетов/портов.

Данное решение также может подойти для разворачивания веб-сервисов на «серых» IP, т.е. можно сэкономить на дорогостоящем VPS.

Известные на данный момент недостатки:

  • Оверхед трафика, для его минимизации используется MessagePack. 

  • Нет поддержки keep-alive на выходе туннелей. Вероятно в будущем придется добавить парсинг http заголовков на входе в туннель, чтобы активировать keep-alive хотя бы для http трафика

Комментарии (3)


  1. build_your_web
    03.01.2023 16:15
    +1

    Неплохое упражнение по программированию, но впн проще.


  1. xmetropol
    03.01.2023 16:32

    FRP - fast reverse proxy, туннелирование TCP/UDP трафика, не то же самое делает?
    https://github.com/fatedier/frp


  1. web3_Venture
    03.01.2023 17:56
    +1

    Самое загадочное в .net это масштабирование SignalR, какие лимиты, что будет если одинаковые хабы будут на разных машинах, нужно ли масштабировать хабы, а не группы внутри одного хаба, какие лимиты и как паралелить если допустим есть 1 супер большая группа. Как при этом работать с fallover допустим если образ SignalR развернут в docker compose в N сервисов с внутренней случайном балансировкой от докер композа.

    Ни разу нигде в интернатах не встречал такую статью. Хорошо бы чтобы ктото на хабаре такую сделал на конкретном примере.