# Acceptance checklist перед production-выкладкой ## A. Базовая установка - [ ] Хост — Debian 13 amd64. - [ ] Сервер выделенный, без соседних сервисов. - [ ] AmneziaWG/AWG 2.0 установлена официальным способом. - [ ] Контейнер `amnezia-awg2` или `amnezia-awg` запущен. - [ ] `docker ps` видит контейнер. - [ ] Можно получить PID контейнера. - [ ] `/proc//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:` с `traffic.peer_limit`. - [ ] Для каждого peer есть `fq_codel` под `1:`. - [ ] Для каждого peer есть egress filter `dst peer_ip -> 1:`. - [ ] Есть 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:` с `traffic.peer_limit`. - [ ] Для каждого peer есть `fq_codel` под `2:`. - [ ] Для каждого peer есть ingress filter `src peer_ip -> 2:`. - [ ] 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. - [ ] Если удалить ingress redirect filter (`parent ffff:`), drift детектируется и topology восстанавливается. - [ ] Если удалить `ifb0`, daemon восстанавливает topology. - [ ] Если сделать `ifb0` DOWN, drift детектируется и topology восстанавливается. - [ ] Если удалить один peer filter, daemon восстанавливает topology. - [ ] Если изменить `rate/ceil` peer class, drift детектируется и topology восстанавливается. - [ ] Если добавить stale peer class/filter, drift детектируется и 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` показывает WireGuard clients (online/offline, latest handshake, RX/TX, endpoint). - [ ] `status` показывает missing/stale peer rules. - [ ] `status` явно показывает partial/drift state. - [ ] `status` показывает tc filter counters по peer или `filter statistics: unavailable`. - [ ] Логи доступны через `journalctl -u awg2-harness`. - [ ] Ошибка отсутствия `tc`/`ip`/`wg` логируется явно. - [ ] Последний reconcile result виден в status или journal. - [ ] `status` показывает последние события reconcile/firewall из `/run/awg2-harness/events.log`. ## 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`. - [ ] Если до запуска был активен другой swap, после `ensure-swap` он выключен. - [ ] После `ensure-swap` активен ровно один swap: `/swapfile`. - [ ] Все старые swap entries удалены из `/etc/fstab`. - [ ] Для block-device swap выполняется только `swapoff` и удаление `fstab` entry; block device не удаляется. - [ ] `vm.swappiness = 1` применён. - [ ] Повторный `ensure-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 `, default `10.8.1.0/24`. - [ ] Лимит считается per source VPN IP через meter/set, а не глобально по всей подсети. - [ ] Порог совпадает с `anti_torrent.max_connections_per_peer`, default `100`. - [ ] Drop counter виден в `status`. - [ ] На first-run без table `awg2-harness apply-firewall` создаёт `table inet awg2_harness`, а не падает. - [ ] При превышении порога новые 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 проверен на тестовом сервере. ## K. Packaging / release - [ ] Package собирается на отдельной Debian 13 amd64 build-machine. - [ ] Production host не требует Rust/cargo/source tree. - [ ] `./tools/build.sh --package` создаёт `dist/awg2-harness__amd64.deb`. - [ ] `dist/SHA256SUMS` валидирует checksum пакета. - [ ] Пакет ставит бинарь в `/usr/sbin/awg2-harness`. - [ ] Пакет ставит unit в `/lib/systemd/system/awg2-harness.service`. - [ ] Пакет не устанавливает `/etc/awg2-harness/config.toml` автоматически. - [ ] Пакет не включает/не стартует сервис автоматически. - [ ] Пакет не имеет `Depends`/`Recommends` на `docker.io`; установка Amnezia/Docker внешняя. - [ ] `apt install ./awg2-harness__amd64.deb` не устанавливает и не заменяет Docker. - [ ] `apt install ./awg2-harness__amd64.deb` проходит на clean Debian 13 Amnezia host. - [ ] После ручной установки config `awg2-harness validate-config` проходит. - [ ] Сервис стартует только после явного `systemctl enable --now awg2-harness.service`.