Files
xah30 15c7da12fe fix(server): v3.6 — implicit auto-NAT on Linux (root cause of full-VPN dying)
Symptoms: in default = "VPN" full-VPN mode external internet was dead even
though tunnel-internal ping (10.7.0.1) worked perfectly. The tunnel itself
was assembled and AEAD-encrypted (see TEST_CASES.md), but packets sent
through it died on the server side.

Root cause: server's `[server.nat]` was opt-in. On the production server
(187.77.67.17) deployed before v2, the section is absent in
/etc/aura/server.toml, so `aura server` never ran the iptables MASQUERADE
plan. Packets egressed to the upstream router with src = 10.7.0.10 (RFC1918),
which the provider's reverse-path filter dropped — full-VPN clients saw
"internet is dead". Tunnel-internal pool addresses worked because they
don't need NAT.

Fix:
* `server.rs`: when `[server.nat]` is absent in server.toml AND we are on
  Linux, attempt auto-NAT with an auto-detected egress_iface. If detection
  or the iptables call fails we DON'T bail — we log a loud error and let
  the server come up so safe-mode clients keep working.
* `config.rs`: `ServerNatSection::default()` now defaults `auto = true`.
  A bare `[server.nat]` header (no `auto =`) now means "yes, enable it"
  instead of the silent-noop it used to be.
* New tests for both bare-header and explicit `auto = false` opt-out paths.
* `docs/server_nat_fix.md`: step-by-step instructions for fixing the
  existing 187.77.67.17 server (binary upgrade vs. manual server.toml
  patch vs. fully-manual sysctl + iptables).
* `docs/deployment.md`: replaces "manual mandatory step" wording with
  the new auto-NAT story.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 14:11:24 +03:00

901 lines
52 KiB
Markdown
Raw Permalink 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.
# Развёртывание Aura VPN
Этот документ — пошаговое руководство, по которому вы поднимаете сервер Aura на удалённой машине,
провижините на нём сертификат для клиента и подключаете клиент (десктоп) к этому серверу. Все
команды и поля конфигов взяты из фактического кода и поставляемых примеров в `config/`.
> Полезные сопутствующие документы: [`protocol.md`](protocol.md) (wire-протокол),
> [`pki.md`](pki.md) (CA и сертификаты), [`split-tunnel.md`](split-tunnel.md) (правила
> маршрутизации), [`sing-box.md`](sing-box.md) (интеграция с sing-box, план).
---
## 1. Обзор схемы
Сервер Aura на удалённой машине провижинит сертификат для клиента (десктопа или, в будущем,
телефона через sing-box), отдаёт клиенту бандл сертификатов и трастового якоря, и клиент
подключается к серверу по протоколу AuraVPN.
На проводе по умолчанию используется **собственный UDP-транспорт Aura с пост-квантовой
криптографией** (без QUIC и без внешнего TLS на основном пути); fallback'и — это TCP/443 и QUIC.
Всё рукопожатие пост-квантовое: **гибридное X25519 + ML-KEM-768** с взаимной X.509-аутентификацией.
Для данных используется AEAD **ChaCha20-Poly1305** с explicit-nonce. Обфускация — это паддинг
датаграмм до «корзин» размера, характерных для HTTPS.
```
[клиент-десктоп] [удалённый сервер aura]
client.toml + PEM-бандл server.toml + PKI (CA + server leaf)
| |
| UDP (основной) / TCP/443 / QUIC |
| гибридное PQ-рукопожатие |
| ChaCha20-Poly1305 |
+--------------------------------------->
AuraVPN
```
---
## 2. Сервер (удалённый хост)
### 2.1. Установка бинаря
В корне репозитория:
```bash
cargo build --release
# -> target/release/aura
```
Скопируйте получившийся бинарь `target/release/aura` на сервер (например в `/usr/local/bin/aura`)
либо соберите его прямо на сервере (требуется Rust toolchain).
### 2.2. Поднять PKI
Эти три команды создают CA и выпускают листовые сертификаты для сервера и клиента. Все они
проверены против реализации в `crates/aura-pki/src/{ca,cert,store}.rs` и
`crates/aura-cli/src/pki.rs`.
```bash
# 1) Создать CA Aura.
aura pki init --ca-name "Aura Root CA" --out /etc/aura/pki
# -> /etc/aura/pki/ca.crt
# -> /etc/aura/pki/ca.key # секрет, защищайте правами файловой системы
# 2) Выпустить сертификат сервера. --domain должен совпадать с тем именем,
# которое клиент будет ожидать в [client] sni (это же имя проверяется по SAN).
aura pki issue-server \
--domain vpn.example.com \
--out /etc/aura/pki/server \
--ca /etc/aura/pki
# -> /etc/aura/pki/server/server.crt
# -> /etc/aura/pki/server/server.key # секрет
# 3) Выпустить сертификат клиента (по одному на устройство).
# --id становится Common Name'ом и проверенным peer_id, который видит сервер.
aura pki issue-client \
--id phone-1 \
--out /etc/aura/clients/phone-1 \
--ca /etc/aura/pki
# -> /etc/aura/clients/phone-1/client.crt
# -> /etc/aura/clients/phone-1/client.key # секрет
```
Подробности (включая `aura pki revoke` / `list`) — см. [`pki.md`](pki.md).
### 2.3. `server.toml`
Раскладка ниже взята из `config/server.toml.example` и поставляемых serde-структур
(`crates/aura-cli/src/config.rs`). Скопируйте пример и поправьте под себя.
```toml
[server]
# Человекочитаемое имя (также внутренняя identity сервера в рукопожатии).
name = "aura-edge-1"
# UDP/TCP listen-сокет. ":443" мимикрирует под HTTPS; для его биндинга нужны привилегии.
# IP отсюда переиспользуется как listen-IP для каждого включённого транспорта.
listen = "0.0.0.0:443"
# Число accept-воркеров (в v1 носит совещательный характер).
workers = 4
[pki]
# Trust anchor (Aura CA) и листовая пара сервера, все PEM.
ca_cert = "/etc/aura/pki/ca.crt"
cert = "/etc/aura/pki/server/server.crt"
key = "/etc/aura/pki/server/server.key"
[tunnel]
# Адресный пул для клиентов; в v1 на сервере один общий TUN в этой сети.
pool_cidr = "10.7.0.0/24"
# MTU TUN-устройства (запас под QUIC + framing Aura).
mtu = 1420
# DNS, анонсируемый клиентам (в v1 информационно).
dns = "10.7.0.1"
[mimicry]
# Hostname, под который мимикрирует внешний TLS-слой (для QUIC).
sni = "cdn.example.com"
# Паддинг для размытия размеров пакетов под «корзины» HTTPS.
padding = true
[transport]
# Набор и порядок транспортов, биндящихся одновременно. UDP — основной; TCP/443 и
# QUIC (мимикрия H3) — fallback'и. При отсутствии всей секции включаются udp/tcp/quic
# на 443/443/444.
order = ["udp", "tcp", "quic"]
# UDP-транспорт и QUIC оба используют UDP, поэтому udp_port и quic_port ДОЛЖНЫ
# различаться. TCP может занимать тот же номер порта (другой протокол).
udp_port = 443
tcp_port = 443
quic_port = 444
# UDP: дополнять датаграммы до «корзин» размера HTTPS, чтобы размыть распределение размеров.
obfuscate = true
# TCP: добавлять минимальный HTTP/1.1-преамбулу (Host = [mimicry] sni), чтобы открытие
# выглядело как обычный HTTP.
masquerade = true
```
Пути могут начинаться с `~` (раскрывается в домашнюю директорию).
### 2.4. Сеть на сервере
#### Файрвол
Откройте те порты, которые перечислены у вас в `[transport]`. С приведённой выше конфигурацией:
- UDP **443** — основной транспорт Aura.
- TCP **443** — fallback Aura поверх TCP.
- UDP **444** — fallback Aura поверх QUIC.
Важно: UDP-транспорт и QUIC — это **оба UDP**, поэтому их порты обязательно должны различаться
(в примере: udp_port=443, quic_port=444). Конфиг-валидатор `transport.modes()` отвергает совпадение.
#### IP-форвардинг и NAT (для выхода клиентов в интернет)
**v3.6 и новее:** настройка делается **автоматически** при старте `aura server`.
Если в `server.toml` есть секция `[server.nat]` с `auto = true` (так пишет
`aura server-init`) — сервер сам сделает `sysctl net.ipv4.ip_forward=1` и
поставит правило MASQUERADE на нужный интерфейс, а при остановке откатит обе
операции. Если секции вообще нет (legacy-конфиг до v2), сервер всё равно
попытается включить NAT с автодетектом egress-интерфейса (**implicit auto-NAT**)
и громко скажет это в логе.
Опт-аут — если оператор уже сам управляет фаерволом:
```toml
[server.nat]
auto = false
```
**Legacy / ручной путь** (v1 или сценарий с отключённым auto-NAT):
```bash
# 1) Включить IP-форвардинг.
sudo sysctl -w net.ipv4.ip_forward=1
# (для постоянства добавьте в /etc/sysctl.conf или /etc/sysctl.d/*)
# 2) MASQUERADE для исходящего трафика клиентов на интернет-интерфейсе (например eth0).
sudo iptables -t nat -A POSTROUTING \
-s 10.7.0.0/24 \
-o eth0 \
-j MASQUERADE
```
Подставьте свой `pool_cidr` и имя интернет-интерфейса.
Подробный сценарий «существующий сервер до v3.6, full-VPN не работает» разобран
в [`docs/server_nat_fix.md`](server_nat_fix.md).
### 2.5. Запуск сервера
```bash
sudo aura server --config /etc/aura/server.toml
```
`sudo` нужен для создания TUN-устройства и для биндинга привилегированных портов (`:443`).
Можно опционально указать путь admin-сокета:
```bash
sudo aura server \
--config /etc/aura/server.toml \
--admin-socket /var/run/aura-admin.sock
```
По умолчанию admin-сокет — `/tmp/aura-admin.sock`.
---
## 3. Что вы получаете для клиента (бандл)
Отдайте клиенту **три PEM-файла**:
- `ca.crt` (из `/etc/aura/pki/ca.crt`) — trust anchor;
- `client.crt` (из `/etc/aura/clients/<id>/client.crt`) — листовой сертификат клиента;
- `client.key` (из `/etc/aura/clients/<id>/client.key`) — **секрет**, приватный ключ клиента.
И сообщите ему два параметра:
- **Адрес сервера** (например `203.0.113.10`).
- **`sni`** — то DNS-имя, которое вы указали в `aura pki issue-server --domain`. Оно же
ожидается в SAN серверного сертификата и проверяется в `verify_server_cert`.
Эти три файла плюс два параметра — это всё, что нужно клиенту для подключения.
---
## 4. Клиент (десктоп)
Путь для телефона — через sing-box; пока нативного клиента нет, см. раздел 6 ниже.
### 4.1. `client.toml`
Раскладка взята из `config/client.toml.example` и `crates/aura-cli/src/config.rs`.
```toml
[client]
# Человекочитаемое имя/id клиента.
name = "laptop"
# UDP-сокет сервера. IP отсюда переиспользуется как server-IP для каждого транспорта.
server_addr = "203.0.113.10:443"
# Внешний TLS-SNI (hostname-камуфляж), предъявляемый серверу. Он же проверяется
# внутри рукопожатия Aura против SAN серверного сертификата.
sni = "cdn.example.com"
[pki]
# Trust anchor (Aura CA) и листовая пара клиента, все PEM.
ca_cert = "~/.aura/ca.crt"
cert = "~/.aura/client.crt"
key = "~/.aura/client.key"
[tunnel]
# Запрошенное имя TUN-интерфейса (на macOS совещательно — ядро назначает utunN).
tun_name = "aura0"
# Локальный адрес для TUN и длина префикса.
local_ip = "10.7.0.2"
prefix = 24
# MTU TUN.
mtu = 1420
# DNS, используемый туннельным резолвером (в v1 информационно; реально используется
# системный резолвер).
dns = "10.7.0.1"
# Split-tunnel: действие по умолчанию плюс точечные правила.
[tunnel.split]
default = "VPN"
[[tunnel.split.direct]]
cidr = "192.168.0.0/16"
[[tunnel.split.direct]]
cidr = "10.0.0.0/8"
[[tunnel.split.direct]]
domain = "intranet.example.com"
# Более узкий префикс возвращает поддиапазон обратно в VPN (longest-prefix бьёт /8).
[[tunnel.split.vpn]]
cidr = "10.7.0.0/24"
[mimicry]
padding = false
[transport]
# Порядок fallback'а (handover), пробуется слева направо: первый удавшийся побеждает.
# При отсутствии всей секции — ["udp","tcp","quic"] на 443/443/444.
order = ["udp", "tcp", "quic"]
udp_port = 443
tcp_port = 443
quic_port = 444
obfuscate = true
masquerade = true
```
Подробности про `[tunnel.split]` — в [`split-tunnel.md`](split-tunnel.md).
### 4.2. Запуск клиента
```bash
sudo aura client --config client.toml
```
`sudo` нужен для поднятия TUN-устройства. Клиент:
1. Загружает PEM-файлы из `[pki]` и строит `aura_proto::ClientConfig`.
2. Строит таблицу маршрутизации из `[tunnel.split]`.
3. Дозванивается до сервера, перебирая транспорты в `[transport] order`
(handover UDP → TCP → QUIC); первый, который удался, побеждает.
4. Разрезолвит доменные правила split-tunnel'а в host-маршруты (best-effort).
5. Создаёт TUN, передаёт его маршрутизатору и начинает гонять трафик.
В логе при успехе вы увидите строку с выбранным транспортом:
```
INFO connected and authenticated to server peer=Some("cdn.example.com") mode=udp
```
`mode` принимает значения `udp`, `tcp` или `quic`.
### 4.3. Управление на лету
После запуска клиента (или сервера) admin-сокет позволяет менять правила и смотреть статус без
перезапуска:
```bash
# Добавить CIDR на лету.
aura route add --cidr 8.8.8.0/24 --action direct
# Завернуть домен через VPN.
aura route add --domain example.com --action vpn
# Перечислить правила.
aura route list
# Удалить CIDR-правило.
aura route remove --cidr 8.8.8.0/24
# Статус и счётчики.
aura status
# Aura tunnel status
# peer: cdn.example.com
# default: vpn
# rules: 2
# rx packets: 0
# tx packets: 0
```
Если сокет лежит не там, добавьте `--admin-socket <PATH>` к каждой команде. Полная спецификация
команд и wire-протокола admin'а — в [`split-tunnel.md`](split-tunnel.md).
---
## 5. Что идёт по проводу (резюме)
- **Основной**: собственный UDP-транспорт Aura (в примере — `443/udp`). Один UDP-сокет несёт
обе фазы, различимые по первому байту:
- `0x01` HS — рукопожатие с надёжным DTLS-подобным слоем поверх (повторы, ack, упорядочивание);
- `0x02` DATA — датаграммы данных с explicit-nonce AEAD; обфускация = паддинг до «корзин»
HTTPS (`[64, 128, 256, 512, 1024, 1280, 1460]`).
- **Fallback TCP/443**: настоящий **outer TLS-443** (rustls) поверх TCP — на проводе неотличимо
от валидного HTTPS, ALPN `[h2, http/1.1]`. Внутри TLS — тот же Aura-handshake. Клиент
использует `AcceptAnyServerCert` (security гарантирует только внутренний Aura-handshake).
- **Fallback QUIC**: внешний TLS-камуфляж под HTTP/3 + внутреннее Aura-рукопожатие.
- Клиент пробует транспорты по `order`, переключается при отказе или таймауте подключения
(по умолчанию 8 с). Сервер слушает все включённые транспорты одновременно (`MultiServer`).
Подробный wire-протокол — в [`protocol.md`](protocol.md).
---
## 6. v2 — что устранено и что остаётся
### Устранено в v2 (по сравнению с v1)
-**Мульти-клиент UDP-сервер.** `UdpServer` теперь демультиплексирует датаграммы по адресу
пира: один сокет обслуживает много клиентов параллельно, каждый со своим `ReliableHsAdapter` и
своим `UdpConnection`. `MultiServer::accept` выдаёт по соединению на каждого нового клиента
(в любом из включённых транспортов).
-**IP-пул + per-client маршрутизация на сервере.** Новая секция `[server.pool]`
(`cidr`, `strategy`, `static`) выдаёт каждому клиенту IP при accept; `ServerRouter` держит
карту `client_ip → Arc<dyn PacketConnection>`, читает с TUN, диспатчит пакеты по `dst_ip` в
нужное подключение, а параллельные per-conn-задачи пишут входящий трафик обратно в TUN.
-**`send_direct` устранён.** Новое `[tunnel.os_routes]` (по умолчанию `enabled = true`)
программирует **системную таблицу маршрутов**: DIRECT-цели идут мимо TUN через изначальный
default-gateway, через TUN попадает только VPN-трафик. RAII-guard откатывает все добавленные
маршруты при остановке клиента.
-**Настоящий TLS-443 в TCP-транспорте.** Вместо лёгкой HTTP/1.1-преамбулы — полный outer
rustls handshake (ALPN `[h2, http/1.1]`); клиент с `AcceptAnyServerCert`, поэтому SNI на
внешнем слое не верифицируется (это камуфляж), а вся реальная аутентификация — внутри
Aura-рукопожатия. На проводе неотличимо от валидного HTTPS.
-**Авто-NAT на сервере.** `[server.nat] {auto, egress_iface, dry_run}` поднимает
IP-форвардинг + MASQUERADE на старте (Linux: `sysctl ip_forward` + `iptables -t nat
MASQUERADE`; macOS: `sysctl ip.forwarding` + `pfctl`). RAII-guard откатывает изменения при
остановке. Опциональный `dry_run = true` логирует команды без выполнения — для отладки.
-**Privilege drop.** `[server]/[client] run_as = "nobody"` — после поднятия TUN и
привилегированных портов euid/gid сбрасываются в нерутового пользователя (Linux:
`setresuid/setresgid`; macOS: `setgid` + `setuid` permanent drop). Минимизирует время
процесса под root.
-**Admin-сокет на Windows.** Cfg-gated транспорт: на Unix — Unix socket
(`/tmp/aura-admin.sock`), на Windows — Tokio named pipe (`\\.\pipe\aura-admin`).
JSON-протокол и команды (`route add/list/remove`, `status`) идентичны.
-**In-band CRL.** Сервер сразу после handshake пушит подписанный CA CRL клиенту
через мультиплексированный control-envelope с magic-префиксом
`[0xAA,0xAA,0xC0,0x01]` (не конфликтует с IPv4/v6, которые начинаются с
`0x4X`/`0x6X`). Клиент проверяет подпись ECDSA-P256 против CA, применяет к
`AuraCertVerifier`, кэширует на диск. Конфиг `[pki] crl_push = true` /
`accept_pushed_crl = true` (по умолчанию).
-**Анти-надзор v2 (NEW в ответ на дрегнет операторов):**
- **Port-knocking** на UDP-транспорте: сервер молчит на скан-зондах, отвечает
только на валидный 16-байтный HMAC-SHA256 «стук» с ±1-минутным окном (ключ
из CA fingerprint). Сканер видит закрытый порт. `[transport.knock]
enabled = true`.
- **Cover traffic / chaff**: при простое UDP-соединение шлёт `Ping` каждые
~500мс±50% (jitter). Defeats volume/timing-fingerprinting. Под нагрузкой
подавляется автоматически. `[transport.cover] enabled = true`.
-**Автоматизация развёртывания:**
- `aura server-init --domain ... --pki-dir ...` — одна команда: CA +
серверный cert + готовый `server.toml` (с авто-определением egress-iface).
- `aura provision-client --id <UUID> --out <dir>` — выпускает client cert и
собирает готовый бандл (`ca.crt` + `client.crt` + `client.key` +
`client.toml`). `--id` опционален — дефолт UUID v4 (имя пользователя не
привязано к сертификату).
-**Минимизация идентификаторов:**
- `[server] no_logs = true` / `[client] no_logs = true` — поля `peer_id`,
`client_ip`, `source_addr`, `client_id`, `local_ip`, `user`, `id`,
`assigned_ip` редактируются из tracing-вывода через field-level фильтр
(события фиксируются, идентификаторы вычищаются).
- `[client] bridges = [...]` — список запасных серверов, клиент пробует
случайный порядок. Блокировка одного IP не убивает доступ.
-**НОВОЕ: ежедневная ротация масок в 05:00 МСК.** Внешний фингерпринт (SNI/UA/Server-
header/UDP padding-профиль) детерминированно меняется раз в сутки. И сервер, и клиент
выводят одинаковый `MaskSet` из общего seed (SHA-256 от CA-сертификата) + UTC-даты через
HKDF-SHA256, без сетевой координации. Конфиг: `[transport.masks] enabled = true` (по
умолчанию). Новые подключения берут текущую маску; уже установленные остаются на своих.
Палитры: 16 SNI default + 15 SNI russian, 10 User-Agent, 5 Server-headers, 4 padding-профиля;
профиль 0 байт-в-байт совместим с v1-паддингом (бэк-совместимость). v3.2 добавляет
`palette = "default" | "russian" | "mixed"` для случая, когда нужно, чтобы outer SNI выглядел
как обращение к российскому сайту (см. сценарий в §7).
-**Multi-hop / onion routing v3.1 + v3.2.** Цепочка из 2-3 хопов: client →
entry-relay → (опционально middle) → exit. Entry-relay не знает destination, exit-узел не
знает клиентский IP. v3.2: per-hop client cert (CN entry и exit различаются — нельзя
слинковать handshakes по identity), cell padding (constant-size cells устраняют per-packet
size-fingerprinting), CIDR whitelist на relay'е. Конфиг — `[client.circuit]` /
`[server.relay]`. См. сценарий §7 для деплоймента «российский entry, иностранный exit».
-**Let's Encrypt outer-TLS cert.** `[server.outer_cert] cert_path / key_path` — outer-TLS
слой QUIC и TCP использует настоящий CA-trusted сертификат вместо self-signed Aura cert;
внутренний Aura mutual-auth handshake продолжает аутентификацию против Aura CA.
### v3.3 — Windows-as-client стал first-class
-**Windows OS-маршруты реализованы.** `[tunnel.os_routes] enabled = true` теперь работает
на Windows: `route ADD <net> MASK <mask> <gw> METRIC 1` для DIRECT-обходов, `netsh interface
ipv4 add route <prefix> "Aura" <tun_local_ip> store=active` для VPN-маршрутов через wintun-
адаптер. Дефолт-GW автодетектится через `route print 0`. Rollback подменяет `ADD``DELETE` и
`add``delete` на обоих путях. Подробности и пошаговый запуск — в §8.
-**wintun audit.** Найден и устранён баг: `Arc<wintun::Adapter>` больше не дропается раньше
`Session` (поле `_adapter` в `AuraTun` держит адаптер живым на всё время сессии).
-**Cross-compile.** Весь workspace проверен под `cargo check --target
x86_64-pc-windows-gnu` без warnings.
- ✓ **Bridge-discovery через подписанный CA-манифест (v3.3).**
`[client.bridges_discovery] enabled = true` плюс файл `bridges.signed` на диске. Админ
собирает манифест командой
`aura sign-bridges --ca /etc/aura/pki --bridges "203.0.113.10:443,198.51.100.20:443" --ttl-days 7 --out /etc/aura/bridges.signed`
(подпись ECDSA-P256/SHA-256 ключом CA — той же примитивой что in-band CRL). Клиент верифицирует
подпись против `[pki] ca_cert`, отвергает истёкшие манифесты (`expires_at < now`), и **расширяет**
статический список из `[client] bridges` (дубликаты по `SocketAddr` удаляются; статика остаётся
fallback'ом если файл повреждён / отсутствует). Фон-таск перечитывает файл каждые
`refresh_interval_secs` секунд (default 3600), горячее обновление без рестарта клиента. Сам HTTP-
пуш через CDN — план v3.4 (опциональная зависимость `reqwest` под feature gate). См.
`crates/aura-cli/src/bridges.rs` и интеграционный тест `tests/bridges_discovery.rs`.
### Остающиеся честные ограничения
- **TUN всё ещё требует root / Администратор** для **создания** интерфейса (это OS-уровень). На
Linux/macOS privilege drop минимизирует окно работы под root; на Windows аналога нет — клиент
работает от Администратора до выхода (warning в логе).
- **IPv6 в OS-маршрутах и iptables MASQUERADE** не реализован — только IPv4 (план v3.4).
- **Windows-as-server не первоклассный.** `[server.nat]` (IP-форвардинг + MASQUERADE) на
Windows не реализован; роль сервера / relay лучше держать на Linux/macOS. Windows клиент
работает с любым сервером.
- **Нативного Go-клиента для телефона нет** — через sing-box (Option B нативный Go-outbound,
по `protocol.md` + KAT из Rust, см. [`sing-box.md`](sing-box.md)). Сейчас доступен только
десктоп-клиент / process-bridge. Это явно исключённый из v2 пункт.
- **Bridge-discovery через push без рестарта клиента** — частично реализовано в v3.3:
подписанный CA-манифест на диске (`[client.bridges_discovery]`) горячо перечитывается фон-
таском; админ переподписывает файл и рассылает любым каналом (rsync/ansible/scp). HTTP-fetch
напрямую с CDN — план v3.4. Если все статически-перечисленные IP заблокированы и манифест не
обновлён до экспирации, восстановление требует доставки нового `bridges.signed` через
out-of-band канал.
---
## 7. Сценарий: российский entry-узел против тарификации иностранного трафика
### 7.1. Контекст и угроза
Российские операторы связи могут начать тарифицировать «иностранный трафик» отдельно: классификация
выполняется по destination IP исходящего пакета пользователя. Если первый IP, к которому
обращается устройство, — российский, биллинг считает соединение «российским», даже если внутри
этого соединения трафик уходит дальше за рубеж. Цель в этом сценарии — добиться того, чтобы
оператор биллил трафик пользователя как «российский», при этом сохраняя VPN-выход за рубежом.
Решение опирается на три компонента, уже реализованные в AuraVPN:
1. **Multi-hop / onion routing v3.1+** (`[client.circuit]` / `[server.relay]`) — entry-узел в РФ
не знает destination, exit-узел за рубежом не знает клиентский IP.
2. **Палитра SNI «russian»** (v3.2) — `[transport.masks] palette = "russian"` ротирует outer-TLS
SNI среди крупных российских доменов (`vk.com`, `www.ozon.ru`, `mail.yandex.ru`, ...).
3. **OS-уровень kill-switch** (`[tunnel.os_routes] enabled = true`) — гарантия, что системный
трафик (push-уведомления, OS-сервисы) не обходит туннель и не попадает напрямую к иностранным
серверам в обход entry-узла.
### 7.2. Топология
```
[устройство]
|
| весь трафик через TUN (kill-switch)
v
[оператор] <-- видит только UDP/443 на RU_VPS_IP, SNI = "vk.com"
|
v
[Russian VPS / entry-relay] <-- v3.1 relay: forward to next hop, never decodes IP packets
|
| inner Aura handshake (PQ-encrypted, opaque)
v
[Foreign VPS / exit] <-- настоящий VPN-выход в интернет
|
v
[internet]
```
Оператор видит только трафик до **entry-узла**: один UDP-поток с SNI крупного российского сайта.
Внутри этого потока — зашифрованный многохоп; entry-relay не имеет ключей внутреннего рукопожатия
и видит только AEAD-ciphertext, который он форвардит на exit. Exit видит только IP entry-узла, а
не IP клиентского устройства.
### 7.3. Что покупать
**Подходящие провайдеры для entry-узла в РФ** (юрисдикция РФ, IP в российских AS):
- **Selectel** (Москва, СПб).
- **Beget** (СПб).
- **Yandex.Cloud** (Москва).
- **VK Cloud** (бывш. Mail.ru Cloud Solutions).
- **Timeweb Cloud**.
**Неподходящие для роли entry-узла в РФ**:
- **Hetzner** (Германия/Финляндия) — IP классифицируется как «иностранный».
- **DigitalOcean / Vultr / Linode** (США/EU) — то же самое.
- **AWS / GCP / Azure** даже с российскими DC-локациями — IP-блоки за пределами российских AS у
большинства операторов.
Для **exit-узла** наоборот — берите любой удобный иностранный VPS (Hetzner, DigitalOcean, Vultr,
любой подходящий по юрисдикции и пропускной способности).
### 7.4. Конфиг сервера в РФ (entry-relay)
`server.toml` на российском VPS (например, Selectel с IP `RUSSIAN_VPS_IP`):
```toml
[server]
name = "aura-ru-entry-1"
listen = "0.0.0.0:443"
[pki]
ca_cert = "/etc/aura/pki/ca.crt"
cert = "/etc/aura/pki/server/server.crt"
key = "/etc/aura/pki/server/server.key"
[tunnel]
# Pool нужен формально (для v1-fallback-пути), но в роли чистого relay он не используется —
# bridged-клиенты не получают IP из пула и не регистрируются в ServerRouter.
pool_cidr = "10.7.0.0/24"
mtu = 1420
# v3.1: relay-режим. Принимаем ExtendBridge от клиента и сплайсим на foreign exit.
[server.relay]
enabled = true
allow_extend_to = ["EXIT_FOREIGN_IP:443"] # IP вашего иностранного exit-узла
# v3.2 cell padding: relay сам не декодирует — это сквозной байт-форвардинг. Знаки опции тут
# для симметрии конфига; реальный декод цельных ячеек — на exit'е.
cell_padding = true
cell_size = 1280
[transport.masks]
enabled = true
# v3.2: outer-TLS SNI крутится среди крупных российских доменов. Каждый день — другой домен.
palette = "russian"
# Опционально: настоящий outer-TLS сертификат (Let's Encrypt) поверх UDP/QUIC и TCP. Без него
# работает self-signed Aura, но с настоящим LE-сертификатом outer-handshake становится
# неотличим от обычного HTTPS на CA-trusted сайт.
[server.outer_cert]
cert_path = "/etc/letsencrypt/live/relay.example.ru/fullchain.pem"
key_path = "/etc/letsencrypt/live/relay.example.ru/privkey.pem"
```
И аналогичный `server.toml` на **иностранном exit-узле** — обычный VPN-сервер БЕЗ `[server.relay]`,
но с `cell_padding_for_circuit_clients = true` в секции `[server]`, чтобы он понимал
constant-size cells от клиента:
```toml
[server]
name = "aura-exit-1"
listen = "0.0.0.0:443"
# v3.2: exit для cell-padded клиентов — декодирует ячейки внутреннего рукопожатия.
cell_padding_for_circuit_clients = true
[pki]
ca_cert = "/etc/aura/pki/ca.crt"
cert = "/etc/aura/pki/server/exit.crt"
key = "/etc/aura/pki/server/exit.key"
[tunnel]
pool_cidr = "10.7.0.0/24"
[server.nat]
auto = true # включить IP-форвардинг и MASQUERADE на egress-интерфейсе
egress_iface = "eth0"
[transport.masks]
# На exit'е SNI палитра не критична (клиент видит exit только через relay) — оставим default.
palette = "default"
```
### 7.5. Конфиг клиента
```toml
[client]
name = "laptop"
server_addr = "RUSSIAN_VPS_IP:443" # entry-узел в РФ; именно этот IP видит оператор
sni = "relay.example.ru" # SAN серверного outer-TLS сертификата (если есть LE)
[pki]
ca_cert = "~/.aura/ca.crt"
cert = "~/.aura/client.crt"
key = "~/.aura/client.key"
[tunnel]
tun_name = "aura0"
local_ip = "10.7.0.2"
prefix = 24
mtu = 1420
[tunnel.split]
default = "VPN"
# КРИТИЧНО: kill-switch — весь трафик через TUN, OS-уровень. Без этого push-уведомления и
# OS-сервисы могут уйти напрямую в иностранные сервера в обход entry-узла, и оператор
# зачтёт это как «иностранный» трафик.
[tunnel.os_routes]
enabled = true
# v3.1 / v3.2: цепочка хопов client -> RU_entry -> foreign_exit.
[client.circuit]
enabled = true
cell_padding = true
cell_size = 1280
[[client.circuit.hops]]
addr = "RUSSIAN_VPS_IP:443" # entry в РФ — то, что видит оператор
cert_path = "~/.aura/circuit/entry.crt"
key_path = "~/.aura/circuit/entry.key"
[[client.circuit.hops]]
addr = "EXIT_FOREIGN_IP:443" # exit за рубежом, к которому привязаны DNS/маршруты внутри VPN
cert_path = "~/.aura/circuit/exit.crt"
key_path = "~/.aura/circuit/exit.key"
[transport.masks]
enabled = true
# Должно совпадать с palette = "russian" на entry-узле — иначе SNI в логах двух сторон
# не будут симметричны (на проводе это не ошибка, но удобнее для отладки).
palette = "russian"
```
Сертификаты двух хопов — разные (`entry.crt` != `exit.crt`). Это v3.2 identity-unlinkability:
entry-relay видит только клиентский cert для роли entry, exit-узел видит только cert для роли
exit, и они не пересекаются (см. `aura provision-client --circuit-hops 2 ...`).
### 7.6. Что это даёт
- **Оператор биллит как «российский».** На проводе оператор видит один UDP-поток на
`RUSSIAN_VPS_IP:443` — это российский IP в российской AS, классификатор биллинга его не
обозначает как иностранный.
- **SNI выглядит как обращение к российскому сайту.** В пакетах outer-TLS / outer-QUIC
hostname-камуфляж берётся из `SNI_PALETTE_RUSSIAN`: каждый день — другой домен (`vk.com`,
`www.ozon.ru`, `mail.yandex.ru`, ...). DPI видит «нормальный HTTPS на крупный российский
сайт».
- **Реальный VPN-выход — за рубежом.** Внутри multi-hop клиент дозванивается до иностранного
exit-узла; именно его IP видят внешние ресурсы. Entry-узел в РФ форвардит зашифрованный
трафик, не зная destination и не имея ключей внутреннего рукопожатия.
- **Kill-switch предотвращает обход.** `[tunnel.os_routes] enabled = true` программирует
системную таблицу маршрутов так, что весь трафик идёт через TUN — push-уведомления, OS-сервисы
и любые «прямые» обращения в обход VPN заблокированы, поэтому ничто из устройства не уйдёт
напрямую к иностранному IP в обход entry-узла.
### 7.7. Что это НЕ даёт (честное ограничение)
- **Не скрывает сам факт VPN-использования** от российских органов. DPI с deep-inspection может
по статистическим паттернам трафика (timing, размеры, поведение в течение сессии) узнать
Aura-протокол; ротация масок и `palette = "russian"` маскирует пассивного наблюдателя, но не
активного аналитика. Для дополнительной защиты включайте `[transport.knock]` и
`[transport.cover]` (port-knocking + cover traffic).
- **Не освобождает от ответственности за заходы на запрещённые ресурсы.** Кто и за что отвечает
при заходе на запрещённый ресурс через VPN — вопрос юрисдикции exit-узла и применимого
законодательства, не технический.
- **Не защищает от блокировки самого entry-IP.** Если СОРМ-система или Роскомнадзор начнут
активно блокировать конкретные VPS-IP, придётся ротировать IP / bridges. v3.3 решает это в две
ступени: (а) `[client] bridges = [...]` — статический список запасных entry-узлов, клиент
пробует их в случайном порядке при отказе primary; (б) `[client.bridges_discovery] enabled = true`
— клиент горячо перечитывает CA-подписанный манифест `bridges.signed` на диске (см. v3.3
раздел в §6 «Устранено в v2/v3»), так что админ ротирует список без рестарта клиентского
процесса — достаточно переподписать файл и доставить новой копией (rsync / ansible / любой
out-of-band канал). HTTP-fetch с CDN — план v3.4.
- **Cell padding не скрывает наличие туннеля.** Constant-size cells устраняют per-packet
size-fingerprinting внутри multi-hop, но не делают сам поток неотличимым от HTTPS — общий
объём и временные паттерны остаются. Это компромисс между обфускацией и накладными расходами.
### 7.8. Что менять при ротации
При смене IP entry-узла (например, при блокировке текущего) обновите три места:
1. `[[client.circuit.hops]] addr` первого хопа → новый `RUSSIAN_VPS_IP:443`.
2. `[client] server_addr` → тот же новый IP.
3. На новом VPS — поднять PKI, выпустить cert для entry-роли, перенести `server.toml` с
`[server.relay]` и `palette = "russian"`.
Перевыпускать сертификаты двух хопов не нужно — они остаются те же, меняется только wire-адрес
entry-узла. На сертификате entry-сервера должен быть SAN, совпадающий с `[client] sni`
(см. `aura pki issue-server --domain relay.example.ru`).
---
## 8. Windows как клиент
Windows-клиент стал first-class в v3.3. Сервер на Windows не поддерживается на уровне
автонастройки сети — `[server.nat]` (IP-форвардинг + MASQUERADE) реализован только для
Linux/macOS. Эта секция — про **клиент**.
### 8.1. Требования
- Windows 10 / 11 (или Server 2019+) с правами **Администратора** для процесса `aura.exe` —
поднятие wintun-адаптера и программирование таблицы маршрутов требуют привилегий.
- **wintun.dll** рядом с `aura.exe`. Скачать с официального сайта
[https://www.wintun.net/](https://www.wintun.net/) (драйвер от автора WireGuard);
распаковать `wintun/bin/amd64/wintun.dll` в каталог `aura.exe`.
### 8.2. Сборка / получение бинаря
Если у вас есть Rust toolchain на Windows — `cargo build --release` соберёт `target\release\aura.exe`.
С macOS / Linux можно собрать кросс-компиляцией (нужен mingw-w64):
```bash
rustup target add x86_64-pc-windows-gnu
# (на macOS) brew install mingw-w64
cargo build --release --target x86_64-pc-windows-gnu
# -> target/x86_64-pc-windows-gnu/release/aura.exe
```
### 8.3. PKI и провижининг
Команды `aura.exe pki ...` и `aura.exe provision-client ...` работают идентично Unix-версии
(см. §2.2). Бандл для клиента — те же три PEM-файла (`ca.crt`, `client.crt`, `client.key`)
плюс `client.toml`. PowerShell-форма:
```powershell
.\aura.exe pki init --ca-name "Aura Root CA" --out C:\ProgramData\Aura\pki
.\aura.exe pki issue-server --domain vpn.example.com --out C:\ProgramData\Aura\pki\server `
--ca C:\ProgramData\Aura\pki
.\aura.exe provision-client --id laptop-1 --out C:\Users\me\.aura
```
### 8.4. `client.toml` на Windows
Раскладка идентична §4.1. Имя TUN — это **отображаемое имя wintun-адаптера**: указанное в
`tun_name` имя становится `Display Name` адаптера в Device Manager (а также используется в
командах `netsh interface ipv4 add route ... "Aura"` — см. §8.5).
```toml
[client]
name = "laptop"
server_addr = "203.0.113.10:443"
sni = "vpn.example.com"
# run_as на Windows — no-op (нет аналога setresuid; warning в логе).
[pki]
ca_cert = "C:\\Users\\me\\.aura\\ca.crt"
cert = "C:\\Users\\me\\.aura\\client.crt"
key = "C:\\Users\\me\\.aura\\client.key"
[tunnel]
tun_name = "Aura" # имя wintun-адаптера; то же имя используется в netsh-командах ниже
local_ip = "10.7.0.2"
prefix = 24
mtu = 1420
[tunnel.split]
default = "VPN"
[[tunnel.split.direct]]
cidr = "192.168.0.0/16"
# v3.3: OS-уровень kill-switch теперь работает на Windows.
[tunnel.os_routes]
enabled = true
# Опционально: pin gateway + interface IP (читается `route print 0` если не задано).
# gateway = "192.168.1.1"
# egress_iface = "192.168.1.42"
[transport]
order = ["udp", "tcp", "quic"]
udp_port = 443
tcp_port = 443
quic_port = 444
```
### 8.5. Что делает `[tunnel.os_routes]` на Windows
На Linux/macOS клиент программирует системную таблицу маршрутов через `ip` / `route`. На
Windows — через `route ADD` (для DIRECT-обходов через исходный default-GW) и `netsh interface
ipv4 add route` (для VPN-маршрутов через wintun-адаптер).
**Auto-detect default GW:** клиент выполняет `route print 0` и парсит row `0.0.0.0 0.0.0.0
<gw> <interface_ip> <metric>` из IPv4 Active Routes. Если автодетект не сработал (например,
у машины несколько NIC и нет default'а в IPv4-таблице) — задайте `gateway` и `egress_iface`
явно в `[tunnel.os_routes]`. На Windows `egress_iface` — это **IP** upstream-интерфейса
(не имя), как в колонке `Interface` в `route print`.
**Что реально выполняется** (с пулом DIRECT `192.168.0.0/16` и default = VPN):
```
netsh interface ipv4 add route 0.0.0.0/0 "Aura" 10.7.0.2 store=active
route ADD 192.168.0.0 MASK 255.255.0.0 192.168.1.1 METRIC 1
```
**Что выполняется при выходе клиента** (Drop порядка LIFO):
```
route DELETE 192.168.0.0 MASK 255.255.0.0
netsh interface ipv4 delete route 0.0.0.0/0 "Aura"
```
`store=active` указывает Windows не сохранять маршрут в персистентном store — он привязан к
TUN, который исчезает на выходе клиента. Параметр `METRIC 1` обеспечивает приоритет
DIRECT-обхода над любыми существующими маршрутами с большей метрикой.
### 8.6. Запуск
PowerShell как Администратор:
```powershell
cd C:\Aura
.\aura.exe client --config .\client.toml
```
В логе при успехе:
```
INFO connected and authenticated to server peer=Some("vpn.example.com") mode=udp
INFO OS-level split-tunnel routes installed (DIRECT traffic now bypasses the TUN)
INFO running: netsh interface ipv4 add route 0.0.0.0/0 "Aura" 10.7.0.2 store=active
INFO running: route ADD 192.168.0.0 MASK 255.255.0.0 192.168.1.1 METRIC 1
```
Прервать через `Ctrl+C` — выводящийся guard корректно вызывает `route DELETE` / `netsh ...
delete route` и затем закрывает wintun-сессию + адаптер (см. §8.7).
### 8.7. Cleanup на Windows (что происходит при остановке клиента)
Порядок dropping:
1. **OsRouteGuard::drop** — выполняет rollback-команды в LIFO-порядке (`route DELETE ...`,
затем `netsh ... delete route ...`). Ошибки логируются warn-ом, дальнейший rollback
продолжается — один сбойный шаг не остановит зачистку остальных маршрутов.
2. **wintun::Session::drop**`WintunEndSession` завершает сессию (закрывает ring buffer).
3. **wintun::Adapter::drop**`WintunCloseAdapter` снимает адаптер с системы. Drop порядка
полей в `AuraTun` гарантирует, что Session завершается до Adapter (поле `inner` объявлено
раньше `_adapter`).
Если процесс упал без graceful shutdown (kill -9 / BSOD): wintun-адаптер останется
зарегистрированным в системе, и при следующем запуске `Adapter::create` найдёт его по имени и
переиспользует. Орфанных системных маршрутов в персистентном store не будет — все наши
маршруты идут через `store=active`, которые система очищает на reboot.
### 8.8. Известные ограничения Windows-клиента
- **`run_as`** на Windows — no-op. Аналога `setresuid` для безпрепятственного drop'а к
service-account во время работы нет; рекомендация — запустить `aura.exe` как Windows
Service от выделенной учётной записи (см. документацию `sc.exe create`), либо просто из
PowerShell-сессии Администратора.
- **`[server.nat]`** на Windows не реализован — Windows-as-server не первоклассный сценарий.
Используйте Linux/macOS для роли сервера / relay.
- **IPv6 routes** программируются через `netsh interface ipv6 add route` для VPN, но IPv6
DIRECT-обходы попадают в тот же `netsh ipv6` путь (а не в IPv4-only `route ADD`). Для
чистой IPv4-only установки это не имеет значения.
- **Mixed-mode** (часть транспортов в одну сеть, часть в другую) на Windows не тестировался
глубоко — `netsh ... store=active` маршруты могут конфликтовать с существующими VPN-
клиентами (WireGuard, OpenVPN) если те уже захватили default-route. Отключите конкурирующие
VPN перед запуском aura-клиента.