94 lines
3.9 KiB
Markdown
94 lines
3.9 KiB
Markdown
# Архитектура
|
||
|
||
## Границы ответственности
|
||
|
||
Hysteria2 URI содержит только переносимые реквизиты VPN:
|
||
|
||
- authentication;
|
||
- hostname и port/port ranges;
|
||
- SNI и `insecure`;
|
||
- `salamander` и obfs password.
|
||
|
||
`policy.json` содержит локальную инфраструктурную политику:
|
||
|
||
- `eth0` — upstream;
|
||
- `eth1` — VPN LAN;
|
||
- `tun-sb0`, `172.19.0.1/30`, MTU 1400;
|
||
- TUN routes, nftables marks и rule/table indexes;
|
||
- bootstrap DNS и DoH через `hy2-out`;
|
||
- bandwidth hints и healthcheck.
|
||
|
||
Renderer не патчит существующий JSON. Полная конфигурация каждый раз строится
|
||
из typed model. Это устраняет config drift и исторические endpoint `/32`.
|
||
|
||
## Поток применения
|
||
|
||
```text
|
||
URI + policy
|
||
│
|
||
├─ strict parsing / schema validation
|
||
├─ exact sing-box version and build-tag gate
|
||
├─ deterministic render
|
||
├─ secure candidate in /etc/sing-box
|
||
└─ sing-box check -c candidate
|
||
│
|
||
▼
|
||
redacted comparison
|
||
│
|
||
unchanged ─────── changed
|
||
│ │
|
||
▼ ├─ last-good backup
|
||
no restart ├─ atomic replace
|
||
├─ systemctl restart
|
||
└─ bounded healthcheck
|
||
│
|
||
fail ┴ success
|
||
│ │
|
||
▼ ▼
|
||
rollback state.json
|
||
```
|
||
|
||
Все операции изменения сериализованы `flock`-совместимой блокировкой. Поэтому
|
||
ручной `import` и запоздалый event от systemd.path не могут применять два
|
||
candidate одновременно.
|
||
|
||
## Почему endpoint IP не нужен
|
||
|
||
Outbound сохраняет DNS hostname и содержит `bind_interface=eth0`. В 1.13.19
|
||
`route.auto_detect_interface` не применяется к outbound с явным
|
||
`bind_interface`. Bootstrap DNS также привязан к `eth0`. Endpoint IP поэтому не
|
||
является частью TUN policy.
|
||
|
||
После смены A-записи следующая Hysteria2-сессия разрешает имя заново через
|
||
`bootstrap-dns`. `doctor` дополнительно проверяет, что текущие A-записи не
|
||
попали в exclusions.
|
||
|
||
## Файловая модель
|
||
|
||
```text
|
||
/etc/vpn-egress/
|
||
├── policy.json root:root 0600
|
||
└── hysteria2.uri root:root 0600
|
||
|
||
/var/lib/vpn-egress/
|
||
├── state.json без секретов, 0600
|
||
├── last-good.json содержит secrets, 0600
|
||
└── backups/ ограниченная история, 0700/0600
|
||
|
||
/etc/sing-box/
|
||
└── config.json root:root 0600
|
||
```
|
||
|
||
Service sing-box уже имеет `CAP_DAC_READ_SEARCH`, поэтому сохраняется текущая
|
||
рабочая модель `root:root 0600`.
|
||
|
||
Guard читает имена интерфейсов из того же policy и устанавливает всю nftables
|
||
таблицу одной batch-транзакцией. Если новый ruleset некорректен, nft не оставляет
|
||
систему с частично заменённой таблицей.
|
||
|
||
## Версионная граница
|
||
|
||
В коде существует только `renderer_1_13_19.py`. Renderer для 1.14 не является
|
||
пустой заготовкой: он появится только в отдельной миграции после аудита схемы,
|
||
маршрутизации и полных интеграционных тестов.
|