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

Модули

dixu_proxy (Elixir / Phoenix)

~950 строк, 22 файла. Не использует HEEx/EEx-шаблоны — весь HTML, который рендерит сам прокси (страницы заморозки, офлайн), встроен прямо в строки в коде контроллера (это намеренно: страниц всего несколько, выносить в файлы избыточно).

lib/dixu_proxy/
├── accounts/{user,profile_link}.ex     ← Ecto-схемы (read-only с точки зрения прокси, пишет modulauth)
├── application.ex                      ← supervision tree, порядок старта см. ниже
├── repo.ex                             ← Ecto.Repo, общая БД с modulauth
├── cache_body_reader.ex                ← кэширует тело запроса для повторного чтения (нужно где-то в плагах)
├── catalog_registry.ex                 ← ETS-кэш slug'ов каталога (status='approved'), обновляется раз в 2 мин
├── frozen_registry.ex                  ← ETS-кэш замороженных туннелей, грузится из modulauth при старте
├── geo_ip.ex / geo_ip_updater.ex       ← офлайн geo-IP (MaxMind), периодическое обновление базы
├── incident_log.ex                      ← общий журнал банов (TCP + HTTP), см. SECURITY.md
├── metrics_collector.ex                 ← трафик/визиты/гео по туннелям, флаш в Postgres раз в 60с
├── mailer.ex                            ← Swoosh, используется по минимуму
└── tcp/tcp_config.ex                    ← роли/разрешённые TCP-порты на устройство, из БД

lib/dixu_proxy_web/
├── channels/{device_socket,proxy_channel}.ex  ← WS-канал устройства, "proxy:{slug}"
├── client_ip.ex                          ← единая точка доверия X-Forwarded-For (см. SECURITY.md)
├── controllers/
│   ├── proxy_controller.ex              ← основной HTTP relay браузер↔устройство
│   ├── internal_controller.ex           ← internal API для modulauth (см. ниже)
│   └── error_json.ex
├── http/visit_guard.ex                  ← анти-скрейпинг/анти-перебор для HTTP-интерстициала
├── plugs/interstitial_plug.ex           ← страница "вы переходите на ресурс пользователя" + анти-абуз гейт
├── tcp/{tcp_listener,rate_limiter}.ex    ← TCP-relay (7 портов) + защита от перебора/флуда
│   tcp/e2e_raw_listener.ex              ← E2E blind-relay (порт 9443): PROXY protocol v1 → SNI → WS-канал без терминации TLS
├── router.ex / endpoint.ex
└── telemetry.ex

Порядок старта (application.ex)

Repo → MetricsCollector → GeoIpUpdater → CatalogRegistry → DNSCluster → PubSub → 3×Registry (Device/Request/TcpSession) → Endpoint → Tcp.RateLimiter → Tcp.TcpListener → Tcp.E2eRawListener → Http.VisitGuard, плюс два ETS для кулдауна офлайн-уведомлений и FrozenRegistry.init() создаются вручную в start/2 (не GenServer).

Internal API (вызывается modulauth)

internal_controller.ex — без SSL-редиректа, обслуживает запросы от modulauth внутри Docker-сети (не публичный путь). Используется для уведомлений (офлайн/service-down) и аналогичных server-to-server вызовов.


modulauth (Java 22 / Spring Boot 3 WebFlux)

~85 Java-файлов. Полностью реактивный стек (WebFlux + R2DBC), сервер-рендеринг через Thymeleaf — никакого отдельного фронтенд-проекта/SPA, весь UI — это шаблоны + статический JS/CSS внутри самого modulauth.

src/main/java/com/dixsu/server/
├── devices/
│   ├── controller/     ← 27 контроллеров: auth, профиль, каталог, короткие ссылки, TCP-порты, верификация соцсетей, админка...
│   ├── service/        ← 17 сервисов: бизнес-логика (auth, email, captcha, OG-парсинг, верификации соцсетей...)
│   ├── model/          ← User, ProfileLink, ShortLink, CatalogEntry, ReservedSlug, UserAuthProvider
│   └── repository/
├── billing/            ← BillingController, TariffService, TinkoffService (платёжный провайдер), SmartBillingScheduler
├── analytics/           ← AnalyticsController (трафик/визиты, PRO/PERS), AnalyticsCleanupScheduler, экспорт данных
├── security/            ← OAuth2-хендлеры, VK custom-filter (Spring OAuth2 для VK "из коробки" не работает)
├── config/              ← SecurityConfig, OAuth2ClientConfig, RedisConfig, WebConfig
├── global/controller/   ← GlobalErrorHandler
└── utils/

Контроллеры — основные группы

  • Auth: AuthController, AuthApiController, SmsAuthController, CryptoAuthController (MetaMask), MetamaskAuthController, OAuthUnlinkController
  • Верификация соцсетей: GithubVerifyController, TwitterVerifyController, YoutubeVerifyController, RutubeVerifyController — каждая своя цепочка подтверждения владения аккаунтом
  • Профиль/визитка: ProfileApiController, PublicProfileController, AvatarController
  • Туннели: TunnelController, TunnelCheckController, TcpPortController
  • Каталог: CatalogController (подача заявки пользователем), AdminCatalogController (модерация)
  • Короткие ссылки: ShortLinkController, LinkInfoController
  • Отзывы и репутация: ReviewController (/api/reviews/* — submit, summary, comments, complaints, avg-rating, owner dashboard, approve-owner), OtzovikApiController (агрегированная лента и страницы ресурсов Otzovik). Бизнес-логика — ReviewService (submit с highTrust-флагом, upsert без даунгрейда статуса, publicComments/publicComplaints с LEFT JOIN для анонимных, approveAsOwner, scoreForSlug, summary).
  • Прочее: AccountDeletionController, FacebookDataDeletionController (GDPR/комплаенс), ClarityController, LandingController, AdminListsController, AdminReservedSlugController, InternalController

52 Flyway-миграций

src/main/resources/db/migration/V1...V52. Ключевые этапы: V19 — каталог (catalog_entries/catalog_categories), V21-V22 — заморозка/роли туннелей, V23-V25 — TCP-порты и reserved slugs, V26/V28 — биллинг (включая Smart Billing Phase 2), V29-V31 — аналитика трафика/визитов/гео, V34-V35 — нормализация email + функциональный индекс на lower(email), V36 — tunnel_incidents, V37 — private_tunnel_invites, V38-V39 — приватный режим (Фаза 1/2), V39 — private_e2e_tokens, V40 — E2E трафик-статистика, V41 — tunnel compression, V42 — npd_settings, V43-V44 — custom domains, V45 — ls_payments (Lemon Squeezy), V46 — email_subscribers, V47-V52 — система отзывов (resource_reviews с анонимными, комментариями, moderation_status; otzovik_* таблицы, звёздный рейтинг).

Шаблоны (Thymeleaf)

src/main/resources/templates/ — 29 страниц, общий layout-фрагмент fragments/site-layout.html (шапка/футер/мобильное меню). Подробно про добавление страниц/пунктов меню — guides/editing-frontend.md.


nginx

nginx/
├── Dockerfile
├── dixu.conf.template      ← HTTP-роутинг по поддоменам, envsubst при старте контейнера
├── stream.conf.template     ← TCP stream: ssl_preread + SNI routing для E2E (Фаза 2)
└── docker-entrypoint.sh

stream.conf.template — TCP L4-прокси с ssl_preread on: читает SNI из TLS ClientHello без терминации и маршрутизирует: - ~^e2e-dixu_proxy:9443 (E2eRawListener, blind-relay без терминации TLS) - всё остальное → 127.0.0.1:8443 (nginx HTTP-блок, обычная TLS-терминация) Добавляет PROXY protocol v1 (proxy_protocol on) на оба апстрима — восстанавливает реальный IP клиента через хоп (иначе $remote_addr был бы 127.0.0.1).

TCP-туннели (SSH/RDP/VNC/БД) через stream{} не идут — это маскировало бы реальный IP от dixu_proxy; они слушаются dixu_proxy напрямую через Docker port-forwarding (см. SECURITY.md).

dixu.conf.template ставит X-Real-IP/X-Forwarded-For через proxy_set_header ... $proxy_add_x_forwarded_for — важно: эта директива дописывает реальный IP в конец цепочки, а не заменяет заголовок (см. SECURITY.md про связанную с этим уязвимость и её фикс).


device_client (Elixir / OTP — отдельный репозиторий)

websocket-client/device_client_app/ — не часть dixsu-infra. Самодостаточный бинарник (Burrito, 5 платформ) для установки на оборудование пользователя. Документация — README.md в том репозитории.

lib/
├── device_client.ex              ← основной WS-клиент + HTTP-прокси + TCP-relay
└── device_client/
    ├── application.ex            ← OTP Application, парсит аргументы, запускает E2eListener если задан E2E_TOKEN
    ├── e2e_listener.ex           ← локальный TLS-терминатор (127.0.0.1:8443), ACME-driven cert
    ├── acme.ex                   ← оркестрация ACME v2 (RFC 8555) flow
    └── acme/{jws,csr,dns_api}.ex ← JWK/thumbprint/JWS-подпись ES256, CSR с SAN, DNS-01 helper

Ключевые переменные: SERVER_HOST, SERVER_PORT, CA_CERT_PATH, E2E_TOKEN, MODULAUTH_URL, ACME_DIRECTORY_URL, E2E_DATA_DIR, E2E_LOCAL_TLS_PORT.


screenshotter (Node + Playwright)

screenshotter/
├── index.js       ← Express + headless Chromium, рендерит и сохраняет скриншот лендинга устройства
├── Dockerfile
└── package.json

Делает скриншот лендинга каждого активного туннеля для карточки на интерстициальной странице (InterstitialPlug в dixu_proxy её показывает). Хранит файлы в общем volume modulauth_uploads (SCREENSHOTS_DIR=/uploads/screenshots), обновляет раз в сутки + 10 минут после старта контейнера.


Прочие каталоги в репозитории

Путь Назначение
INCIDENT/logs/ Журнал банов (volume на прод), см. SECURITY.md. INCIDENT/incident1.md — отдельный, вручную написанный пост-мортем одного инцидента, не связан с автоматическим журналом
certs/ Локальные сертификаты для разработки (в .gitignore, кроме шаблонов)
TEST/web-server/ Тестовая фикстура — простой веб-сервер с загрузками для проверки туннеля локально, не часть прод-системы
docs/ Эта документация