feat: bootstrap awg2 harness rust workspace and docs

This commit is contained in:
2026-05-09 02:17:25 +05:00
commit d937f816bf
47 changed files with 4195 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
# Acceptance checklist перед production-выкладкой
## A. Базовая установка
- [ ] Хост — Debian 13 amd64.
- [ ] Сервер выделенный, без соседних сервисов.
- [ ] AmneziaWG/AWG 2.0 установлена официальным способом.
- [ ] Контейнер `amnezia-awg2` или `amnezia-awg` запущен.
- [ ] `docker ps` видит контейнер.
- [ ] Можно получить PID контейнера.
- [ ] `/proc/<pid>/ns/net` существует.
- [ ] На хосте доступны `ip`, `tc`, `wg`, `nsenter`, `modprobe`.
- [ ] `awg2-harness validate-config` проходит без ошибок.
## B. Peer discovery
- [ ] `awg2-harness status` показывает найденный контейнер.
- [ ] `awg2-harness status` показывает PID/netns контейнера.
- [ ] `awg2-harness status` показывает найденный интерфейс.
- [ ] Количество peers совпадает с `wg show all allowed-ips`.
- [ ] Offline peers с allowed IP тоже попадают в список shaping.
- [ ] IP вне `amnezia.peer_ipv4_cidr` не попадают в traffic shaping.
## C. Traffic topology parity with old bash
- [ ] `awg2-harness apply-limits` успешно завершается.
- [ ] На AWG/WG-интерфейсе есть root qdisc `1: htb`.
- [ ] На AWG/WG-интерфейсе есть root class `1:1` с `traffic.root_rate`.
- [ ] Для каждого peer есть class `1:<id>` с `traffic.peer_limit`.
- [ ] Для каждого peer есть `fq_codel` под `1:<id>`.
- [ ] Для каждого peer есть egress filter `dst peer_ip -> 1:<id>`.
- [ ] Есть ingress qdisc `ffff:` на AWG/WG-интерфейсе.
- [ ] Есть ingress redirect через `mirred ... redirect dev ifb0`.
- [ ] `ifb0` существует и находится в UP state.
- [ ] На `ifb0` есть root qdisc `2: htb`.
- [ ] На `ifb0` есть root class `2:1` с `traffic.root_rate`.
- [ ] Для каждого peer есть class `2:<id>` с `traffic.peer_limit`.
- [ ] Для каждого peer есть `fq_codel` под `2:<id>`.
- [ ] Для каждого peer есть ingress filter `src peer_ip -> 2:<id>`.
- [ ] Class ids начинаются с `traffic.class_start_id`, default `10`.
- [ ] Peer IP → class id mapping сохраняет bash parity: IP сортируются лексикографически как строки, не numeric IPv4.
## D. Idempotency and drift recovery
- [ ] Повторный `apply-limits` не создаёт дубли.
- [ ] Повторный `apply-limits` не меняет healthy state без причины.
- [ ] Если удалить root qdisc на AWG/WG-интерфейсе, daemon восстанавливает topology.
- [ ] Если удалить ingress qdisc, daemon восстанавливает topology.
- [ ] Если удалить `ifb0`, daemon восстанавливает topology.
- [ ] Если удалить один peer filter, daemon восстанавливает topology.
- [ ] После удаления peer устаревшие class/filter исчезают через rebuild.
- [ ] После добавления peer появляются новые class/filter.
## E. Auto-healing lifecycle
- [ ] `docker restart amnezia-awg2` → лимиты восстанавливаются автоматически.
- [ ] `systemctl restart docker` → после возвращения контейнера лимиты восстанавливаются автоматически.
- [ ] `systemctl restart awg2-harness` → сервис сам находит контейнер и применяет/проверяет лимиты.
- [ ] Reboot хоста → сервис стартует и восстанавливает лимиты.
- [ ] Изменение имени интерфейса на допустимое fallback-имя не ломает detector.
- [ ] Если контейнер временно отсутствует, daemon логирует ожидание и продолжает работу.
## F. Status and observability
- [ ] `status` показывает desired peer limit из конфига, а не hardcoded 70.
- [ ] `status` показывает egress state.
- [ ] `status` показывает ingress/ifb state.
- [ ] `status` показывает missing/stale peer rules.
- [ ] `status` явно показывает partial/drift state.
- [ ] Логи доступны через `journalctl -u awg2-harness`.
- [ ] Ошибка отсутствия `tc`/`ip`/`wg` логируется явно.
- [ ] Последний reconcile result виден в status или journal.
## G. Reboot scheduler
- [ ] Если `reboot.enabled = false`, сервис не планирует reboot.
- [ ] Если `reboot.enabled = true`, в journal видна запланированная перезагрузка.
- [ ] Время попадает в окно `window_start_utc`–`window_end_utc`.
- [ ] Вызов reboot выполняется через `systemctl --no-block reboot`.
## H. Swap
- [ ] `ensure-swap` создаёт `/swapfile` размером 2048 MB.
- [ ] Права `/swapfile` — `0600`.
- [ ] `swapon --show` показывает активный swap.
- [ ] В `/etc/fstab` нет дублей.
- [ ] `vm.swappiness = 1` применён.
- [ ] Повторный `ensure-swap` не пересоздаёт корректный swap.
- [ ] Несовместимый существующий swap не удаляется молча.
## I. Anti-torrent nftables guard
- [ ] Если `anti_torrent.enabled = true`, в Amnezia netns есть `table inet awg2_harness`.
- [ ] Есть chain `forward_antitorrent` с hook `forward` и policy `accept`.
- [ ] Правило ограничивает `ip saddr <peer_ipv4_cidr>`, default `10.8.1.0/24`.
- [ ] Лимит считается per source VPN IP через meter/set, а не глобально по всей подсети.
- [ ] Порог совпадает с `anti_torrent.max_connections_per_peer`, default `200`.
- [ ] Drop counter виден в `status`.
- [ ] При превышении порога новые flows дропаются, established flows ниже порога не ломаются.
- [ ] После restart контейнера nftables rule пересоздаётся в новом netns.
- [ ] При `anti_torrent.enabled = false` удаляется/отключается только AWG2-owned nftables state; Docker/Amnezia rules не flush-ятся.
- [ ] `status` показывает `nf_conntrack_count` / `nf_conntrack_max`, если sysctl доступны.
## J. Rollback
- [ ] Старые bash-файлы сохранены до завершения миграции.
- [ ] Известно, как остановить `awg2-harness.service`.
- [ ] Известно, как вернуть `amnezia-tc.service` и `amnezia-tc-watcher.service`.
- [ ] Rollback проверен на тестовом сервере.