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>
234 lines
14 KiB
Markdown
234 lines
14 KiB
Markdown
# PKI Aura
|
|
|
|
Aura использует небольшую самодостаточную X.509-PKI для **взаимной аутентификации** во внутреннем
|
|
рукопожатии. Один самоподписанный **CA** Aura выдаёт один сертификат для **сервера** и по одному
|
|
сертификату для каждого **клиента**. Во время рукопожатия клиент проверяет сертификат сервера, а
|
|
сервер — сертификат клиента, и в обе стороны проверка идёт против этого CA.
|
|
|
|
PKI реализована в крейте `aura-pki` (`ca.rs`, `cert.rs`, `store.rs`) и доступна в командной строке
|
|
как `aura pki ...` (`crates/aura-cli/src/pki.rs`, `crates/aura-cli/src/main.rs`).
|
|
|
|
> Внешний QUIC/TLS-слой эту PKI **не** использует — он принимает любой сертификат (см.
|
|
> `protocol.md`, раздел про слой мимикрии). Всё доверие сертификатам сосредоточено во внутреннем
|
|
> рукопожатии Aura.
|
|
|
|
---
|
|
|
|
## Модель доверия
|
|
|
|
```
|
|
Aura CA (self-signed)
|
|
CN = <ca_name>, isCA, keyCertSign/crlSign
|
|
|
|
|
+------------+------------+
|
|
| |
|
|
server leaf client leaf(s)
|
|
CN = <domain> CN = <client_id>
|
|
SAN: DNS:<domain> (нет SAN)
|
|
EKU: serverAuth EKU: clientAuth
|
|
```
|
|
|
|
- **CA** самоподписан, имеет `BasicConstraints: CA` (unconstrained) и `keyUsage`:
|
|
`keyCertSign` + `crlSign` + `digitalSignature`. Срок действия по умолчанию — **3650 дней**.
|
|
- **Server leaf** несёт `CN = <domain>`, **`DNS:<domain>` SAN** и
|
|
`extendedKeyUsage = serverAuth`. Именно DNS-SAN сравнивается клиентом с ожидаемым `server_name`.
|
|
- **Client leaf** несёт `CN = <client_id>` и `extendedKeyUsage = clientAuth`. Этот CN — та
|
|
идентичность, которую увидит сервер и запишет как `peer_id` сессии.
|
|
- `keyUsage` для листовых сертификатов: `digitalSignature` + `keyEncipherment`. Срок действия по
|
|
умолчанию — **365 дней**.
|
|
- У всех выпускаемых сертификатов (и у CA, и у листовых) `not_before` смещён назад на **5 минут**,
|
|
чтобы выдерживать небольшой расхождение часов.
|
|
|
|
### Алгоритмы
|
|
|
|
Все ключи — **ECDSA P-256 / SHA-256** (`KeyPair::generate` rcgen по умолчанию). Приватные ключи
|
|
сохраняются в **PKCS#8 PEM**. Проверка цепочки (`cert.rs`) принимает ECDSA P-256/SHA-256
|
|
(обязательно), а также ECDSA P-384/SHA-384 и Ed25519, — так что развёртывание сможет позже сменить
|
|
тип ключа без изменения кода.
|
|
|
|
---
|
|
|
|
## Раскладка файлов
|
|
|
|
CLI хранит файлы в обычных директориях. Стандартные имена (`crates/aura-cli/src/pki.rs`):
|
|
|
|
| Файл | Константа | Содержимое |
|
|
|---------------|------------|--------------------------------------------------|
|
|
| `ca.crt` | `CA_CERT` | Сертификат CA (PEM) |
|
|
| `ca.key` | `CA_KEY` | Приватный ключ CA (PKCS#8 PEM) — **секрет** |
|
|
| `server.crt` | | Листовой сертификат сервера (PEM) |
|
|
| `server.key` | | Приватный ключ сервера (PEM) — **секрет** |
|
|
| `client.crt` | | Листовой сертификат клиента (PEM) |
|
|
| `client.key` | | Приватный ключ клиента (PEM) — **секрет** |
|
|
| `revoked.crl` | `CRL_FILE` | Список отозванных идентификаторов (по одному в строке) |
|
|
|
|
Команды `issue-server` и `issue-client` загружают CA из `ca.crt` + `ca.key` в директории CA и
|
|
записывают `server.{crt,key}` / `client.{crt,key}` в выходную директорию. Пути, начинающиеся с
|
|
`~`, раскрываются в домашнюю директорию (из `$HOME`, либо `$USERPROFILE` на Windows).
|
|
|
|
Эти имена напрямую соответствуют секции `[pki]` в `server.toml` / `client.toml`
|
|
(`ca_cert`, `cert`, `key`).
|
|
|
|
---
|
|
|
|
## Команды `aura pki`
|
|
|
|
```
|
|
aura pki init --ca-name <CN> --out <DIR>
|
|
aura pki issue-server --domain <DNS> --out <DIR> [--ca <CA_DIR>]
|
|
aura pki issue-client --id <CLIENT> --out <DIR> [--ca <CA_DIR>]
|
|
aura pki revoke --id <ID> [--crl <PATH>]
|
|
aura pki list [--crl <PATH>]
|
|
```
|
|
|
|
Для `issue-server` / `issue-client` параметр `--ca` по умолчанию равен значению `--out` (так что CA
|
|
и выпущенный лист могут лежать в одной директории). Для `revoke` / `list` параметр `--crl` по
|
|
умолчанию равен `./revoked.crl`.
|
|
|
|
### `init` — создать CA
|
|
|
|
Генерирует свежий самоподписанный CA и записывает `ca.crt` + `ca.key` в `--out` (директория
|
|
создаётся при необходимости).
|
|
|
|
```bash
|
|
aura pki init --ca-name "Aura Root CA" --out ~/.aura
|
|
# CA generated:
|
|
# cert: ~/.aura/ca.crt
|
|
# key: ~/.aura/ca.key
|
|
```
|
|
|
|
### `issue-server` — выпустить сертификат сервера
|
|
|
|
Выпускает листовой сертификат для DNS-имени, подписанный CA, с SAN `DNS:<domain>` и EKU
|
|
`serverAuth`.
|
|
|
|
```bash
|
|
aura pki issue-server --domain vpn.example.com --out ~/.aura --ca ~/.aura
|
|
# server certificate issued for 'vpn.example.com':
|
|
# cert: ~/.aura/server.crt
|
|
# key: ~/.aura/server.key
|
|
```
|
|
|
|
> Значение `--domain` должно совпадать с тем именем, которое клиент ожидает в рукопожатии. В
|
|
> поставляемой конфигурации клиента это имя берётся из `[client] sni`, поэтому SNI камуфляжа и
|
|
> проверяемый SAN сервера — это одно и то же значение.
|
|
|
|
### `issue-client` — выпустить сертификат клиента
|
|
|
|
Выпускает листовой сертификат с `CN = <id>` и EKU `clientAuth`. Это `<id>` станет проверенным
|
|
`peer_id`, который увидит сервер.
|
|
|
|
```bash
|
|
aura pki issue-client --id laptop --out ~/.aura --ca ~/.aura
|
|
# client certificate issued for 'laptop':
|
|
# cert: ~/.aura/client.crt
|
|
# key: ~/.aura/client.key
|
|
```
|
|
|
|
### `revoke` — добавить в список отзыва
|
|
|
|
Добавляет идентификатор — это либо **client id / Common Name**, либо **серийный номер
|
|
сертификата** (строчные шестнадцатеричные цифры без разделителей) — в CRL-файл, создавая его (и
|
|
родительские директории) при отсутствии.
|
|
|
|
```bash
|
|
aura pki revoke --id laptop --crl ~/.aura/revoked.crl
|
|
# revoked 'laptop' (CRL: ~/.aura/revoked.crl)
|
|
```
|
|
|
|
### `list` — показать отозванные идентификаторы
|
|
|
|
Печатает идентификаторы из CRL-файла (пусто, если файла нет).
|
|
|
|
```bash
|
|
aura pki list --crl ~/.aura/revoked.crl
|
|
# revoked identifiers (CRL: ~/.aura/revoked.crl):
|
|
# laptop
|
|
```
|
|
|
|
### Полный пример
|
|
|
|
```bash
|
|
# 1. Создать CA.
|
|
aura pki init --ca-name "Aura Root CA" --out ~/.aura
|
|
|
|
# 2. Выпустить серверный сертификат для публичного DNS-имени.
|
|
aura pki issue-server --domain vpn.example.com --out ~/.aura
|
|
|
|
# 3. Выпустить клиентский сертификат — по одному на устройство.
|
|
aura pki issue-client --id laptop --out ~/.aura
|
|
|
|
# 4. (позже) Отозвать скомпрометированный клиент.
|
|
aura pki revoke --id laptop
|
|
```
|
|
|
|
---
|
|
|
|
## Проверка
|
|
|
|
Проверку выполняет `AuraCertVerifier` (`crates/aura-pki/src/cert.rs`), собранный из PEM-сертификата
|
|
CA. Внутри он использует **`rustls-webpki`** для валидации листового сертификата пира против CA как
|
|
trust anchor. Рукопожатие Aura вызывает его с обеих сторон (см. `protocol.md`).
|
|
|
|
**Сертификат сервера** (`verify_server_cert`), запускает клиент:
|
|
|
|
1. webpki-валидация цепочки против CA с key usage **`serverAuth`** плюс проверка срока действия
|
|
(времени).
|
|
2. Лист должен быть валиден для запрошенного `server_name` (совпадение DNS-SAN); расхождение —
|
|
`NameMismatch`.
|
|
3. Проверка по CRL (см. ниже).
|
|
|
|
**Сертификат клиента** (`verify_client_cert`), запускает сервер:
|
|
|
|
1. webpki-валидация цепочки против CA с key usage **`clientAuth`** плюс проверка срока действия.
|
|
2. **client id** извлекается как первый Common Name из subject листа (отсутствие CN —
|
|
`MissingIdentity`).
|
|
3. Проверка по CRL.
|
|
4. Возвращает client id, который рукопожатие фиксирует как `peer_id` сессии.
|
|
|
|
Листовой сертификат передаётся **inline** в рукопожатии (DER, без промежуточной цепочки); CA — это
|
|
единственный trust anchor. Владение приватным ключом листового сертификата доказывается отдельно
|
|
подписью рукопожатия по транскрипту (см. `protocol.md`).
|
|
|
|
Ошибки сообщаются как `PkiError`: `CertParse`, `EmptyChain`, `TrustAnchor`, `Verification`,
|
|
`NameMismatch`, `MissingIdentity`, `Revoked`.
|
|
|
|
---
|
|
|
|
## Отзыв (CRL)
|
|
|
|
Механизм отзыва в Aura v1 намеренно минимальный (`crates/aura-pki/src/store.rs`). `CrlStore` — это
|
|
**множество строк-идентификаторов отозванных сертификатов**, где идентификатор — это либо:
|
|
|
|
- **серийный номер** сертификата (строчные hex-цифры без разделителей), либо
|
|
- **client id / Common Name**.
|
|
|
|
При проверке, если CRL непуст, листовой сертификат отвергается (`Revoked`), когда **либо** его
|
|
серийный номер, **либо** его Common Name присутствует в множестве. Пустой CRL пропускает проверку
|
|
полностью.
|
|
|
|
Формат на диске — один идентификатор в строке; пустые строки и комментарии `#` игнорируются при
|
|
загрузке. Файл управляется командами `aura pki revoke` / `aura pki list`.
|
|
|
|
> Ограничение v1: это плоское множество разрешения/запрета, а не подписанный X.509 CRL. Нет
|
|
> подписи CRL, нет `nextUpdate` и нет автоматического распространения — файл нужно доставить на
|
|
> проверяющую сторону вне протокола. Верификатор передаёт `None` в собственные крючки отзыва
|
|
> webpki и полагается исключительно на это множество.
|
|
|
|
---
|
|
|
|
## Замечания по безопасности
|
|
|
|
- **Защищайте приватные ключи.** `ca.key` — корень всего доверия; владея им, можно выпускать
|
|
любые валидные серверные/клиентские сертификаты. `server.key` / `client.key` должны оставаться
|
|
на своих хостах. CLI пишет их с дефолтными правами файловой системы — ограничивайте доступ
|
|
средствами ОС.
|
|
- **CA самоподписан и не ограничен** (`BasicConstraints: CA` unconstrained). Это единственный
|
|
trust anchor; в v1 нет уровня промежуточных CA.
|
|
- **Идентичность сервера связана с именем.** Клиент принимает только тот серверный лист, чей
|
|
DNS-SAN совпадает с ожидаемым именем, поэтому другой валидный лист от того же CA не будет
|
|
принят для чужого хоста.
|
|
- **Отзыв — best-effort** (см. выше): планируйте раздачу CRL-файла и поддерживайте его в актуальном
|
|
состоянии на каждом сервере, который проверяет клиентов.
|
|
- **Срок жизни листов — 365 дней**; планируйте перевыпуск. Автоматической ротации в v1 нет.
|