# qeli — Модель угроз Документ фиксирует, **от чего qeli защищает, от чего сознательно НЕ защищает и каков текущий уровень проверенности.** Цель — чтобы пользователь мог соотнести qeli со своими рисками, а ревьюер знал, куда смотреть. Это описание замысла, а не гарантия. English version: [`../eng/THREAT-MODEL.md`](../eng/THREAT-MODEL.md). ## 1. Назначение qeli — это VPN с упором на устойчивость к сетевому анализу трафика (DPI). Основная задача — доставить трафик пользователя до сервера так, чтобы сетевой наблюдатель не мог: (а) прочитать его, (б) незаметно подменить, (в) уверенно определить поток как VPN/qeli и заблокировать его. Приватность **от оператора сервера** в задачи не входит (своему серверу вы доверяете — как и в любом VPN). ## 2. Модели нарушителя, под которые сделан дизайн | Нарушитель | Возможности | Ответ qeli | |-----------|-------------|------------| | **Пассивный DPI на пути** | Читает каждый байт, классифицирует по сигнатуре/энтропии/отпечатку | Режимы мимикрии: `reality-tls` отдаёт побайтово-точный Chrome TLS 1.3 ClientHello (паритет JA3/JA4) и JA3S, зеркалированный с реального сайта; `obfs` идёт в WebSocket-fronted канале; PRP-перестановка nonce убирает признак счётчика «+1 на пакет». | | **Активное зондирование на пути** | Сам инициирует/реплеит соединения к серверу, проверяя, не прокси ли это | REALITY: соединение без валидного крипто-токена в `session_id` ClientHello прозрачно проксируется на реальный сайт-декой — сервер неотличим от этого сайта. Переотправленные (replay) ClientHello детектируются и тоже бриджуются. | | **Активный 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`. | ## 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 от цензора*; он не анонимизирует вас от целеустремлённого глобального наблюдателя или от вашего сервера. 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» тому, кто видит запрос (внутри туннеля — это апстрим вашего же выхода). ## 4. Уровень проверенности - **Независимый внешний аудит: ПОКА не проведён.** Самая большая поверхность атаки — **самописный стек TLS 1.3** (`qeli/src/protocol/realtls/*`, ~3k строк), написанный, чтобы побайтово контролировать сетевой отпечаток и кросс-компилиро- ваться без `ring`. Прошёл внутреннее ревью и покрыт юнит-тестами, но без аудита третьей стороной. Относитесь соответственно. - **Fuzzing:** харнесы для парсеров недоверенного ввода (ClientHello, кодек пакетов, realtls-записи) лежат в [`qeli/fuzz/`](../../qeli/fuzz/). Непрерывное покрытие фаззингом наращивается. - **Тесты:** обширный набор юнит-тестов в дереве (крипто-векторы, окно replay, привязка транскрипта хендшейка, round-trip конфигов) как обязательный merge-гейт CI вместе с `clippy -D warnings` и `rustfmt`. - **Воспроизводимость:** готовые нативные ядра сейчас закоммичены в репозиторий для удобства клиентов; переход на публикуемые артефакты с контрольными суммами и reproducible build отслеживается в [`ROADMAP.md`](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 нет завершённого независимого аудита — предпочитайте инструменты, у которых он есть.