Возникла необходимость организовать трафик к внешнему сервису из сегмента сети с ограничением на исходящие соединения. Этот внешний сервис работал одновременно со множеством tcp/udp сокетов. При беглом обзоре существующих утилит не обнаружил готовое решение инкапсуляции множества сокетов с поддержкой «обратного» соединения.
Решил сделать свое решение, основными требованиями которого являются:
Использовать один белый IP адрес с одним tcp портом, по умолчанию 80 порт http.
Транспорт туннеля по websocket или http, перфоманс или доступность. Идеально если будет автоматический выбор лучшего из доступных протоколов обмена.
Туннелирование одновременно множества tcp и udp сокетов.
Кросс-платформенное консольное приложение, конфигурируемое параметрами запуска.
Исходя из пунктов 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 трафика