Перейти к содержанию

Архитектура dix.su

Стек

Контейнер Технология Роль
nginx nginx:alpine TLS-терминация, маршрутизация по поддоменам, единственная точка входа на 80/443
dixu_proxy Elixir / Phoenix 1.7 WebSocket-туннель, HTTP- и TCP-проксирование к устройствам, анти-абуз защита
modulauth Java 22 / Spring Boot 3 WebFlux Авторизация, профили, биллинг, каталог, админка, весь веб-UI
postgres PostgreSQL 17 Основная БД, общая для modulauth и dixu_proxy (внешний порт 5433)
redis Redis 7 Сессии Spring Security
screenshotter Node + Playwright Headless-скриншоты для карточек туннелей на интерстициале

Все контейнеры — в одной Docker-сети dixsu-net. nginx — единственный сервис, слушающий внешние порты 80/443; dixu_proxy дополнительно слушает напрямую 7 TCP-портов для прямого проксирования протоколов (SSH/RDP/VNC/БД, см. ниже) — это сделано намеренно, не через nginx (см. «Почему TCP не через nginx»).

Репозиторий

Единый монорепо gitlab.com:dixsu/infra, ветка prod для всего (включая вложенные modulauth/ и dixu_proxy/ — раньше это были отдельные git-репозитории с разными ветками, сейчас все три — один репозиторий, одна ветка). Деплоится напрямую на прод по SSH, без CI/CD.

dixsu-infra/
├── docker-compose.yml
├── Makefile
├── .env                  ← секреты, не в git, живёт только на VPS
├── .env.example
├── docs/                 ← эта документация
├── nginx/                ← конфиг nginx (шаблоны + entrypoint)
├── modulauth/             ← Java/Spring Boot
├── dixu_proxy/            ← Elixir/Phoenix
├── screenshotter/         ← Node/Playwright
└── INCIDENT/logs/         ← журнал банов (volume, см. SECURITY.md)

Подробности по деплою — DEPLOYMENT.md. Разбор каждого модуля — MODULES.md.

Три публичных сервиса на одном домене

Сервис URL Описание
Прокси-туннель slug.dix.su Локальный сервер пользователя в интернете через WebSocket-туннель
Личная визитка dix.su/p/slug Публичная страница с аватаром, ссылками, соцсетями
Короткие ссылки dix.su/код Сокращатель URL с авторством и счётчиком кликов

Поток данных: HTTP-туннель

Браузер → nginx:443 → dixu_proxy:4000 (HTTP)
                          ├─ InterstitialPlug — анти-абуз гейт + страница "вы переходите на ресурс пользователя"
                          ├─ ProxyController — ищет устройство в DeviceRegistry по slug (поддомен)
                     ProxyChannel (Phoenix Channel "proxy:{slug}")
                          │  push http_request_start / http_request_chunk* / http_request_end
                   device_client (на оборудовании пользователя, отдельный проект,
                                   не входит в этот монорепо — устанавливается
                                   как готовый бинарь, см. ARCHITECTURE.md → Device client)
                          │  Req.request! к локальному серверу устройства
                          │  http_response_start / http_response_chunk* / http_response_end (стримингом)
                   ProxyChannel → RequestRegistry → ProxyController → Plug.Conn.chunk → Браузер

Тело запроса/ответа base64-кодируется (Phoenix Channels — JSON, бинарные данные напрямую не передать) и стримится чанками — ни сервер, ни устройство не накапливают тело целиком в памяти (кроме request body на устройстве — намеренный компромисс, устройство контролируется пользователем).

Поток данных: TCP-туннель

Помимо HTTP, dixu_proxy проксирует TCP-трафик семи фиксированных протоколов (SSH/2222, RDP/3389, VNC/4900, PostgreSQL/4432, MySQL/4306, Redis/4379, MongoDB/4017) через то же WebSocket-соединение device_client, терминируя TLS самостоятельно (устройству не нужен SSL ни для одного протокола кроме VNC). Подробный протокол по каждому из 7 портов — в dixu_proxy/README.md (разделы «TCP-туннели: порты и роли» и «Протокол TCP-туннеля»).

Что не поддерживается и почему:

  • UDP — в принципе. WebSocket работает поверх TCP; UDP-датаграммы не передаются через relay. Это затрагивает Minecraft Bedrock Edition (порт 19132/UDP), многие игровые серверы, DNS и VoIP.
  • Произвольные TCP-порты (например, игровые серверы, Minecraft Java/25565) — маршрутизация к конкретному устройству возможна только через TLS SNI: прокси читает slug из поля SNI в ClientHello. Протоколы без встроенного TLS (Minecraft, большинство игровых движков) не посылают SNI → прокси не может определить назначение → соединение отклоняется. Добавить новый порт технически несложно, но клиент обязан говорить TLS.
  • HTTP-панели управления игровым сервером (Pterodactyl и аналоги) — работают через обычный HTTP-туннель без ограничений.

Почему TCP не через nginx

dixu_proxy слушает TCP-порты (2222, 3389, 4900, 4432, 4306, 4379, 4017) напрямую через Docker port-forwarding, не через nginx stream-proxy. Раньше было наоборот — nginx терминировал TCP и открывал новое соединение к dixu_proxy, из-за чего прокси видел IP nginx, а не реального клиента, и rate-limiter считал всех клиентов одним IP. См. docs/SECURITY.md и dixu_proxy/README.md раздел 3.

Device client

Клиент, который устанавливается на оборудование пользователя (Raspberry Pi, домашний сервер, VPS) и держит WebSocket-соединение с dixu_proxy. Не входит в этот монорепо — отдельный компонент, распространяется как готовый бинарь (Burrito-сборка) через личный кабинет пользователя. Если потребуется работать с его исходниками — уточнить у пользователя расположение репозитория, в dixsu-infra его нет.

Роли и тарифы

Иерархия ролей (по возрастанию): SIMPLE → VIP → PRO → PERS → GLAVA (GLAVA — административная роль). Роль определяет доступ к TCP-портам (см. таблицу в dixu_proxy/README.md), доступ к аналитике (PRO/PERS), и сейчас управляется через систему "умного биллинга" — см. BILLING.md.

Текущее состояние и куда смотреть дальше

  • Полный разбор каждого модуля — MODULES.md
  • Деплой и workflow — DEPLOYMENT.md
  • Защита от абуза (TCP+HTTP rate-limiting, журнал банов) — SECURITY.md
  • Биллинг/тарифы — BILLING.md
  • Полная карта фич + известные пробелы (что сделано, что нет) — FEATURES_AND_GAPS.md
  • Гайды по типовым задачам — guides/
  • Архив устаревших проектных документов (исторические брифы, уже реализованные дизайн-документы) — history/