083c441e4c
- docs/protocol.md, pki.md, split-tunnel.md, sing-box.md переведены на русский и сверены с текущим кодом (транспорт v2: свой UDP + TCP/443 + QUIC fallback, handover; PKI; split-tunnel; sing-box-план). - docs/deployment.md (новый, 369 строк): пошаговое руководство для удалённого сервера — сборка, PKI init/issue-server/issue-client (проверено бинарём), server.toml/client.toml на основе фактических config/*.example, firewall + NAT/IP-форвардинг, sudo-запуск, бандл клиента (ca.crt + client.crt + client.key + server addr/sni), на каком транспорте идёт трафик, ограничения v1. - README.md (новый, корень): краткий обзор + таблица крейтов + быстрый старт. Всё на русском (проза); команды/идентификаторы/конфиги — как есть. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
246 lines
14 KiB
Markdown
246 lines
14 KiB
Markdown
# 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 <CIDR> | --domain <DOMAIN>) --action <vpn|direct> [--admin-socket <PATH>]
|
||
aura route list [--admin-socket <PATH>]
|
||
aura route remove --cidr <CIDR> [--admin-socket <PATH>]
|
||
aura status [--admin-socket <PATH>]
|
||
```
|
||
|
||
`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` в конфигурации туннеля носит информационный
|
||
характер (используется системный резолвер).
|