Files
AuraVPN/docs/split-tunnel.md
T
xah30 083c441e4c docs: rewrite all documentation in Russian + add deployment guide
- 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>
2026-05-26 10:42:08 +03:00

246 lines
14 KiB
Markdown
Raw 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.
# 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` в конфигурации туннеля носит информационный
характер (используется системный резолвер).