189 lines
11 KiB
Markdown
189 lines
11 KiB
Markdown
# 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`.
|