# Split tunnel Aura Split-tunneling решает для каждого назначения IP, идёт ли пакет **через шифрованный VPN** или **уходит напрямую** (минуя туннель). Это позволяет, например, оставить трафик к RFC1918-сетям локальным, а остальное пустить через Aura — или наоборот. Реализация лежит в крейте `aura-tunnel` (`routes.rs`, `router.rs`, `dns.rs`), статически настраивается секцией `[tunnel.split]` в `client.toml` (`crates/aura-cli/src/config.rs`), а управляется на лету командами `aura route` / `aura status` через admin-сокет (`crates/aura-cli/src/admin.rs`). --- ## Концепция: VPN или DIRECT Каждый исходящий IP-пакет, прочитанный с TUN-устройства, классифицируется в одно из двух действий (`RouteAction`): - **`Vpn`** — зашифровать и отправить пакет по соединению Aura на сервер. - **`Direct`** — выпустить пакет напрямую, минуя туннель. Маршрутизатор (`AuraRouter::run`, `router.rs`) парсит у каждого пакета IP назначения, классифицирует его и диспетчеризует: ``` TUN read --> parse dst IP --> RouteTable.classify(dst) --> Vpn? -> conn.send_packet() \ Direct? -> send_direct() (заглушка в v1) ``` > **Ограничение v1 — `Direct` это заглушка.** Текущая реализация `send_direct` **логирует и > отбрасывает** пакет; реальный raw-socket / реинъекция в стек ОС в объём v1 не входят. Метод уже > объявлен `async` и возвращает `Result`, чтобы реальный путь egress подключился без изменения > вызывающего кода. Путь через VPN полностью работоспособен сквозным образом. Пакеты, у которых > не получилось разобрать назначение (не IPv4/IPv6 или слишком короткие), отбрасываются с trace- > сообщением. Входящее направление прямолинейно: расшифрованные IP-пакеты, полученные от пира, пишутся обратно на TUN-устройство. --- ## Правила Таблица маршрутизации (`RouteTable`, `routes.rs`) хранит три вещи: набор **CIDR-правил**, набор **доменных правил** и **действие по умолчанию**. ### CIDR-правила CIDR-правило — это `IpNetwork` (например `10.0.0.0/8`) плюс действие. CIDR-правила ключуются сетью, поэтому повторное добавление той же сети **перезаписывает** её действие. ### Доменные правила Доменное правило — это доменное имя плюс действие. Домены **не** сопоставляются с IP напрямую. Вместо этого `AuraDns` (`dns.rs`) резолвит домен через системный резолвер (hickory) и вставляет каждый получившийся адрес как **host-маршрут** — `/32` для IPv4, `/128` для IPv6, — так что они участвуют в обычном longest-prefix matching. Результаты резолва кэшируются. > Поскольку доменные правила становятся host-маршрутами в момент резолва, они действуют только > после того, как домен был разрешён (при старте или по требованию). Они отражают адреса, > увиденные в момент резолва, и не перерезолвятся непрерывно в v1. ### Действие по умолчанию Если ни одно CIDR-правило (включая host-маршруты от резолва доменов) не совпало с назначением, применяется **действие по умолчанию** таблицы. --- ## Приоритет longest-prefix `classify(dst_ip)` выполняет **longest-prefix match** (`routes.rs`): > Среди всех CIDR-правил, чьи сети содержат назначение, побеждает правило с **наибольшей длиной > префикса** (наиболее специфичное). Если ни одно правило не совпало, возвращается действие по > умолчанию. Так специфичный диапазон может перекрыть более широкий независимо от порядка вставки. IPv4-правила совпадают только с IPv4-назначениями, а IPv6 — только с IPv6. Пример (из поставляемой конфигурации): при `default = VPN`, `10.0.0.0/8 = Direct` и `10.7.0.0/24 = Vpn`: | Назначение | Сработавшее правило | Действие | |--------------|----------------------------------------------|----------| | `10.1.2.3` | `10.0.0.0/8` | Direct | | `10.7.0.9` | `10.7.0.0/24` (более специфичное, бьёт `/8`) | Vpn | | `192.168.1.1`| `192.168.0.0/16` | Direct | | `8.8.8.8` | (нет) → действие по умолчанию | Vpn | > Крайний случай: если два правила имеют **одинаковую** длину префикса, побеждает > **вставленное последним** (оно перезатирает предыдущее, потому что правила ключуются сетью). --- ## Статическая конфигурация: `[tunnel.split]` Split-tunnel настраивается в `client.toml` в секции `[tunnel.split]` (`crates/aura-cli/src/config.rs`). `build_route_table` превращает её в `RouteTable`: CIDR-правила применяются напрямую; доменные правила сохраняются и возвращаются, чтобы клиент мог разрезолвить их на старте. ### Схема | Ключ | Тип | По умолчанию | Смысл | |------------------------------|---------------------|--------------|------------------------------------------------| | `default` | строка | `"VPN"` | Действие, когда ни одно правило не совпало: `VPN` / `DIRECT` (регистронезависимо) | | `[[tunnel.split.direct]]` | массив правил | `[]` | Правила, отправляющие совпавшие назначения в **Direct** | | `[[tunnel.split.vpn]]` | массив правил | `[]` | Правила, отправляющие совпавшие назначения **через VPN** | Каждое правило в `direct` / `vpn` — это таблица с **ровно одним** из ключей: | Ключ | Тип | Пример | |----------|--------|--------------------------| | `cidr` | строка | `"192.168.0.0/16"` | | `domain` | строка | `"intranet.example.com"` | Правило, у которого указаны и `cidr`, и `domain` (или ни того ни другого), отвергается на этапе построения таблицы маршрутизации. ### Пример ```toml # Split-tunnel routing: the default action plus per-destination overrides. [tunnel.split] # Default for destinations matching no rule below: "VPN" or "DIRECT". default = "VPN" # Send these directly (bypass the tunnel): RFC1918 ranges stay on the LAN... [[tunnel.split.direct]] cidr = "192.168.0.0/16" [[tunnel.split.direct]] cidr = "10.0.0.0/8" # ...and a corporate domain egresses directly (resolved to host routes at startup). [[tunnel.split.direct]] domain = "intranet.example.com" # Force a more-specific range back through the VPN (longest-prefix wins over 10.0.0.0/8). [[tunnel.split.vpn]] cidr = "10.7.0.0/24" ``` Это и есть та конфигурация, что поставляется в `config/client.toml.example`. --- ## Управление на лету: `aura route` / `aura status` Работающий `aura client` (или `aura server`) поднимает **admin-сокет** — крошечный построчный JSON-протокол поверх **Unix domain socket** (`crates/aura-cli/src/admin.rs`). Подкоманды `aura route` и `aura status` подключаются к нему, чтобы инспектировать и менять живую таблицу маршрутизации, не перезапуская туннель. Путь сокета по умолчанию — `/tmp/aura-admin.sock` (перекрывается через `--admin-socket`). > Замечание про платформу: admin-сокет использует Unix domain sockets (Linux/macOS). На Windows > это `cfg`-заглушка, возвращающая поясняющую ошибку (named-pipe-транспорт — будущая работа), — > остальная часть CLI там по-прежнему собирается. ### Команды ``` aura route add (--cidr | --domain ) --action [--admin-socket ] aura route list [--admin-socket ] aura route remove --cidr [--admin-socket ] aura status [--admin-socket ] ``` `route add` принимает **ровно один** из ключей `--cidr` / `--domain` (они взаимоисключающие, и один из них обязателен), плюс `--action vpn` или `--action direct`. ```bash # Отправить CIDR напрямую, на лету. aura route add --cidr 8.8.8.0/24 --action direct # ok # Завернуть домен через VPN (разрезолвится в host-маршруты). aura route add --domain example.com --action vpn # ok # Посмотреть текущие правила и действие по умолчанию. aura route list # default: vpn # cidr 8.8.8.0/24 direct # domain example.com vpn # Удалить CIDR-правило. aura route remove --cidr 8.8.8.0/24 # ok (removed) # или: "ok (nothing to remove)", если его не было # Статус туннеля и счётчики. aura status # Aura tunnel status # peer: client-1 # default: vpn # rules: 1 # rx packets: 0 # tx packets: 0 ``` ### Особенности поведения - **`route remove` удаляет только CIDR-правила** — он принимает `--cidr` и не имеет доменной формы. У библиотечного `RouteTable` нет API для покнопочного удаления, поэтому удаление **перестраивает** таблицу из оставшихся правил (с сохранением действия по умолчанию). Доменные правила добавляются заново при перестройке, но их ранее разрезолвенные host-маршруты сбрасываются и перерезолвятся по требованию. - **`route list` перечисляет зеркало правил.** Истиной для классификации остаётся живой `RouteTable`, но он не отдаёт итерацию по правилам, поэтому admin-слой ведёт параллельное зеркало, синхронизированное с каждой мутацией; `list` отдаёт это зеркало, а `classify` по-прежнему ходит в реальную таблицу. - **`status`** сообщает проверенный peer id, действие по умолчанию, суммарное число правил (CIDR + домены), а также счётчики пакетов на вход/выход. ### Wire-протокол (для справки) По одному JSON-объекту в строке: сначала запрос, затем ответ (`crates/aura-cli/src/admin.rs`): ```text -> {"cmd":"route_add","cidr":"8.8.8.0/24","action":"direct"} <- {"ok":true} -> {"cmd":"route_list"} <- {"ok":true,"default":"vpn","cidrs":[{"cidr":"8.8.8.0/24","action":"direct"}],"domains":[]} -> {"cmd":"route_remove","cidr":"8.8.8.0/24"} <- {"ok":true,"removed":true} -> {"cmd":"status"} <- {"ok":true,"peer_id":"client-1","rx_packets":0,"tx_packets":0,"default":"vpn","rules":1} ``` При ошибке ответ имеет вид `{"ok":false,"error":"..."}`. --- ## Сводка ограничений v1 - **`Direct` — заглушка**: пакеты с действием `Direct` логируются и отбрасываются, а не реинъецируются в стек ОС. Путь через VPN полностью функционален. - **Доменные правила резолвятся один раз** (на старте или по требованию) в host-маршруты; непрерывного перерезолва нет. - **`route remove` работает только с CIDR** и перестраивает таблицу (доменные host-маршруты потом разрезолвятся по требованию). - **Admin-сокет только под Unix**; на Windows — `cfg`-заглушка. - Сервер в v1 — это **один общий TUN**, а поле `dns` в конфигурации туннеля носит информационный характер (используется системный резолвер).