ostp/REBUILD_PLAN.md

186 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Чистая переборка на базе v0.2.98
База: `v0.2.98` (commit `31d0020`) — последняя версия, которая **стабильно работает**.
Ветка: `clean-rebuild`. Всё, что появилось после (0.3.1 … 0.3.21), переносим
**выборочно и с чистой головой**, а не копируем рефактор целиком.
Принцип: 0.3.1 принёс «модульный multi-server рефактор» + лавину фич — и вместе с
ними нестабильность. Берём только проверенное и нужное.
---
## Решения (зафиксировано пользователем)
- **Junk-пакеты + TCP-фрагментация — ОСТАВЛЯЕМ** (нравятся). НО починить вредную
часть: junk по UDP не должен выглядеть для сервера как `Unauthorized probe`
(rate-limit/гейт на сервере), иначе флуд лога и риск самобана клиента. Фича
остаётся — чиним поведение, а не выпиливаем. Тонкая настройка — §E.
- **Версия переборки — 0.4.0** (решено; 0.3.x сожжены в pre-release).
- **WSS и Reality (TLS-мимикрия) — ВЫКИНУТЬ.** Путь проекта — **zapret-like**:
обфускация/DPI-evasion на уровне пакетов (junk, фрагментация, обфускация), а НЕ
мимикрия под TLS. Reality с нуля тяжела и не вписывается.
- **Multi-server — НЕ НУЖЕН.** Режем до одного сервера → уходит urltest-группа и
половина сложности 0.3.1.
- **Конфиг — ПЛОСКИЙ по сути, но оформлен красиво/секционно как сейчас** (решено).
Сохраняем читаемую секционную структуру (server / transport / tun / dns / exclude
и т.п.), но **выпиливаем модульную машинерию**: массивы `inbounds[]`/`outbounds[]`,
`routing.rules[]` с тегами, `default_outbound`, urltest, мульти-сервер. Один сервер
на конфиг. Исключения = плоский список внутри секции `exclude`.
- **Профили — ОСТАВЛЯЕМ, single-select, в UI-слое** (решено). Профиль = сохранённый
конфиг одного сервера; активен ровно один (radio). Список/выбор/share живут во
фронте (prefs GUI / Flutter); **ядро о профилях не знает** — на «Подключить» из
выбранного профиля генерится плоский конфиг на один сервер. Никаких чекбоксов/
мульти-актив/urltest.
- **Derived-secrets — ОСТАВЛЯЕМ, но ОБЯЗАТЕЛЬНО проверить, что он РЕАЛЬНО работает:**
старый клиент НЕ должен подключаться к новому серверу. В прошлой реализации это
НЕ соблюдалось (старый клиент → новый сервер подключался) — значит сервер всё ещё
принимал старый формат handshake / obfuscation-key. Это **баг**, закрыть в первую
очередь: сервер обязан отвергать всё, что не прошло derived-secrets.
- **Лицензия — AGPLv3.**
- **Брендинг — ОСТАВЛЯЕМ**: тёмная тема + орёл на фоне (watermark/логотип).
- **Стелс-философия (north-star): zapret-like** — «нет узнаваемого заголовка +
манипуляции пакетами» (обфускация, junk, фрагментация, DNS/UoT-транспорты), а НЕ
«притворись известным протоколом» (Reality/WSS — выкинуты).
---
## 0. Корневая причина нестабильности 0.3.x
**Модульный multi-server рефактор (0.3.1)** — `580faf6`, `8ed66f9`, `67f9c06`.
Сменил формат конфига (inbounds/outbounds/routing/urltest), session-модель,
hot-reload. Источник большинства багов (мёртвые маршруты, фейк-коннект,
рассинхрон конфига). **НЕ копировать целиком.** Если multi-server реально нужен —
добавлять минимально и поверх рабочей одно-серверной модели 0.2.98.
---
## A. ВЫКИНУТЬ / не переносить
1. **WSS-фрейминг и Reality (TLS-мимикрия)**оба выкинуть. Путь zapret-like, а не
маскировка под TLS-сайт; Reality (`reality.rs`) к тому же сложно сделать корректно
с нуля. Удалить из базы 0.2.98 целиком.
2. **Multi-server / urltest-группа** — не нужен. Один сервер на конфиг.
3. Остатки **tun2socks** на Android (`libtun2socks.so`, `tun2socks-arm64`,
`tun_child`, `t2sBinPath`) — давно мёртвый код, только раздувает APK. Не тащить.
> ⚠️ Junk-пакеты и TCP-фрагментация **ОСТАЮТСЯ** (см. Решения и §E) — это уже не
> «мусор». Но junk по UDP нужно сделать так, чтобы сервер его не считал
> `Unauthorized probe` (rate-limit/гейт), иначе лог-флуд и риск самобана.
---
## B. ОБЯЗАТЕЛЬНО перенести (фиксы стабильности)
- **fd limits / EMFILE** — `922cf0b`.
- **Lifecycle хелпера**: принудительный `std::process::exit` после остановки, чтобы
не оставался зомби-процесс, держащий адаптер `ostp_tun` и дефолтный маршрут — `b6e78c1`.
- **Bypass-маршрут сервера через `route.exe` по шлюзу** (а не legacy
`CreateIpForwardEntry`, который падал с err 160 из-за рассинхрона индексов
интерфейсов) — `b6e78c1`.
- **IPC хелпера** (ChaCha20Poly1305 + hex) + **единый формат логов**`ee38b15`.
- **Closing-state fix** + `sent_history` на `BTreeMap` (O(log n) NACK) — `47d44fa`.
- **Handshake timeout fixes** — `d65af35`, `6eb7b36` (ждать ответ до отправки данных).
- **Buffer / UDP handler** — `b5e830a`.
- **Логи**: UoT и unauthorized-probe → debug; rate-limit probe-лога — `1151726`, `fc339b3`.
---
## C. Протокол / крипто — решить и перенести
- **Derived secrets handshake** — `f8f27d3`. PSK и obfuscation-key выводятся из
access-key через HKDF; handshake-payload = `[timestamp][session_id][access_key]`;
параметры паддинга деривируются; timestamp anti-replay (±300с).
⚠️ **Ломает совместимость с 0.2.98 wire** (старый клиент не подключится).
Безопаснее старого (raw-PSK + нулевой obfuscation-key). **РЕШЕНИЕ:** переносим ли
(тогда нужен ребилд всех клиентов) — ДА, скорее всего, но осознанно.
- **l4_protocol** для server outbound — `2997bfd`, `ad3a8cb`, `aae9d22`.
---
## D. Транспорты — перенести аккуратно (большие куски)
- **DNS transport (dnstt)** как fallback — `3f1adbc`, `3ced4a1`, `d031b15`,
`10c1772`, `b31da29`. Полезно против блокировок, но объёмно и со своей
фрагментацией/reassembly. Переносить отдельным изолированным модулем.
- UoT (UDP-over-TCP) — уже есть в 0.2.98, проверить что не сломан.
---
## E. Тонкая настройка junk/фрагментации (как в AmneziaWG)
Junk и фрагментацию **оставляем** (Решения), а это — их параметризация. Главное
условие: **координация клиент↔сервер**, иначе junk превращается в probe-флуд.
- `Jc` — кол-во junk-пакетов, `Jmin`/`Jmax` — размеры; **сервер знает и молча отбрасывает**.
- `S1`/`S2` — размеры init/response подгоняются.
- Магические заголовки/сигнатуры пакетов (`H1..H4`).
Реализовать как явные настраиваемые поля (не хардкод). Сервер ОБЯЗАН их понимать.
Сам факт junk/frag — в базе; это «желание» — сделать их настраиваемыми. Можно потом.
---
## F. GUI (desktop) — перенести нужное, без хаоса
- Профили на странице **настроек** (пусто + «Create a new profile» + «+» когда нет
профиля; «+» → меню «из ссылки / вручную»). Главный экран не усложнять.
- **Share** профиля: QR (генерить локально, ключ наружу не отдавать — крейт `qrcode`)
+ копируемая `ostp://` ссылка.
- **Метрики**: байты считать в TUN-инбаунде; rtt брать из round-trip handshake
(а не отдельным TCP-probe).
- **Health/состояние**: «connected» по реальной достижимости сервера на ПРАВИЛЬНОМ
порту (не хардкод :443), а не по факту «процесс запустился».
- **routing**: всегда задавать `default_outbound: "proxy"`; ключи правил —
`domain_suffix` / `ip_cidr` / `process_name` (не `domains/ips/processes`).
- Смена сервера = полный **stop+start**, а не hot-reload (иначе остаётся старый сервер).
- Никаких непрогарженных `addEventListener` на удалённые элементы (краш init).
---
## G. Мобилка (Flutter + JNI) — перенести нужное
- **routing**: тот же `default_outbound` + правильные ключи правил
(без них трафик шёл мимо туннеля — реальный IP).
- **Байты на Android**: считать в обеих задачах fd-пути (read=upload, write=download).
- **rtt**: из handshake (health-probe сокет на Android не protected → до сервера не доходит).
- **Смена сети (WiFi↔LTE)**: реальный reconnect (сейчас `notifyNetworkChanged` — no-op).
- **fd ownership**: НЕ двойное закрытие (Rust `OwnedFd` + Kotlin `close()`) → `detachFd()`.
- **Share** профиля: QR (`qr_flutter`) + ссылка.
- Выкинуть tun2socks (см. §A.3).
---
## H. Инфра / лицензия / брендинг
- Лицензия: **AGPLv3** ✅ (зафиксировано). В 0.2.98 был BSL 1.1 → заменить (`9ce9e6d`).
- **Брендинг — ОСТАВЛЯЕМ** ✅: тёмная тема + орёл на фоне (watermark/логотип) в GUI.
Перенести из текущего `ostp-gui` (assets/logo.svg, тёмная палитра) в чистую переборку.
- Панель/license-check: open-source без license-check — `5782107`, `99ff76d` (если нужно).
- Версионирование/CI build-script — `774d926` и пр.
---
## Инвентаризация базы 0.2.98 (что уже есть / что портировать)
- **Есть в 0.2.98**: WSS (→ удалить), Reality/`reality.rs` (→ удалить),
инфра derived-secrets (`derive_all_secrets`, `obfuscation_key`) — но клиент юзал
dummy-ключи до `f8f27d3`.
- **Нет в 0.2.98 — портировать из пост-0.2.98 кода**: junk-пакеты, TCP-фрагментация,
DNS-transport (dnstt), фикс derived-secrets `f8f27d3`, все фиксы §B, GUI/мобилка §F/§G.
## Что легко упустить (решить до старта)
1. **Версия переборки — 0.4.0** (решено). Сожжённые 0.3.x не переиспользуем.
2. **Серверный конфиг — тоже плоский** и согласован с клиентским. Сервер обязан
поддерживать всё оставленное: derived-secrets (и **отвергать** старый формат),
корректную обработку junk (не probe-флуд), UoT, DNS-transport, management API.
3. **Версия/магический байт протокола ДО крипто-слоя.** Сейчас нельзя отличить старый
handshake от нового — отсюда баг «старый клиент → новый сервер подключился».
Добавить версию в wire → будущие изменения управляемы, сервер чётко режет
несовместимое. Это системный фикс проблемы derived-secrets.
4. **Клиент и сервер обновляются ВМЕСТЕ** — derived-secrets ломает совместимость,
смешивать старое и новое нельзя. Координировать выкладку.
5. **Reality — выкинуть** (решено; в базе 0.2.98 есть `reality.rs` → удалить целиком).
6. **Verify-loop = критерий «готово».** Каждая фича проверяется реальным тестом, не
«на словах»: connect → `curl` показывает IP **сервера**; старый клиент к новому
серверу **не** подключается; смена сети на мобилке восстанавливает туннель.
## Порядок переборки (предложение, 1 сессия)
1. §B (фиксы стабильности) — на чистый 0.2.98.
2. §C (derived-secrets) — и СРАЗУ проверить: старый клиент к новому серверу НЕ
подключается (в прошлый раз был баг — подключался).
3. **Junk + TCP-фрагментация** — перенести (оставляем), но junk по UDP не должен
читаться сервером как `Unauthorized probe` (rate-limit/гейт на сервере).
4. §F/§G по минимуму (routing, метрики, состояние, share) **+ брендинг** (тёмная
тема, орёл на фоне).
5. §D (DNS transport) — если нужно.
6. ВЫКИНУТЬ: **WSS, multi-server, tun2socks** (§A). §E (тюнинг junk) — позже.
7. Конфиг: плоский по сути, секционно-оформленный, один сервер (РЕШЕНО — без
inbounds/outbounds/routing-движка).