mirror of https://github.com/ospab/ostp.git
186 lines
15 KiB
Markdown
186 lines
15 KiB
Markdown
# Чистая переборка на базе 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-движка).
|