# 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`. - [ ] `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 `, 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 проверен на тестовом сервере.