# qeli — Модель угроз Документ фиксирует, **от чего qeli защищает, от чего сознательно НЕ защищает и каков текущий уровень проверенности.** Цель — чтобы пользователь мог соотнести qeli со своими рисками, а ревьюер знал, куда смотреть. Это описание замысла, а не гарантия. English version: [`../eng/THREAT-MODEL.md`](../../eng/reference/THREAT-MODEL.md). ## 1. Назначение qeli — это VPN с упором на устойчивость к сетевому анализу трафика (DPI). Основная задача — доставить трафик пользователя до сервера так, чтобы сетевой наблюдатель не мог: (а) прочитать его, (б) незаметно подменить, (в) уверенно определить поток как VPN/qeli и заблокировать его. Приватность **от оператора сервера** в задачи не входит (своему серверу вы доверяете — как и в любом VPN). ## 2. Модели нарушителя, под которые сделан дизайн | Нарушитель | Возможности | Ответ qeli | |-----------|-------------|------------| | **Пассивный DPI на пути** | Читает каждый байт, классифицирует по сигнатуре/энтропии/отпечатку | `reality-tls` использует REALITY TLS 1.3 + настоящий H2 со случайным batching; `obfs` идёт через WebSocket fronting; nonce PRP защищает счётчик private records. Полный browser/H2 semantic parity не достигнут и не заявляется. | | **Активное зондирование на пути** | Сам инициирует/реплеит соединения к серверу, проверяя, не прокси ли это | REALITY: соединение без валидного крипто-токена в `session_id` ClientHello прозрачно проксируется на реальный сайт-декой. Переотправленные (replay) ClientHello детектируются и тоже бриджуются. Это убирает очевидный active-probe oracle, но не доказывает универсальную эквивалентность target при анализе таймингов и корреляции. | | **Активный MITM на пути** | Перехватывает и переписывает записи хендшейка | Доказательство идентичности сервера привязано к транскрипту хендшейка (channel binding): подмена ServerHello/Certificate/Finished ломает proof. Опция `bind_static_to_session` (вкл. по умолчанию) привязывает сессионные ключи к долговременной идентичности сервера (стиль Noise-IK). | | **Запись-сейчас-расшифровка-потом / квантовый** | Пишет трафик сегодня, ломает X25519 будущим квантовым компьютером | Все режимы кроме `plain` выполняют гибридный обмен X25519 + ML-KEM-768; ключи данных зависят **от обоих** секретов. Сервер отклоняет не-PQ хендшейк (без тихого даунгрейда). | | **Онлайн-подбор паролей** | Брутфорс пароля пользователя или админа панели | Argon2id; per-IP-локаут + адаптивный per-username tarpit на туннеле; то же на API веб-панели; constant-time сравнение proof; работа с dummy-хэшем для неизвестных юзеров (нет энумерации по таймингу). | | **Replay-атака** | Переотправляет перехваченный шифртекст | Скользящее окно replay 2048 бит на сессию; AEAD с уникальными nonce на пакет. | | **Локальный непривилегированный пользователь клиента** | Пытается прочитать секреты или перехватить привилегированные записи qeli | Секреты/конфиг/ключи пишутся атомарно с `O_EXCL` + `O_NOFOLLOW` и сохранением прав `0600`; control-сокет закрыт каталогом `0700`. | PCAP-регрессия 2026-08-26 дала 0/6 обнаружений старым qeli-shape classifier, а не универсальную вероятность. Остаточные passive-сигналы: согласованность настоящего browser TLS profile, H2 SETTINGS/priority/window, один долгоживущий POST и timing нагрузки. См. [DPI-AUDIT.md](../reports/DPI-AUDIT.md). ## 3. Не-цели и остаточные утечки (ВАЖНО) qeli **не** претендует на защиту от перечисленного. Часть — фундаментальна, часть — осознанный инженерный компромисс. 1. **Глобальная пассивная корреляция трафика.** Наблюдатель, видящий оба конца пути, может сопоставить поток по **таймингу и объёму** пакетов. Паддинг и нормализация трафика повышают стоимость, но не побеждают истинно глобального пассивного наблюдателя. qeli — не высоколатентная mix-сеть. 2. **DNS-метаданные при включённом kill-switch.** Linux kill-switch разрешает порт 53 (UDP/TCP), чтобы при реконнекте можно было заново разрешить *имя* сервера. Пока туннель опущен, DNS-запросы (только метаданные, не плоскость данных) могут проходить по физическому каналу. Чтобы закрыть и это — указывайте адрес сервера как **IP**. Плоскость данных и реальный IP к произвольным сайтам остаются заблокированы. (См. `qeli/src/client/killswitch.rs`.) 3. **Покрытие kill-switch.** На каждой десктоп-платформе — свой нативный fail-safe kill-switch: Linux **iptables** (Rust-ядро, `qeli/src/client/killswitch.rs`), Windows **WFP** (`New-NetFirewallRule` + default-block исходящего, `qeli-win/QeliWin/Vpn/KillSwitch.cs`), macOS **pf** (`qeli-mac/QeliMac/Vpn/KillSwitch.cs`). Все разрешают только tun-устройство, IP сервера, DNS и DHCP и остаются включёнными после краша (хост заблокирован — без утечки — пока qeli не запустится снова). Остаточный DNS-компромисс (пункт 2) применим ко всем. На **Android** используйте системный *Always-on VPN + блокировка соединений без VPN*. 4. **Компрометация конечной точки.** Вредонос, враждебная ОС, скомпрометированное клиентское устройство или принуждённый/забэкдоренный сервер — вне области. qeli защищает байты на проводе, а не скомпрометированную конечную точку. 5. **Доверие оператору сервера.** Сервер видит назначение вашего расшифрованного трафика (он — точка выхода). Поднимайте свой. 6. **Режим `plain` — не анти-DPI.** Это голый шифрованный туннель (высокая энтропия с нулевого байта) для уже доверенных сетей или бенчмарка — он сам по себе сигнал для энтропийного детектора. Против активной цензуры используйте `obfs` / `reality-tls`. 7. **Анонимность.** qeli — не Tor. Он шифрует содержимое и стремится скрыть VPN-отпечаток от описанных выше моделей DPI; гарантии, что цензор не классифицирует поток, нет. Он также не анонимизирует вас от целеустремлённого глобального наблюдателя или от вашего сервера. 8. **Опциональная проверка обновлений — ВЫКЛ по умолчанию, раскрыта здесь, чтобы никогда не была «скрытой».** В qeli **нет телеметрии**. Единственный встроенный запрос к проекту qeli или сервису разработчика — *opt-in* проверка обновлений (Настройки на клиентах, `[web] update_check` в панели или `qeli version --check`). Если вы её включите, клиент делает **один неаутентифицированный GET** публичных метаданных релизов GitHub (`/repos/litvinovtd/qeli/releases`) с **обезличенным User-Agent**, не отправляя ничего, что идентифицирует вас или устройство; сравнение версий — локальное. На GUI-клиентах запрос делается **только при поднятом туннеле**, и он идёт по таблице маршрутов ОС. **Внимание: «туннель поднят» ≠ «запрос пошёл в туннель».** В full-tunnel — да, GitHub увидит IP сервера. В **split-tunnel** (а это дефолт на CLI и десктопе) или если GitHub попал под `exclude`, запрос уйдёт напрямую и GitHub увидит ваш реальный IP. Хотите гарантию — включайте проверку обновлений только на full-tunnel-профиле. У CLI-команды `qeli version --check` гейта по туннелю нет вовсе: она выполняет запрос когда угодно. Проверка панели выполняется в браузере оператора, а не серверным процессом. Это **только уведомление** — ничего не скачивается и не устанавливается. Если оставить ВЫКЛ (по умолчанию), qeli не открывает сокет проверки обновлений. Остаточный след при включении — сигнал «этот хост запросил у GitHub репозиторий qeli» тому, кто видит запрос (внутри туннеля — это апстрим вашего же выхода). 9. **Автоопрос доступности профилей — ВЫКЛ по умолчанию (opt-in).** Windows, macOS и Android периодически обращаются ко всем настроенным VPN endpoint'ам только после явного включения этой функции, чтобы показать цвет точки и задержку. Интервал по умолчанию после включения — 30 секунд, допустимый диапазон — 10–3600 секунд; автоматические sweep не выполняются при подключающемся или поднятом туннеле, а Android дополнительно требует видимого приложения. Для TCP выполняется ограниченный connect, для UDP — credential-free первый пакет протокола. Эти проверки ничего не отправляют проекту qeli, но их время видят настроенные серверы и наблюдатель на пути. При выключенном автоопросе сетевые пробы запускаются только явной ручной проверкой. 10. **Настроенный исходящий трафик сервера.** Handrolled-профиль `reality-tls` проверяет настроенный target при старте и раз в 12 часов, обновляя заимствованный сертификат/форму. DNS-forwarding и опциональные уведомления/webhook также выполняют явно настроенные сетевые запросы. Это не телеметрия, но egress-политика оператора должна их учитывать. ### 3.1. Linkability roaming при смене пути Roaming сохраняет одну аутентифицированную VPN-сессию при смене физической сети. Он улучшает непрерывность, но не делает старый и новый пути несвязываемыми. - **UDP на проводе.** Каждый зафиксированный путь получает новый направленный восьмибайтовый CID. Это убирает стабильный открытый qeli-идентификатор между путями, но не timing/volume signals. - **TCP на проводе.** JOIN/resume proof и стабильный session locator передаются только внутри нового зашифрованного carrier; повторно используемый resume token не выходит в cleartext. - **Кто может связать пути.** Сервер неизбежно присоединяет оба peer address к одной аутентифицированной сессии, чтобы сохранить TUN-адрес и NetworkPlan. Наблюдатель, который видит обе сети, всё ещё может сопоставить переход по времени, объёму или make-before-break overlap. Ротация CID убирает тривиальный идентификатор, но не является гарантией анонимности. ### 3.2. Остаточные риски dual-stack, TAP и фрагментации - **Смешение outer/inner семейств.** IPv6 до сервера не означает IPv6 внутри туннеля и наоборот. Ошибка capability/NetworkPlan может дать silent downgrade; режим `required` и generation ACK должны превращать его в отказ. `allow_*_leak` — явное принятие утечки. - **TAP control plane.** qeli не является L2-мостом, но локальные ARP/NDP/RA ответы всё равно являются недоверенным входом. Неизвестные EtherType, malformed NDP/RA, VLAN и oversized frames должны отбрасываться; TAP-матрица обязательна отдельно от обычного TUN. - **DATA_FRAG DoS.** Аутентифицированный peer может создавать множество незавершённых reassembly. Bounded bytes/fragments/entries/expiry ограничивают ущерб, но не устраняют CPU/память как поверхность DoS; нужны fuzz, quota/drop metrics и soak. - **PMTU/ICMPv6.** Потеря Packet Too Big или фильтрация ICMPv6 создаёт black hole; поддельный PTB может снизить throughput. Probe и PMTU state должны быть привязаны к path/family/source. - **NAT66 и routed GUA.** NAT66 скрывает адреса, но не заменяет firewall; routed GUA делает клиентские адреса глобально маршрутизируемыми и требует явной upstream routing/ACL policy. - **Persist TUN.** Повторное использование интерфейса допустимо только при полном совпадении fingerprint NetworkPlan (семейства, адреса, маршруты, DNS, MTU и leak policy). ## 4. Уровень проверенности - **Независимый внешний аудит: ПОКА не проведён.** Самая большая поверхность атаки — **самописный стек TLS 1.3** (`qeli/src/protocol/realtls/*`, ~3k строк), написанный, чтобы побайтово контролировать сетевой отпечаток и кросс-компилиро- ваться без `ring`. Прошёл внутреннее ревью и покрыт юнит-тестами, но без аудита третьей стороной. Относитесь соответственно. - **Fuzzing:** харнесы для парсеров недоверенного ввода (ClientHello, кодек пакетов, realtls-записи и roaming wire contracts) лежат в [`qeli/fuzz/`](../../../qeli/fuzz). Непрерывное покрытие фаззингом наращивается. - **Тесты:** обширный набор юнит-тестов в дереве (крипто-векторы, окно replay, привязка транскрипта хендшейка, round-trip конфигов) как обязательный merge-гейт CI вместе с `clippy -D warnings` и `rustfmt`. - **Воспроизводимость:** готовые нативные ядра сейчас закоммичены в репозиторий для удобства клиентов; переход на публикуемые артефакты с контрольными суммами и reproducible build отслеживается в [`ROADMAP.md`](../plans/ROADMAP.md). - **Гигиена памяти (принятое ограничение):** долгоживущие секреты зануляются на Drop (статические/эфемерные ключи X25519, IKM для HKDF). А вот *транзитные* сессионные AEAD-ключи внутри realtls TLS-record объектов (развёрнутый key schedule `aes-gcm`) НЕ зануляются — крейт `aes-gcm` не реализует `Zeroize`, а расширенные round-ключи живут внутри его объекта шифра, недостижимы из qeli. Это принятый defense-in-depth- долг: кто может читать освобождённую кучу процесса, тот уже читает *живые* ключи во время сессии — модель угроз не меняется. Вернуться при отдельном проходе по memory-hygiene или смене крипто-крейта. ## 5. Если от этого зависит ваша безопасность Тогда: пиньте идентичность сервера (`require_client_key_proof`), используйте **IP** (не имя) адрес сервера с включённым kill-switch, режим `reality-tls`, держите все компоненты на одной выпущенной версии и осознавайте пункты 1 и 4 выше. И пока у qeli нет завершённого независимого аудита — предпочитайте инструменты, у которых он есть.