Files
AWG2_harness/docs/ACCEPTANCE_CHECKLIST.md
T

175 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Acceptance checklist перед production-выкладкой
## A. Базовая установка
- [ ] Хост — Debian 13 amd64.
- [ ] Сервер выделенный, без соседних сервисов.
- [ ] AmneziaWG/AWG 2.0 или AmneziaWG/AWG 1.5 установлена официальным способом.
- [ ] Контейнер `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 contract
- [ ] `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 использует стабильный лексикографический контракт: 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 <peer_ipv4_cidr>`, default `10.8.1.0/24`.
- [ ] Лимит считается per source VPN IP через meter/set, а не глобально по всей подсети.
- [ ] Порог совпадает с `anti_torrent.max_connections_per_peer`; текущий пример config использует `150`, более строгий вариант — `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
- [ ] Известно, как остановить и отключить `awg2-harness.service` и `awg2-reboot-scheduler.service`.
- [ ] Известно, как удалить только AWG2-owned host firewall state (`inet awg2_host_firewall`).
- [ ] Известно, как удалить только AWG2-owned anti-torrent state (`inet awg2_harness`) в выбранном namespace.
- [ ] Известно, как удалить AWG2-owned `tc` topology (`root`, `ingress`, `ifb0`) в netns Amnezia.
- [ ] При необходимости rollback включает удаление пакета `awg2-harness` после остановки сервисов.
- [ ] Rollback проверен на тестовом сервере.
## K. Packaging / release
- [ ] Package собирается на отдельной Debian 13 amd64 build-machine.
- [ ] Production host не требует Rust/cargo/source tree.
- [ ] `./tools/build.sh --package` создаёт `dist/awg2-harness_<version>_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_<version>_amd64.deb` не устанавливает и не заменяет Docker.
- [ ] `apt install ./awg2-harness_<version>_amd64.deb` проходит на clean Debian 13 Amnezia host.
- [ ] После ручной установки config `awg2-harness validate-config` проходит.
- [ ] Сервис стартует только после явного `systemctl enable --now awg2-harness.service`.
## K2. Documentation / install UX
- [ ] README не утверждает, что AmneziaVPN ожидает public key.
- [ ] Документация явно разделяет private key для AmneziaVPN и public key для `authorized_keys`.
- [ ] Есть пример заполнения AmneziaVPN fields: `Server IP address [:port]`, `SSH Username`, `Password or SSH private key`.
- [ ] Описаны Windows пути `%USERPROFILE%\.ssh\amnezia_key` и `%USERPROFILE%\.ssh\amnezia_key.pub`.
- [ ] Описан runtime config bootstrap из `/usr/share/doc/awg2-harness/repo/config.example.toml`.
- [ ] Clean install подтверждает, что `/etc/awg2-harness/config.toml` не создаётся автоматически.
## K3. AmneziaDNS
- [ ] AmneziaDNS установлен из приложения AmneziaVPN.
- [ ] Контейнер `amnezia-dns` в состоянии healthy.
- [ ] `docker inspect amnezia-dns` показывает `"53/tcp":null` и `"53/udp":null`.
- [ ] `amnezia-dns` и `amnezia-awg2` подключены к одной Amnezia DNS Docker-сети.
- [ ] Публичный DNS-запрос к `<server-public-ip>:53` не получает ответа.
- [ ] DNS-резолвинг у VPN-клиента работает при активном подключении.
- [ ] AWG2 host firewall не открывает `53/tcp` и `53/udp`.
## L. Host firewall (ports/SSH safety)
- [ ] `awg2-harness validate-config` падает, если отсутствует секция `[host_firewall]`.
- [ ] `apply-firewall` не блокирует SSH на `host_firewall.ssh_port` (default `2323`).
- [ ] После `apply-firewall` порт `host_firewall.amnezia_port` (default `443`) доступен по всем протоколам из `host_firewall.amnezia_protocols`.
- [ ] Если эксплуатационно требуется закрыть legacy SSH `22`, это явно подтверждено проверкой доступа на `ssh_port` перед закрытием `22`.
- [ ] При `host_firewall.enabled = false` удаляется только таблица `inet awg2_host_firewall`.
- [ ] Повторный `apply-firewall` идемпотентен и не вносит лишних изменений в healthy state.
- [ ] При невалидном `host_firewall.amnezia_protocols` (`icmp`, `foo`, дубли) `validate-config` завершается ошибкой.
- [ ] После `docker restart` anti-torrent правила продолжают восстанавливаться корректно и не ломаются host firewall логикой.
- [ ] Проверено, где реально проходит VPN-трафик в вашей Docker-схеме (`INPUT` vs `FORWARD`) и это отражено в эксплуатационной заметке.
- [ ] Для release-кандидата зафиксирован итог проверки `INPUT` vs `FORWARD` на реальном сервере (команды/логи/вывод) и подтверждено, что текущие правила firewall покрывают реальный путь трафика.
- [ ] `status` показывает блок `Docker/Amnezia port path` с полями `docker_userland_proxy`, `published_udp_port` и note про Docker-managed `FORWARD`.
- [ ] `status` показывает поля `host_forward_policy_drop` и `docker_forward_rules_present` в блоке `Docker/Amnezia port path`.
- [ ] Если `docker_userland_proxy=false` и `FORWARD policy DROP`, при отсутствии явных Docker accept-признаков `status` выводит warning, но `awg2-harness` не изменяет `FORWARD` автоматически.
- [ ] Сверено соответствие: `config host_firewall.amnezia_port` ↔ опубликованный Docker UDP host port ↔ `docker exec amnezia-awg2 wg show` (`listening port`).
- [ ] При нескольких UDP bindings без однозначного соответствия `host_firewall.amnezia_port` диагностика `published_udp_port` показывает `unknown` (ambiguous), а не произвольный первый порт.
- [ ] Для диагностики FORWARD используется best-effort проверка: `iptables -S FORWARD` и fallback через `nft list ruleset`; `status` не падает при недоступности одной из команд.