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>
This commit is contained in:
+119
-115
@@ -1,121 +1,124 @@
|
||||
# Aura Split Tunnel
|
||||
# Split tunnel Aura
|
||||
|
||||
Split tunneling decides, per destination IP, whether a packet travels **through the encrypted
|
||||
VPN** or **egresses directly** (bypassing the tunnel). It lets you keep, say, RFC1918 LAN
|
||||
traffic local while sending the rest through Aura — or the reverse.
|
||||
Split-tunneling решает для каждого назначения IP, идёт ли пакет **через шифрованный VPN** или
|
||||
**уходит напрямую** (минуя туннель). Это позволяет, например, оставить трафик к RFC1918-сетям
|
||||
локальным, а остальное пустить через Aura — или наоборот.
|
||||
|
||||
It is implemented in the `aura-tunnel` crate (`routes.rs`, `router.rs`, `dns.rs`), configured
|
||||
statically via the `[tunnel.split]` section of `client.toml`
|
||||
(`crates/aura-cli/src/config.rs`), and managed live via the `aura route` / `aura status`
|
||||
admin commands (`crates/aura-cli/src/admin.rs`).
|
||||
Реализация лежит в крейте `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`).
|
||||
|
||||
---
|
||||
|
||||
## Concept: VPN vs DIRECT
|
||||
## Концепция: VPN или DIRECT
|
||||
|
||||
Every outbound IP packet read from the TUN device is classified into one of two actions
|
||||
Каждый исходящий IP-пакет, прочитанный с TUN-устройства, классифицируется в одно из двух действий
|
||||
(`RouteAction`):
|
||||
|
||||
- **`Vpn`** — encrypt and send the packet over the Aura connection to the server.
|
||||
- **`Direct`** — let the packet egress directly, bypassing the tunnel.
|
||||
- **`Vpn`** — зашифровать и отправить пакет по соединению Aura на сервер.
|
||||
- **`Direct`** — выпустить пакет напрямую, минуя туннель.
|
||||
|
||||
The router (`AuraRouter::run`, `router.rs`) parses each packet's destination IP, classifies
|
||||
it, and dispatches:
|
||||
Маршрутизатор (`AuraRouter::run`, `router.rs`) парсит у каждого пакета IP назначения,
|
||||
классифицирует его и диспетчеризует:
|
||||
|
||||
```
|
||||
TUN read --> parse dst IP --> RouteTable.classify(dst) --> Vpn? -> conn.send_packet()
|
||||
\ Direct? -> send_direct() (v1 stub)
|
||||
\ Direct? -> send_direct() (заглушка в v1)
|
||||
```
|
||||
|
||||
> **v1 limitation — `Direct` is a stub.** `send_direct` currently **logs and drops** the
|
||||
> packet; real raw-socket / OS-stack re-injection is out of scope for v1. The method is
|
||||
> already `async` and fallible so a real egress path can slot in without changing call sites.
|
||||
> The VPN path is fully functional end-to-end. Packets whose destination cannot be parsed
|
||||
> (not IPv4/IPv6, or too short) are dropped with a trace.
|
||||
> **Ограничение v1 — `Direct` это заглушка.** Текущая реализация `send_direct` **логирует и
|
||||
> отбрасывает** пакет; реальный raw-socket / реинъекция в стек ОС в объём v1 не входят. Метод уже
|
||||
> объявлен `async` и возвращает `Result`, чтобы реальный путь egress подключился без изменения
|
||||
> вызывающего кода. Путь через VPN полностью работоспособен сквозным образом. Пакеты, у которых
|
||||
> не получилось разобрать назначение (не IPv4/IPv6 или слишком короткие), отбрасываются с trace-
|
||||
> сообщением.
|
||||
|
||||
The inbound direction is straightforward: decrypted IP packets received from the peer are
|
||||
written back to the TUN device.
|
||||
Входящее направление прямолинейно: расшифрованные IP-пакеты, полученные от пира, пишутся обратно
|
||||
на TUN-устройство.
|
||||
|
||||
---
|
||||
|
||||
## Rules
|
||||
## Правила
|
||||
|
||||
The routing table (`RouteTable`, `routes.rs`) holds three things: a set of **CIDR rules**, a
|
||||
set of **domain rules**, and a **default action**.
|
||||
Таблица маршрутизации (`RouteTable`, `routes.rs`) хранит три вещи: набор **CIDR-правил**, набор
|
||||
**доменных правил** и **действие по умолчанию**.
|
||||
|
||||
### CIDR rules
|
||||
### CIDR-правила
|
||||
|
||||
A CIDR rule is an `IpNetwork` (e.g. `10.0.0.0/8`) plus an action. CIDR rules are keyed by
|
||||
network, so re-adding the same network **overwrites** its action.
|
||||
CIDR-правило — это `IpNetwork` (например `10.0.0.0/8`) плюс действие. CIDR-правила
|
||||
ключуются сетью, поэтому повторное добавление той же сети **перезаписывает** её действие.
|
||||
|
||||
### Domain rules
|
||||
### Доменные правила
|
||||
|
||||
A domain rule is a domain name plus an action. Domains do **not** match IPs directly. Instead
|
||||
`AuraDns` (`dns.rs`) resolves the domain via the system resolver (hickory) and inserts each
|
||||
resulting address as a **host route** — `/32` for IPv4, `/128` for IPv6 — so it participates
|
||||
in the normal longest-prefix match. Resolution results are cached.
|
||||
Доменное правило — это доменное имя плюс действие. Домены **не** сопоставляются с IP напрямую.
|
||||
Вместо этого `AuraDns` (`dns.rs`) резолвит домен через системный резолвер (hickory) и вставляет
|
||||
каждый получившийся адрес как **host-маршрут** — `/32` для IPv4, `/128` для IPv6, — так что они
|
||||
участвуют в обычном longest-prefix matching. Результаты резолва кэшируются.
|
||||
|
||||
> Because domain rules become host routes at resolution time, they only take effect once the
|
||||
> domain has been resolved (at startup, or on demand). They reflect the addresses seen at
|
||||
> resolution time and are not continuously re-resolved in v1.
|
||||
> Поскольку доменные правила становятся host-маршрутами в момент резолва, они действуют только
|
||||
> после того, как домен был разрешён (при старте или по требованию). Они отражают адреса,
|
||||
> увиденные в момент резолва, и не перерезолвятся непрерывно в v1.
|
||||
|
||||
### Default action
|
||||
### Действие по умолчанию
|
||||
|
||||
If no CIDR rule (including resolved domain host routes) matches a destination, the table's
|
||||
**default action** applies.
|
||||
Если ни одно CIDR-правило (включая host-маршруты от резолва доменов) не совпало с назначением,
|
||||
применяется **действие по умолчанию** таблицы.
|
||||
|
||||
---
|
||||
|
||||
## Longest-prefix precedence
|
||||
## Приоритет longest-prefix
|
||||
|
||||
`classify(dst_ip)` performs a **longest-prefix match** (`routes.rs`):
|
||||
`classify(dst_ip)` выполняет **longest-prefix match** (`routes.rs`):
|
||||
|
||||
> Among all CIDR rules whose network contains the destination, the rule with the **largest
|
||||
> prefix length** (most specific) wins. If no rule matches, the default action is returned.
|
||||
> Среди всех CIDR-правил, чьи сети содержат назначение, побеждает правило с **наибольшей длиной
|
||||
> префикса** (наиболее специфичное). Если ни одно правило не совпало, возвращается действие по
|
||||
> умолчанию.
|
||||
|
||||
This lets a specific range override a broader one regardless of insertion order. IPv4 rules
|
||||
only match IPv4 destinations and IPv6 rules only match IPv6 destinations.
|
||||
Так специфичный диапазон может перекрыть более широкий независимо от порядка вставки. IPv4-правила
|
||||
совпадают только с IPv4-назначениями, а IPv6 — только с IPv6.
|
||||
|
||||
Example (from the shipped config): with `default = VPN`, `10.0.0.0/8 = Direct`, and
|
||||
Пример (из поставляемой конфигурации): при `default = VPN`, `10.0.0.0/8 = Direct` и
|
||||
`10.7.0.0/24 = Vpn`:
|
||||
|
||||
| Destination | Matched rule | Action |
|
||||
|--------------|----------------------|--------|
|
||||
| `10.1.2.3` | `10.0.0.0/8` | Direct |
|
||||
| `10.7.0.9` | `10.7.0.0/24` (more specific, wins over `/8`) | Vpn |
|
||||
| `192.168.1.1`| `192.168.0.0/16` | Direct |
|
||||
| `8.8.8.8` | (none) → default | 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 |
|
||||
|
||||
> Edge case: if two rules share the **same** prefix length, the **last-inserted** one wins
|
||||
> (it overwrites the earlier entry, since rules are keyed by network).
|
||||
> Крайний случай: если два правила имеют **одинаковую** длину префикса, побеждает
|
||||
> **вставленное последним** (оно перезатирает предыдущее, потому что правила ключуются сетью).
|
||||
|
||||
---
|
||||
|
||||
## Static config: `[tunnel.split]`
|
||||
## Статическая конфигурация: `[tunnel.split]`
|
||||
|
||||
The split tunnel is configured in `client.toml` under `[tunnel.split]`
|
||||
(`crates/aura-cli/src/config.rs`). `build_route_table` turns it into a `RouteTable`: CIDR
|
||||
rules are applied directly; domain rules are recorded and returned for the client to resolve
|
||||
at startup.
|
||||
Split-tunnel настраивается в `client.toml` в секции `[tunnel.split]`
|
||||
(`crates/aura-cli/src/config.rs`). `build_route_table` превращает её в `RouteTable`: CIDR-правила
|
||||
применяются напрямую; доменные правила сохраняются и возвращаются, чтобы клиент мог разрезолвить
|
||||
их на старте.
|
||||
|
||||
### Schema
|
||||
### Схема
|
||||
|
||||
| Key | Type | Default | Meaning |
|
||||
|------------------------------|-----------------|---------|----------------------------------------------------|
|
||||
| `default` | string | `"VPN"` | Action when no rule matches: `VPN` / `DIRECT` (case-insensitive) |
|
||||
| `[[tunnel.split.direct]]` | array of rules | `[]` | Rules forcing matching destinations to **Direct** |
|
||||
| `[[tunnel.split.vpn]]` | array of rules | `[]` | Rules forcing matching destinations through the **VPN** |
|
||||
| Ключ | Тип | По умолчанию | Смысл |
|
||||
|------------------------------|---------------------|--------------|------------------------------------------------|
|
||||
| `default` | строка | `"VPN"` | Действие, когда ни одно правило не совпало: `VPN` / `DIRECT` (регистронезависимо) |
|
||||
| `[[tunnel.split.direct]]` | массив правил | `[]` | Правила, отправляющие совпавшие назначения в **Direct** |
|
||||
| `[[tunnel.split.vpn]]` | массив правил | `[]` | Правила, отправляющие совпавшие назначения **через VPN** |
|
||||
|
||||
Each rule in `direct` / `vpn` is a table with **exactly one** of:
|
||||
Каждое правило в `direct` / `vpn` — это таблица с **ровно одним** из ключей:
|
||||
|
||||
| Key | Type | Example |
|
||||
|----------|--------|---------------------|
|
||||
| `cidr` | string | `"192.168.0.0/16"` |
|
||||
| `domain` | string | `"intranet.example.com"` |
|
||||
| Ключ | Тип | Пример |
|
||||
|----------|--------|--------------------------|
|
||||
| `cidr` | строка | `"192.168.0.0/16"` |
|
||||
| `domain` | строка | `"intranet.example.com"` |
|
||||
|
||||
A rule with both `cidr` and `domain`, or neither, is rejected when the route table is built.
|
||||
Правило, у которого указаны и `cidr`, и `domain` (или ни того ни другого), отвергается на этапе
|
||||
построения таблицы маршрутизации.
|
||||
|
||||
### Example
|
||||
### Пример
|
||||
|
||||
```toml
|
||||
# Split-tunnel routing: the default action plus per-destination overrides.
|
||||
@@ -139,23 +142,23 @@ domain = "intranet.example.com"
|
||||
cidr = "10.7.0.0/24"
|
||||
```
|
||||
|
||||
This is the configuration shipped in `config/client.toml.example`.
|
||||
Это и есть та конфигурация, что поставляется в `config/client.toml.example`.
|
||||
|
||||
---
|
||||
|
||||
## Live management: `aura route` / `aura status`
|
||||
## Управление на лету: `aura route` / `aura status`
|
||||
|
||||
A running `aura client` (or `aura server`) hosts an **admin socket** — a tiny JSON
|
||||
line-protocol over a **Unix domain socket** (`crates/aura-cli/src/admin.rs`). The `aura
|
||||
route` and `aura status` subcommands connect to it to inspect and mutate the live routing
|
||||
table without restarting the tunnel. The default socket path is `/tmp/aura-admin.sock`
|
||||
(override with `--admin-socket`).
|
||||
Работающий `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`).
|
||||
|
||||
> Platform note: the admin socket uses Unix domain sockets (Linux/macOS). On Windows it is a
|
||||
> `cfg`-gated stub that returns an explanatory error (a named-pipe transport is future work),
|
||||
> so the rest of the CLI still compiles there.
|
||||
> Замечание про платформу: admin-сокет использует Unix domain sockets (Linux/macOS). На Windows
|
||||
> это `cfg`-заглушка, возвращающая поясняющую ошибку (named-pipe-транспорт — будущая работа), —
|
||||
> остальная часть CLI там по-прежнему собирается.
|
||||
|
||||
### Commands
|
||||
### Команды
|
||||
|
||||
```
|
||||
aura route add (--cidr <CIDR> | --domain <DOMAIN>) --action <vpn|direct> [--admin-socket <PATH>]
|
||||
@@ -164,29 +167,29 @@ aura route remove --cidr <CIDR> [--a
|
||||
aura status [--admin-socket <PATH>]
|
||||
```
|
||||
|
||||
`route add` takes **exactly one** of `--cidr` / `--domain` (they are mutually exclusive, and
|
||||
one is required), plus `--action vpn` or `--action direct`.
|
||||
`route add` принимает **ровно один** из ключей `--cidr` / `--domain` (они взаимоисключающие, и
|
||||
один из них обязателен), плюс `--action vpn` или `--action direct`.
|
||||
|
||||
```bash
|
||||
# Send a CIDR directly, live.
|
||||
# Отправить CIDR напрямую, на лету.
|
||||
aura route add --cidr 8.8.8.0/24 --action direct
|
||||
# ok
|
||||
|
||||
# Route a domain through the VPN (resolved into host routes).
|
||||
# Завернуть домен через VPN (разрезолвится в host-маршруты).
|
||||
aura route add --domain example.com --action vpn
|
||||
# ok
|
||||
|
||||
# Inspect the current rules and default.
|
||||
# Посмотреть текущие правила и действие по умолчанию.
|
||||
aura route list
|
||||
# default: vpn
|
||||
# cidr 8.8.8.0/24 direct
|
||||
# domain example.com vpn
|
||||
|
||||
# Remove a CIDR rule.
|
||||
# Удалить CIDR-правило.
|
||||
aura route remove --cidr 8.8.8.0/24
|
||||
# ok (removed) # or: "ok (nothing to remove)" if it wasn't present
|
||||
# ok (removed) # или: "ok (nothing to remove)", если его не было
|
||||
|
||||
# Tunnel status / counters.
|
||||
# Статус туннеля и счётчики.
|
||||
aura status
|
||||
# Aura tunnel status
|
||||
# peer: client-1
|
||||
@@ -196,22 +199,23 @@ aura status
|
||||
# tx packets: 0
|
||||
```
|
||||
|
||||
### Behavior notes
|
||||
### Особенности поведения
|
||||
|
||||
- **`route remove` only removes CIDR rules** — it takes `--cidr` and has no domain form. The
|
||||
library `RouteTable` has no per-rule remove API, so a removal **rebuilds** the table from
|
||||
the surviving rules (preserving the default). Domain rules are re-added on rebuild, but
|
||||
their previously resolved host routes are dropped and re-resolved on demand.
|
||||
- **`route list` enumerates a rule mirror.** The live `RouteTable` is the source of truth for
|
||||
classification but does not expose iteration, so the admin layer keeps a parallel mirror in
|
||||
lockstep with every mutation; `list` echoes that mirror while `classify` still uses the real
|
||||
table.
|
||||
- **`status`** reports the verified peer id, the default action, the total rule count
|
||||
(CIDR + domain), and inbound/outbound packet counters.
|
||||
- **`route remove` удаляет только CIDR-правила** — он принимает `--cidr` и не имеет доменной
|
||||
формы. У библиотечного `RouteTable` нет API для покнопочного удаления, поэтому удаление
|
||||
**перестраивает** таблицу из оставшихся правил (с сохранением действия по умолчанию). Доменные
|
||||
правила добавляются заново при перестройке, но их ранее разрезолвенные host-маршруты
|
||||
сбрасываются и перерезолвятся по требованию.
|
||||
- **`route list` перечисляет зеркало правил.** Истиной для классификации остаётся живой
|
||||
`RouteTable`, но он не отдаёт итерацию по правилам, поэтому admin-слой ведёт параллельное
|
||||
зеркало, синхронизированное с каждой мутацией; `list` отдаёт это зеркало, а `classify`
|
||||
по-прежнему ходит в реальную таблицу.
|
||||
- **`status`** сообщает проверенный peer id, действие по умолчанию, суммарное число правил
|
||||
(CIDR + домены), а также счётчики пакетов на вход/выход.
|
||||
|
||||
### Wire protocol (for reference)
|
||||
### Wire-протокол (для справки)
|
||||
|
||||
One JSON object per line, request then response (`crates/aura-cli/src/admin.rs`):
|
||||
По одному JSON-объекту в строке: сначала запрос, затем ответ (`crates/aura-cli/src/admin.rs`):
|
||||
|
||||
```text
|
||||
-> {"cmd":"route_add","cidr":"8.8.8.0/24","action":"direct"}
|
||||
@@ -224,18 +228,18 @@ One JSON object per line, request then response (`crates/aura-cli/src/admin.rs`)
|
||||
<- {"ok":true,"peer_id":"client-1","rx_packets":0,"tx_packets":0,"default":"vpn","rules":1}
|
||||
```
|
||||
|
||||
On error the response is `{"ok":false,"error":"..."}`.
|
||||
При ошибке ответ имеет вид `{"ok":false,"error":"..."}`.
|
||||
|
||||
---
|
||||
|
||||
## v1 limitations summary
|
||||
## Сводка ограничений v1
|
||||
|
||||
- **`Direct` egress is a stub** — `Direct` packets are logged and dropped, not re-injected to
|
||||
the OS stack. The VPN path is fully functional.
|
||||
- **Domain rules are resolved once** (at startup / on demand) into host routes; no continuous
|
||||
re-resolution.
|
||||
- **`route remove` is CIDR-only** and rebuilds the table (domain host routes are re-resolved
|
||||
on demand afterward).
|
||||
- **Admin socket is Unix-only**; Windows is a `cfg`-gated stub.
|
||||
- The server is a **single shared TUN** in v1, and the tunnel resolver `dns` config field is
|
||||
informational (the system resolver is used).
|
||||
- **`Direct` — заглушка**: пакеты с действием `Direct` логируются и отбрасываются, а не
|
||||
реинъецируются в стек ОС. Путь через VPN полностью функционален.
|
||||
- **Доменные правила резолвятся один раз** (на старте или по требованию) в host-маршруты;
|
||||
непрерывного перерезолва нет.
|
||||
- **`route remove` работает только с CIDR** и перестраивает таблицу (доменные host-маршруты потом
|
||||
разрезолвятся по требованию).
|
||||
- **Admin-сокет только под Unix**; на Windows — `cfg`-заглушка.
|
||||
- Сервер в v1 — это **один общий TUN**, а поле `dns` в конфигурации туннеля носит информационный
|
||||
характер (используется системный резолвер).
|
||||
|
||||
Reference in New Issue
Block a user