fix28: обновлена production-документация и упаковка docs в deb

This commit is contained in:
2026-05-12 16:56:36 +05:00
parent a9bac8fdad
commit 69c737bcf4
10 changed files with 488 additions and 150 deletions
+252 -36
View File
@@ -1,69 +1,285 @@
# AWG2 Harness
`AWG2 Harness` — host-side multi-orchestrator на Rust для production-сервера с AmneziaWG/AWG 2.0.
`AWG2 Harness` — host-side orchestrator для выделенного Debian-хоста с self-hosted AmneziaWG. Пакет управляет инфраструктурным состоянием вокруг Amnezia: per-peer traffic shaping, IFB ingress shaping, nftables host firewall, anti-torrent connection guard, managed swap, диагностикой drift и плановым reboot.
Это не просто перенос bash-скриптов на Rust. Целевая система должна воспроизвести и затем постоянно удерживать всё состояние, которое сейчас создаёт `Amnezia limits (old bash)`: discovery контейнера и интерфейса, список peers, per-peer traffic shaping в обе стороны, `tc` topology, `ifb0`, auto-healing после рестартов, диагностику, плановый reboot и swap setup, а также опциональный nftables anti-torrent guard через per-peer connection-count limit.
Проект разрабатывался и проверялся под AmneziaWG/AWG 2.0 и совместимый legacy-сценарий AmneziaWG/AWG 1.5. Он не заменяет AmneziaVPN, не генерирует ключи клиентов и не меняет продуктовую логику Amnezia. Amnezia сначала ставится официальным способом, после этого устанавливается AWG2 Harness.
Сервер выделенный: Debian 13 amd64, SSH заранее настроен, рядом нет соседних сервисов, после установки Amnezia доставляется наш бинарь. Поэтому бинарь имеет право и обязан полностью управлять нужными правилами хоста и network namespace Amnezia, а не быть пассивной обёрткой, которая боится менять состояние системы. При этом он не форкает Amnezia и не меняет её продуктовую логику; он управляет инфраструктурным состоянием вокруг неё.
Текущий репозиторий:
## Что ставит пакет
```text
AWG2_harness/
├── Amnezia limits (old bash)/ # production reference behavior
├── reboot_module/ # production auxiliary binary: awg2-reboot-scheduler
├── traffic_core/ # целевая зона для Rust traffic/orchestrator логики
└── docs/ # документация по миграции, архитектуре и эксплуатации
/usr/sbin/awg2-harness # основной orchestrator
/usr/sbin/awg2-reboot-scheduler # отдельный reboot scheduler
/lib/systemd/system/awg2-harness.service
/lib/systemd/system/awg2-reboot-scheduler.service
```
Production reboot path — внешний scheduler service [`awg2-reboot-scheduler.service`](awg2-reboot-scheduler.service).
`awg2-harness.service` запускает `ensure-swap` перед стартом daemon, затем в фоне держит drift recovery. Daemon слушает Docker events и дополнительно делает периодический reconcile. `awg2-reboot-scheduler.service` отдельно читает общий config, выбирает случайное время в заданном UTC-окне и запрашивает reboot через `systemctl --no-block reboot`.
Важно: `Amnezia limits (old bash)` используется как эталон production-поведения, а `reboot_module` ставится как второй production-бинарь.
Пакет **не** устанавливает Docker, **не** устанавливает AmneziaVPN, **не** включает сервисы автоматически и **не** создаёт боевой `/etc/awg2-harness/config.toml`. Это сделано намеренно: SSH, Amnezia и боевой config должны быть подготовлены оператором до включения firewall/reconcile.
## Production deployment model
## Репозиторий
AWG2 Harness собирается на отдельной Debian 13 amd64 build-machine.
```text
awg2_harness/
├── Amnezia limits (old bash)/ # исторический reference старой bash-схемы
├── reboot_module/ # исходники awg2-reboot-scheduler
├── traffic_core/ # исходники awg2-harness
├── tools/ # build/package tooling
├── docs/ # эксплуатационная документация
├── config.example.toml # пример боевого конфига
├── awg2-harness.service # основной systemd unit
└── awg2-reboot-scheduler.service # reboot scheduler unit
```
Production Amnezia-хост **не должен** требовать Rust, cargo, clippy, source tree и legacy bash-скрипты. На production-хост переносится готовый Debian-пакет и вручную подготовленный `/etc/awg2-harness/config.toml`.
`Amnezia limits (old bash)` оставлен как historical reference, а не как runtime-зависимость. На production-хосте после перехода на `.deb` source tree и старые bash-скрипты не нужны.
Build machine:
## Runtime-план production-развёртывания
Ниже целевой порядок установки. Его смысл простой: сначала безопасный SSH, потом официальная Amnezia, потом AWG2 Harness.
### 1. Подготовить Debian-хост
Целевая платформа проекта — Debian 13 amd64 на KVM/полноценной VM или dedicated host. Для Amnezia upstream также указывает Debian 12/13 и Ubuntu 22.04/24.04 как официально поддерживаемые OS, x86-64/amd64, публичный IPv4, SSH-доступ и Docker-based self-hosted deployment через приложение AmneziaVPN.
Минимальный baseline перед установкой Amnezia:
```bash
apt update
apt install -y openssh-server ca-certificates curl
```
### 2. Настроить SSH до установки Amnezia
Production-пример использует порт `2323` и вход только по ключу. Сначала убедитесь, что публичный ключ уже добавлен в `~root/.ssh/authorized_keys` или в ключевой файл нужного sudo-пользователя.
```bash
install -d -m 0700 /root/.ssh
nano /root/.ssh/authorized_keys
chmod 0600 /root/.ssh/authorized_keys
```
Затем включите отдельный drop-in для порта и key-only login:
```bash
install -d -m 0755 /etc/ssh/sshd_config.d
cat >/etc/ssh/sshd_config.d/99-awg2-production.conf <<'EOF'
Port 2323
PermitRootLogin prohibit-password
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
EOF
sshd -t
systemctl reload ssh 2>/dev/null || systemctl reload sshd
ss -ltnp | grep ':2323'
```
Критично: не закрывайте старую SSH-сессию, пока не проверили новый вход с другого терминала:
```bash
ssh -p 2323 root@<server-ip>
```
Если сервер уже переведён на 2323 случайно и нужно временно вернуть 22 для тестов, поменяйте `Port 2323` на `Port 22`, выполните `sshd -t`, `systemctl reload ssh` и проверьте `ss -ltnp | grep ':22'`.
### 3. Установить AmneziaVPN официальным способом
AWG2 Harness не устанавливает Amnezia. Используйте официальный self-hosted flow AmneziaVPN:
1. Установите актуальное приложение AmneziaVPN на рабочую машину.
2. В приложении выберите self-hosted server.
3. Укажите публичный IPv4 сервера, SSH-порт, SSH-пользователя и приватный ключ.
4. Установите AmneziaWG/AWG. Для AWG 2.0 нужен актуальный AmneziaVPN, где протокол AmneziaWG 2.0 доступен в списке протоколов.
5. После установки убедитесь, что контейнер поднят и UDP-порт опубликован.
Проверка на сервере:
```bash
docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
C="$(docker ps --format '{{.Names}}' | grep -E '^(amnezia-awg2|amnezia-awg)$' | head -n1)"
echo "container=$C"
docker port "$C"
docker exec "$C" wg show
```
Ожидаемые имена контейнера по умолчанию: `amnezia-awg2` или `amnezia-awg`. Ожидаемый интерфейс внутри container netns: обычно `awg0`, fallback-кандидаты описаны в `config.example.toml`.
### 4. Собрать пакет на build-machine
На production-хосте не нужны Rust, cargo и source tree. Сборка делается отдельно.
```bash
cd awg2_harness
./tools/build.sh --prepare-build-env --bootstrap-rust --yes
./tools/build.sh --package
./tools/build.sh --package --version 0.1.6
sha256sum -c dist/SHA256SUMS
dpkg-deb -I dist/awg2-harness_0.1.6_amd64.deb
dpkg-deb -c dist/awg2-harness_0.1.6_amd64.deb | sort
```
Копирование на production host:
`--version` можно не указывать, если версия в `traffic_core/Cargo.toml` уже соответствует release. Если version override указан, он меняет версию Debian-пакета, а не версию crate в `Cargo.toml`.
### 5. Перенести `.deb` и config на production
```bash
scp dist/awg2-harness_*_amd64.deb root@vpn-host:/root/
scp config.toml root@vpn-host:/root/awg2-harness.config.toml
scp -P 2323 dist/awg2-harness_*_amd64.deb root@<server-ip>:/root/
scp -P 2323 dist/SHA256SUMS root@<server-ip>:/root/
scp -P 2323 config.example.toml root@<server-ip>:/root/awg2-harness.config.toml
```
Production host:
Перед установкой отредактируйте `/root/awg2-harness.config.toml`: SSH-порт должен совпадать с реальным sshd, `amnezia_port` — с опубликованным UDP-портом контейнера, `peer_ipv4_cidr` — с подсетью клиентов Amnezia.
### 6. Установить пакет и config
```bash
cd /root
sha256sum -c SHA256SUMS
apt install ./awg2-harness_*_amd64.deb
install -d -m 0755 /etc/awg2-harness
install -m 0644 /root/awg2-harness.config.toml /etc/awg2-harness/config.toml
awg2-harness validate-config --config /etc/awg2-harness/config.toml
awg2-harness validate-runtime --config /etc/awg2-harness/config.toml
awg2-harness apply-limits --config /etc/awg2-harness/config.toml
awg2-harness apply-firewall --config /etc/awg2-harness/config.toml
awg2-harness status --config /etc/awg2-harness/config.toml
systemctl enable --now awg2-harness.service
systemctl enable --now awg2-reboot-scheduler.service
```
В production запускаются оба сервиса: [`awg2-harness.service`](awg2-harness.service) и [`awg2-reboot-scheduler.service`](awg2-reboot-scheduler.service).
Пакет ставит пример config в `/usr/share/doc/awg2-harness/repo/config.example.toml`, но не копирует его в `/etc` автоматически.
Основная документация:
### 7. Ручная preflight-проверка
- [`docs/MIGRATION.md`](docs/MIGRATION.md) — исправленная миграционная стратегия bash → Rust multi-orchestrator.
- [`docs/OLD_BASH_BEHAVIOR.md`](docs/OLD_BASH_BEHAVIOR.md) — зафиксированное поведение старой bash-системы по файлам.
- [`docs/IMPLEMENTATION_SPEC.md`](docs/IMPLEMENTATION_SPEC.md) — целевая архитектура бинаря и state reconciliation.
- [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md) — единый `config.toml` без эксплуатационного мусора.
- [`docs/INSTALLATION.md`](docs/INSTALLATION.md) — установка на Debian 13 amd64.
```bash
awg2-harness --config /etc/awg2-harness/config.toml validate-config
awg2-harness --config /etc/awg2-harness/config.toml validate-runtime
awg2-harness --config /etc/awg2-harness/config.toml ensure-swap
awg2-harness --config /etc/awg2-harness/config.toml apply-firewall
awg2-harness --config /etc/awg2-harness/config.toml apply-limits
awg2-harness --config /etc/awg2-harness/config.toml status
```
Ожидаемый healthy state:
```text
System:
service: inactive # до enable --now нормально
Traffic actual:
drift: no
Host firewall:
drift: no
Anti-torrent:
drift: no
Swap:
drift: no
```
Если peers ещё нет, `apply-limits` корректно напишет `no peers found, skip tc apply`. Это не ошибка: traffic topology строится, когда есть хотя бы один peer allowed IP в целевой подсети.
### 8. Включить systemd services
```bash
systemctl daemon-reload
systemctl enable --now awg2-harness.service
systemctl enable --now awg2-reboot-scheduler.service
systemctl status awg2-harness.service --no-pager -l
systemctl status awg2-reboot-scheduler.service --no-pager -l
journalctl -u awg2-harness.service --since '5 minutes ago' --no-pager
journalctl -u awg2-reboot-scheduler.service --since '5 minutes ago' --no-pager
```
После 30–60 секунд:
```bash
awg2-harness --config /etc/awg2-harness/config.toml status
```
Healthy daemon показывает `service: active`, `traffic=ok`, `firewall=ok`, `swap=ok` в recent events и пустой `last_error`.
### 9. Проверить после reboot
```bash
reboot
```
После возврата:
```bash
systemctl status docker --no-pager -l
systemctl status awg2-harness.service --no-pager -l
systemctl status awg2-reboot-scheduler.service --no-pager -l
awg2-harness --config /etc/awg2-harness/config.toml status
```
Дополнительно можно проверить фактическую `tc` topology внутри network namespace Amnezia:
```bash
C="$(docker ps --format '{{.Names}}' | grep -E '^(amnezia-awg2|amnezia-awg)$' | head -n1)"
PID="$(docker inspect -f '{{.State.Pid}}' "$C")"
nsenter -t "$PID" -n -- tc class show dev awg0
nsenter -t "$PID" -n -- tc class show dev ifb0
nsenter -t "$PID" -n -- nft list table inet awg2_harness
nft list table inet awg2_host_firewall
```
Для default config на одном peer ожидаются классы `1:10` и `2:10` с `rate 50Mbit ceil 50Mbit`, root classes `1:1` и `2:1` с `500Mbit`, host firewall table `inet awg2_host_firewall` и anti-torrent table `inet awg2_harness` внутри Amnezia netns.
## CLI команды
Все команды принимают `--config`. Если параметр не указан, используется `/etc/awg2-harness/config.toml`.
```bash
awg2-harness --config /etc/awg2-harness/config.toml <command>
```
`validate-config` проверяет TOML и смысловые ограничения: пустые списки, формат rate (`50mbit`, `500mbit`), CIDR, reboot window, swap path, SSH/Amnezia ports, protocol list.
`validate-runtime` проверяет host tools, которые реально нужны выбранным модулям: `tc`, `ip`, `wg`, `docker`, `modprobe`, `nsenter`, а также `nft`, `systemctl`, swap tooling при включённых firewall/reboot/swap.
`ensure-swap` приводит swap к desired state: создаёт/проверяет swapfile, включает его с заданным priority, применяет `vm.swappiness`, делает swap state exclusive и переписывает swap entries в `/etc/fstab` под управляемый swapfile.
`apply-firewall` делает два reconcile: host INPUT firewall в таблице `inet awg2_host_firewall` и anti-torrent guard в выбранном namespace. Команда не управляет Docker NAT и host `FORWARD`.
`apply-limits` находит Amnezia container, заходит в его network namespace, читает peers через `wg`, строит per-peer HTB/fq_codel классы на AWG/WG интерфейсе и IFB ingress path. Если drift не найден, пишет `traffic rules up-to-date, skip apply`.
`status` печатает фактическую диагностику: сервис, контейнер, netns, интерфейс, peers, WireGuard runtime, traffic desired/actual, host firewall drift, Docker/Amnezia port path, anti-torrent counters, swap state, conntrack, recent events и planned reboot.
`daemon` — режим для systemd. На старте делает startup reconcile, затем слушает Docker events и периодически сверяет traffic/swap/firewall. Вручную обычно не запускается, кроме отладки.
`awg2-reboot-scheduler --config /etc/awg2-harness/config.toml` читает только секцию `[reboot]`, пишет план в `/run/awg2-harness/reboot.plan` и ждёт назначенного UTC-времени.
## Что происходит под капотом
Traffic shaping реализован через `tc` в network namespace контейнера Amnezia. На egress используется root qdisc `htb 1:` на AWG/WG интерфейсе, root class `1:1`, per-peer classes `1:<id>` и `fq_codel` под каждым peer class. На ingress ставится `ingress ffff:` на AWG/WG интерфейс, входящие пакеты перенаправляются через `mirred` в `ifb0`, а там строится отдельная HTB topology `2:` с такими же per-peer лимитами.
Peer discovery берётся из WireGuard/AmneziaWG allowed IPs, а не из handshake state. Поэтому offline peer с `allowed ips: 10.8.1.1/32` тоже получает лимит заранее.
Host firewall — отдельная AWG2-owned таблица `inet awg2_host_firewall`. Она открывает loopback, established/related, SSH-порт из конфига, Amnezia-порт по указанным протоколам и optionally ICMP/ICMPv6. Docker-managed `FORWARD` и NAT не трогаются.
Anti-torrent guard — отдельная AWG2-owned таблица `inet awg2_harness` обычно внутри Amnezia netns. Правило ограничивает число новых conntrack flows per source VPN IP. Это не DPI и не распознавание BitTorrent-протокола; это грубый, дешёвый guard против сотен одновременных P2P-соединений.
Swap module намеренно строгий: если `swap.enabled=true`, desired state — один управляемый swapfile. Старые swap entries удаляются из `/etc/fstab`, block-device swap выключается, но block device не удаляется.
## Production-границы ответственности
AWG2 Harness владеет только собственным state:
- `tc` topology на Amnezia interface и configured `ifb_name`;
- nft table `inet awg2_host_firewall` в host namespace;
- nft table `inet awg2_harness` для anti-torrent;
- `/run/awg2-harness/*` runtime state;
- managed swapfile и swap entries, если `swap.enabled=true`.
AWG2 Harness не владеет:
- установкой AmneziaVPN;
- Docker daemon lifecycle;
- Docker NAT/FORWARD chains;
- генерацией client configs/keys;
- политикой пользователя внутри Amnezia app;
- сторонними firewall-таблицами.
## Документация
- [`docs/INSTALLATION.md`](docs/INSTALLATION.md) — установка и preflight.
- [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md) — параметры `config.toml`.
- [`docs/ACCEPTANCE_CHECKLIST.md`](docs/ACCEPTANCE_CHECKLIST.md) — production checklist.
- [`docs/ANTI_TORRENT.md`](docs/ANTI_TORRENT.md) — простая анти-торрент защита через nftables `ct count`.
- [`docs/ANTI_TORRENT.md`](docs/ANTI_TORRENT.md) — connection-count guard.
- [`tools/README.md`](tools/README.md) — сборка и упаковка.
- [`reboot_module/README.md`](reboot_module/README.md) — внешний reboot scheduler.