No description
  • Rust 86.7%
  • Shell 10.6%
  • Dockerfile 2.7%
Find a file
DSvinka a822bb6fb0
All checks were successful
ci / ci (push) Successful in 3m41s
фикс названий профилей
2026-05-30 23:05:02 +03:00
.forgejo/workflows CI Fix 2026-05-30 02:34:37 +03:00
acme фикс ACME 2026-05-30 14:18:12 +03:00
cover-nginx фикс ACME 2026-05-30 14:18:12 +03:00
gateway фикс названий профилей 2026-05-30 23:05:02 +03:00
tt-image фикс ICMP 2026-05-30 22:36:19 +03:00
.gitignore CI 2026-05-29 18:38:17 +03:00
docker-compose.yml фикс названий профилей 2026-05-30 23:05:02 +03:00
README.md фикс названий профилей 2026-05-30 23:05:02 +03:00

CaviCodeVPN Plugin: TrustTunnel

Traffic-плагин для CaviCodeVPN, обеспечивающий маскировку VPN-трафика под легитимный HTTPS через протокол TrustTunnel.

Компоненты

Директория Назначение
gateway/ Rust L4-классификатор на :443. Парсит TLS ClientHello, маршрутизирует по стего-маркеру в Random[0..32]. Без совпадения — splice на сайт прикрытия
tt-image/ Docker-образ TT endpoint + socat sidecar. No-auth режим на loopback, TCP и DNS/UDP идут через SOCKS5 Egress Core
cover-nginx/ Реверс-прокси на настраиваемый внешний HTTPS-сервис (по умолчанию www.wikipedia.org). Pass-through для CDN. ACME HTTP-01 на :80
acme/ Certbot sidecar. Получает и обновляет Let's Encrypt сертификат, рестартит потребителей TLS

Как это работает

Интернет :443
    │
    ▼
Gateway ── маркер совпал ──► tt-shared (TT endpoint)
    │                            │
    └── нет совпадения ──► cover-nginx (маскируемый внешний сервис)
                                 │
                            SOCKS5 Egress (Core)
                                 │
                             Интернет
  1. Клиент подключается к vpn.example.com:443 с deeplink'ом
  2. Gateway читает первый пакет (TLS ClientHello), извлекает Random[0..32]
  3. Проверяет стего-маркер (prefix & mask) — при совпадении splice в TT endpoint
  4. TT endpoint работает без авторизации, прокидывает credentials клиента в SOCKS5 Egress через extended_auth
  5. SOCKS5 Egress валидирует login/password и выпускает TCP CONNECT и UDP ASSOCIATE в интернет

Установка

Плагин устанавливается через панель управления CaviCodeVPN Core:

  1. Откройте Плагины → Установка
  2. Git URL: URL этого репозитория
  3. Имя: tt, Тип: Traffic
  4. Нажмите «Установить» — Core клонирует, соберёт и запустит все 4 контейнера

Настройка

После установки настройте плагин через Плагины → карточка tt → шестерёнка:

Плагин предоставляет собственный интерфейс настроек (iframe) со следующими параметрами:

Параметр Описание Обязателен
cover_hostname Публичный домен VPN: DNS должен указывать на IP сервера; используется для TLS, SNI, deeplink и TOML профилей Да
cover_target_hostname Маскируемый внешний HTTPS-сервис для обычных запросов без TT-маркера, например www.wikipedia.org Нет, по умолчанию www.wikipedia.org
acme_email Email для уведомлений Let's Encrypt Для боевого ACME
acme_staging Тестовый режим LE (без rate limit, сертификат не доверен) Нет
marker_length Длина стего-маркера в байтах (по умолчанию 6) Нет
marker_percent Плотность значимых бит маски в % (по умолчанию 70) Нет
rotation_interval_days Период ротации маркеров (по умолчанию 30) Нет
grace_period_days Overlap при ротации (по умолчанию 7) Нет
project_name Название проекта в имени профиля подключения (по умолчанию CaviCodeVPN) Нет
node_name Название ноды в имени профиля подключения (по умолчанию Germany) Нет
tt_icmp_enabled Прокидывание ICMP/ping через TrustTunnel endpoint (по умолчанию включено) Нет
tt_icmp_interface Сетевой интерфейс контейнера shared для raw ICMP socket (по умолчанию eth0) Нет

После сохранения настроек нажмите Перезапустить — контейнеры пересоздаются с новыми переменными окружения. ACME контейнер автоматически запросит сертификат для указанного домена.

Протокол Config UI (postMessage)

Iframe плагина взаимодействует с Core через window.postMessage:

  • Core → iframe: { type: 'cavicodevpn-plugin-load-settings', settings: {...} } — загрузка текущих настроек при открытии
  • iframe → Core: { type: 'cavicodevpn-plugin-save-settings', settings: {...} } — сохранение
  • iframe → Core: { type: 'cavicodevpn-plugin-request-settings' } — повторный запрос настроек

Передача настроек в контейнеры

Настройки из JSON передаются как env-переменные с префиксом CaviCodeVPN_:

  • cover_hostnameCaviCodeVPN_COVER_HOSTNAME
  • cover_target_hostnameCaviCodeVPN_COVER_TARGET_HOSTNAME
  • acme_emailCaviCodeVPN_ACME_EMAIL
  • acme_stagingCaviCodeVPN_ACME_STAGING (bool: true1, false0)
  • project_nameCaviCodeVPN_PROJECT_NAME
  • node_nameCaviCodeVPN_NODE_NAME
  • tt_icmp_enabledCaviCodeVPN_TT_ICMP_ENABLED
  • tt_icmp_interfaceCaviCodeVPN_TT_ICMP_INTERFACE

Gateway загружает настройки из Core API при старте: GET /api/plugin-api/traffic/settings. Генерация клиентского профиля дополнительно перечитывает актуальные настройки из Core, поэтому сохранённый cover_hostname сразу попадает в deeplink/TOML и не заменяется старым localhost. Для применения cover_target_hostname к nginx-прикрытию перезапустите плагин из Core UI.

Некоторые крупные сайты могут отдавать пустой ответ или антибот-страницу при проксировании из серверной сети. Например, vk.com в части окружений отвечает HTTP 418 с пустым телом. В таком случае смените cover_target_hostname на сервис, который стабильно отдаёт обычную HTML-страницу с IP вашего сервера.

ICMP не проходит через SOCKS5 Egress: SOCKS5 не поддерживает ICMP, поэтому TrustTunnel endpoint отправляет ping через raw socket из контейнера shared. Для этого контейнер получает CAP_NET_RAW, а бинарник trusttunnel_endpoint собран с cap_net_raw.

Генерация клиентских конфигураций

Gateway предоставляет callback для Core:

POST /client-config

Запрос:

{
  "subscriptionId": "guid",
  "login": "subscription-login",
  "plainPassword": "одноразово известный пароль",
  "transport": "http2",
  "filterProfile": {
    "vpnMode": "general",
    "exclusions": ["example.com", "*.example.org", "203.0.113.10"],
    "excludedRoutes": ["203.0.113.0/24"],
    "includedRoutes": null
  }
}

Ответ:

{
  "deeplink": "tt://?...",
  "toml": "vpn_mode = \"general\"...",
  "markerHex": "aabb/ff00",
  "warnings": []
}

TOML содержит client_random, dns_upstreams, vpn_mode, exclusions, [listener.tun].included_routes и [listener.tun].excluded_routes. По умолчанию DNS upstreams — https://1.1.1.1/dns-query и https://8.8.8.8/dns-query, чтобы DNS не зависел от UDP ASSOCIATE; локальные/private маршруты остаются вне TUN. Маркер берётся из локального registry Gateway.

Deeplink исправлен под спецификацию TrustTunnel: upstream_protocol кодируется одним байтом (0x01 = http2, 0x02 = http3), dns_upstreams — TLV-массивом строк, а display name пишется в тег name как {project_name} @{login} ({node_name}). Текущая upstream-спецификация deeplink не содержит тегов для vpn_mode, exclusions и routes, поэтому при непустых фильтрах deeplink/QR возвращаются с предупреждением: фильтры сохраняются только в TOML.

Клиентский bypass остаётся основным механизмом защиты локальных ресурсов пользователя: Desktop применяет exclusions и routes из TOML. Core дополнительно привязывает выбранный filter profile к подписке, а SOCKS5 Egress блокирует совпадающие домены/IP/CIDR как серверную страховку для TCP/UDP. Этот блок только предотвращает выход с IP VPN-сервера; TT-плагин не заставляет клиента повторять запрос в обход VPN.

Требования

  • CaviCodeVPN Core запущен и доступен
  • Доменное имя направлено на IP сервера (для ACME сертификата)
  • Порт 443 (TCP) открыт (входящие TLS-соединения)
  • Порт 80 (TCP) открыт и не занят другим сервисом: cover-nginx публикует его для ACME HTTP-01 challenge
  • Для ping/ICMP у shared должен работать CAP_NET_RAW; стандартный compose уже добавляет эту capability.

Docker Compose — сервисы

Сервис Контейнер Описание
gateway cavicodevpn-plugin-tt-gateway Rust L4-классификатор, слушает :443
shared cavicodevpn-plugin-tt-shared TT endpoint + socat, принимает от gateway
cover cavicodevpn-plugin-tt-cover nginx реверс-прокси на выбранный внешний сервис
acme cavicodevpn-plugin-tt-acme Certbot, управляет TLS-сертификатами

Compose использует готовые Docker images из registry, а не локальный build:. Для Forgejo Packages задайте в Core окружение:

CaviCodeVPN_PLUGIN_IMAGE_PREFIX=git.dsvinka.ru/cavicode/
CaviCodeVPN_PLUGIN_IMAGE_TAG=latest

Core прокидывает эти значения в .env плагина при установке, поэтому тяжёлый tt-image собирается в CI и скачивается сервером как cavicodevpn-plugin-tt-shared.

Общие тома: cavicodevpn-acme-certs (TLS сертификаты), cavicodevpn-acme-webroot (ACME challenge), cavicodevpn-acme-work (Let's Encrypt). ACME sidecar использует Docker socket, чтобы перезапустить TLS consumers после выдачи или обновления сертификата.

Связанные репозитории