Files
elmprodvpn/docs/phase-e/E1_MULTI_CLIENT_PBR_DESIGN.md

189 lines
11 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.
# E1 Дизайн multi-client маршрутизации (PBR)
Дата: 2026-03-04
Статус: draft
Владелец: Engineering
## 1) Цель
- Спроектировать расширение текущего ядра так, чтобы несколько transport-клиентов (`sing-box`, `dnstt-client`, `phoenix->slipstream`) работали под единым control-plane API.
- Сохранить один источник истины для маршрутизации: только Go-ядро управляет PBR/nft/ip rule, UI только вызывает API.
- Обеспечить безопасную многоклиентную схему без конфликтов: один трафик/сайт не должен одновременно идти через два интерфейса.
## 2) Текущий baseline (по коду)
- Сейчас модель маршрутизации бинарная: `vpn|direct` + глобальные `MARK`/`MARK_APP`/`MARK_DIRECT`/`MARK_INGRESS`.
- Runtime app routing хранится в `traffic-appmarks.json`, persistent launcher-профили в `traffic-app-profiles.json`.
- Центр оркестрации: `traffic_mode.go`, `traffic_appmarks.go`, `routes_update.go`.
- Ограничение: нет сущности "клиент-транспорт" как объекта с собственным iface/table/mark.
## 3) Архитектурный инвариант
- `PBR Engine` в Go-ядре остается единственным writer для:
- `ip rule`,
- `ip route table`,
- `nft chains/sets/rules`,
- `conntrack mark policy`.
- Transport backends (sing-box/dnstt/phoenix) подключаются через backend-адаптеры и не пишут маршруты напрямую.
- UI (desktop/web/iOS/android) оперирует одинаковым API-контрактом, не знает о внутреннем устройстве backend-клиентов.
## 4) Целевая модель данных
### 4.1 TransportClient
- `id`: стабильный ключ (`sb-main`, `dnstt-home`, `phoenix-eu`).
- `kind`: `singbox | dnstt | phoenix`.
- `enabled`: bool.
- `status`: `starting | up | degraded | down`.
- `iface`: фактический интерфейс/туннель.
- `routing_table`: имя таблицы (`agvpn_<id>`).
- `mark_hex`: выделенная fwmark клиента.
- `priority_base`: базовый диапазон `ip rule pref` для клиента.
- `capabilities`: `tcp`, `udp`, `dns_tunnel`, `ssh_tunnel`.
- `health`: last_check, latency, last_error.
### 4.2 RouteIntent
- Нормализованная запись назначения трафика к клиенту:
- `selector_type`: `domain | cidr | app_key | cgroup | uid`.
- `selector_value`: значение селектора.
- `client_id`: целевой клиент.
- `priority`: порядок применения.
- `mode`: `strict | fallback`.
### 4.3 ConflictRecord
- Запись о конфликте маршрутизации:
- `key`: нормализованный ключ пересечения.
- `owners`: список `client_id`.
- `severity`: `warn | block`.
- `reason`: человеко-читаемая причина.
- `suggested_resolution`: автоматическая подсказка.
## 5) Схема марков и таблиц (без конфликтов)
### 5.1 Mark allocator
- Ввести менеджер выделения марков из пула, например:
- `0x100-0x1FF` для client-specific route marks,
- `0x66/0x67/0x68/0x69` оставить для legacy/системных сценариев.
- Для каждого `client_id` выделяется:
- `client_mark`,
- `client_reply_mark` (если нужен отдельный ingress stickiness).
### 5.2 Table allocator
- Для каждого клиента заводится отдельная routing table:
- `agvpn_sb_main`, `agvpn_dnstt_home`, `agvpn_phoenix_eu`.
- В таблице только default route через iface клиента + локальные bypass-правила.
### 5.3 Rule priority allocator
- Для каждого клиента выделяется непересекаемый диапазон `pref`:
- пример: `13000-13049` клиент A, `13050-13099` клиент B.
- Это исключает перетирание правил между клиентами при apply/reconcile.
## 6) Защита от "мешанины" трафика
### 6.1 Destination ownership lock
- Один домен/cidr/app_key в активной конфигурации может иметь только одного владельца (`client_id`).
- При пересечении:
- по умолчанию `block` (HTTP `409` на apply),
- опционально `force_override` с явным подтверждением пользователя.
### 6.2 Flow stickiness (conntrack)
- Для первого пакета потока проставляется `ct mark = client_mark`.
- Для последующих пакетов mark восстанавливается из `ct mark`, чтобы один и тот же flow не перескакивал между интерфейсами.
- Правило действует в `output` и `prerouting`, аналогично текущему ingress-reply подходу.
### 6.3 DNS/IP coherence
- Для domain-based маршрутизации вводится owner-cache:
- `domain -> client_id -> ip set` с TTL.
- Один и тот же домен в активной политике не может одновременно резолвиться в разные клиентские set-цепочки.
### 6.4 Audit/guardrail
- Расширить `traffic_audit` на multi-client проверки:
- duplicate destination ownership,
- overlap CIDR между клиентами,
- app_key на двух клиентах одновременно,
- nft/rule drift по client chains.
## 7) UX дизайн (удобное добавление/переключение)
### 7.1 Экран "Клиенты"
- Список клиентов: имя, тип, статус, интерфейс, health.
- Действия: `Добавить`, `Включить/Выключить`, `Перезапустить`, `Удалить`.
- Мастер добавления:
- Шаг 1: тип клиента (`sing-box`, `dnstt`, `phoenix`),
- Шаг 2: параметры подключения,
- Шаг 3: health-check,
- Шаг 4: назначение default policy.
- Подпункты UX для engine:
- единый селектор `Active engine` с вариантами `singbox|dnstt|phoenix`;
- быстрые действия `Connect`, `Disconnect`, `Switch to ...`;
- явное отображение `desired_engine` vs `active_engine`;
- при деградации показывать `last_error` и action `Rollback to previous engine`.
### 7.2 Экран "Маршрутизация"
- Матрица `Селектор -> Клиент`.
- Массовое назначение списков IP/CIDR/доменов.
- Быстрый переключатель "перенести селектор на другой клиент" с dry-run проверкой конфликтов.
### 7.3 UX предупреждения
- Перед apply показывать diff:
- какие селекторы сменят владельца,
- какие потоки могут быть прерваны,
- какие конфликты заблокируют применение.
- При `force_override` обязательное подтверждение пользователя с явным риском:
- "Один и тот же сайт может потерять стабильность при частой смене интерфейса".
- При switch/connect engine:
- показывать предупреждение о кратковременном разрыве активных сессий;
- запрещать параллельные mutating-операции до завершения текущего switch;
- при failed switch предлагать rollback на предыдущий engine.
## 8) API-контракт (новые ручки, проект)
- `GET /api/v1/transport/clients`
- `POST /api/v1/transport/clients`
- `POST /api/v1/transport/clients/{id}/start`
- `POST /api/v1/transport/clients/{id}/stop`
- `GET /api/v1/transport/clients/{id}/health`
- `GET /api/v1/transport/policies`
- `POST /api/v1/transport/policies/validate`
- `POST /api/v1/transport/policies/apply`
- `GET /api/v1/transport/conflicts`
- `GET /api/v1/transport/capabilities`
Принцип:
- Все операции изменения policy идут через `validate -> apply`.
- `apply` атомарный: либо вся новая политика применена, либо rollback на предыдущую snapshot-конфигурацию.
## 9) Реализация по шагам
### E1.1 Контракты и состояние
- Ввести state-файлы:
- `transport-clients.json`,
- `transport-policies.json`,
- `transport-conflicts.json`.
- Добавить DTO и минимальные read endpoints.
### E1.2 PBR compiler v2
- Реализовать компиляцию RouteIntent в:
- nft sets/chains per client,
- ip rule pref ranges per client,
- table route entries per client.
### E1.3 Guardrails
- Валидация ownership/overlap до apply.
- Conntrack stickiness rules для стабильности flow.
### E1.4 UX-ready слой
- API предупреждений + dry-run diff.
- SSE события:
- `transport_client_state_changed`,
- `transport_policy_applied`,
- `transport_conflict_detected`.
## 10) Критерии готовности дизайна
- Можно добавить 2+ клиентов и поднять 2+ интерфейса без перезаписи чужих rule/table.
- Нельзя назначить один и тот же selector двум клиентам без explicit override.
- `traffic_audit` показывает целостную картину конфликтов и drift.
- UI получает понятные предупреждения до применения рискованной конфигурации.
## 11) Обратная совместимость
- Текущие `/api/v1/traffic/*` продолжают работать в legacy-режиме.
- При отсутствии multi-client политики ядро использует текущий single-client pipeline.
- Миграция: legacy `vpn|direct` может быть автоматически представлена как:
- `client_id=legacy-vpn`,
- `client_id=legacy-direct`.