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

14 KiB
Raw Blame History

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 (или ни того ни другого), отвергается на этапе построения таблицы маршрутизации.

Пример

# 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.

# Отправить 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):

-> {"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 в конфигурации туннеля носит информационный характер (используется системный резолвер).