diff --git a/docs/ACCEPTANCE_CHECKLIST.md b/docs/ACCEPTANCE_CHECKLIST.md index ab0cce6..187100b 100644 --- a/docs/ACCEPTANCE_CHECKLIST.md +++ b/docs/ACCEPTANCE_CHECKLIST.md @@ -1,5 +1,7 @@ # Чек-лист приёмки перед production-выкладкой +Исторический synthetic validation для версии 0.3.1 зафиксирован в [`VALIDATION_0.3.1.md`](VALIDATION_0.3.1.md). Он подтверждает перечисленные там проверки, но не заменяет этот production acceptance checklist. + ## A. Базовая установка - [ ] Хост — Debian 13 amd64. diff --git a/docs/README.md b/docs/README.md index cf1c8d5..6481871 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,8 +7,9 @@ 3. [`INSTALLATION.md`](INSTALLATION.md) — сжатый порядок установки на production. 4. [`CONFIGURATION.md`](CONFIGURATION.md) — параметры `/etc/awg2-harness/config.toml`. 5. [`ACCEPTANCE_CHECKLIST.md`](ACCEPTANCE_CHECKLIST.md) — чек-лист приёмки перед статусом production-ready. -6. [`ANTI_TORRENT.md`](ANTI_TORRENT.md) — ограничение числа соединений через nftables. -7. [`IMPLEMENTATION_SPEC.md`](IMPLEMENTATION_SPEC.md) — архитектура и reconciliation состояния. +6. [`VALIDATION_0.3.1.md`](VALIDATION_0.3.1.md) — фактически выполненные Debian 13 build/package/runtime-проверки версии 0.3.1 и их ограничения. +7. [`ANTI_TORRENT.md`](ANTI_TORRENT.md) — ограничение числа соединений через nftables. +8. [`IMPLEMENTATION_SPEC.md`](IMPLEMENTATION_SPEC.md) — архитектура и reconciliation состояния. Отдельно: diff --git a/docs/VALIDATION_0.3.1.md b/docs/VALIDATION_0.3.1.md new file mode 100644 index 0000000..9282fdf --- /dev/null +++ b/docs/VALIDATION_0.3.1.md @@ -0,0 +1,417 @@ +# AWG2 Harness 0.3.1 — отчёт о валидации + +Дата проверки: 2026-08-28 + +## 1. Назначение + +Этот документ фиксирует фактически выполненные build-, package- и runtime-проверки AWG2 Harness 0.3.1 по сохранённым локальным логам оператора. + +Он не заменяет [`ACCEPTANCE_CHECKLIST.md`](ACCEPTANCE_CHECKLIST.md). Его задача — сохранить доказанный на практике baseline: + +- какая ревизия проверялась; +- в каком окружении выполнялись проверки; +- какие свойства подтверждены реальным Linux runtime; +- какие проверки были negative/fail-closed; +- какие сценарии остаются до production acceptance. + +Сырой terminal dump намеренно не включён в репозиторий: в нём присутствуют public keys, container IDs и инфраструктурные детали. Ниже сохранены воспроизводимые команды, существенные наблюдения и границы результата. + +## 2. Проверенные ревизии + +Полный synthetic runtime-прогон выполнен для базовой ревизии: + +`7390d1fa2c0710cb189897728e1bc1bfed9fbc53` + +Финальная ревизия с изменениями production-кода, прошедшая повторный Linux build/package gate: + +`37573022c2ca56480e99bf7515dca1c68254690e` + +Версия пакета: + +`awg2-harness 0.3.1` + +Production delta между этими ревизиями затронул только `traffic_core/src/swap/runtime.rs`: вызов `swapon` переведён на документированную форму `--show=NAME,TYPE,SIZE,PRIO`. Изменение отдельно закрыто regression-тестом и smoke-тестом на реальном util-linux 2.41.5. + +Таким образом, разделы с полным synthetic runtime относятся к `7390d1f`, а build/package и `swapon` runtime-проверки после этого исправления — к `3757302`. + +## 3. Окружение + +Build/runtime host: + +- Debian 13 amd64; +- Linux `6.12.101+deb13-amd64`; +- Rust `1.95.0`; +- Cargo `1.95.0`; +- ShellCheck `0.10.0`; +- util-linux `2.41.5`. + +Synthetic runtime выполнялся на существующем контейнере AmneziaWG 2.0. + +Контейнер использовался как локальный runtime fixture. Внешний VPN dataplane на сервере не являлся рабочим из-за известной внешней конфигурационной проблемы, не связанной с AWG2 Harness. Поэтому этот test run не является end-to-end acceptance AWG 3.1. + +## 4. Build gate и упаковка + +Для ревизии `3757302` команда `tools/build.sh --package` успешно выполнила обязательные gates: + +- `cargo fmt --check`; +- `cargo clippy --locked --workspace --all-targets -- -D warnings`; +- release tests; +- release build; +- Cargo locked metadata validation; +- ShellCheck; +- проверку release-бинарников; +- Debian package assembly; +- package metadata/content checks; +- создание SHA256 manifest. + +Linux release tests: + +- `awg2-harness`: 251 passed; +- `awg2-reboot-scheduler`: 4 passed; +- failures: 0. + +Собран пакет: + +`awg2-harness_0.3.1_amd64.deb` + +Подтверждено: + +- package metadata: версия `0.3.1`, архитектура `amd64`; +- оба binary устанавливаются и выполняют `--help`; +- systemd units проходят `systemd-analyze verify`; +- production `config.toml` не создаётся автоматически; +- после clean package state оба сервиса остаются disabled/inactive до явного включения оператором; +- `dist/SHA256SUMS` успешно проверяет пакет на builder. + +Runtime config создаётся только вручную из `config.example.toml`. + +## 5. Целостность перенесённого артефакта + +Для пакета, собранного из `3757302` до добавления этого отчёта, Windows вычислил SHA-256: + +`9ffcc8f84e01ddf5b365d37d99c3efdbe0a130aa2017ba63476697bdaa216c4c` + +Это значение полностью совпало с записью в созданном на builder файле `SHA256SUMS`. Регистр шестнадцатеричных символов на значение digest не влияет. + +Результат: перенос `.deb` через `scp` не повредил артефакт. + +Документы `docs/*.md` входят в `.deb`, поэтому package, пересобранный после добавления этого отчёта, закономерно получит другой SHA-256. Его checksum должен проверяться по новому `SHA256SUMS`; изменение package hash само по себе не означает изменение бинарников. + +## 6. Реальный smoke-тест `swapon` + +На Debian 13 с util-linux 2.41.5 выполнен новый документированный вызов: + +```bash +swapon --show=NAME,TYPE,SIZE,PRIO --bytes --raw --noheadings +``` + +Он успешно вернул ожидаемые четыре поля. После переустановки пакета команда `status` также успешно прочитала active swap, а foreign-swap fail-closed сохранился. + +Результат: PASS. + +Исправление проверено не только unit/regression-тестом, но и реальным Debian runtime. + +Для расширенной ручной диагностики использовалась форма: + +```bash +swapon --show=NAME,TYPE,SIZE,USED,PRIO,UUID +``` + +## 7. AWG discovery и topology parser + +На реальном AmneziaWG 2.0 runtime успешно обнаружены: + +- container; +- network namespace; +- interface `awg0`; +- 28 server-side peers. + +Все server peer routes были корректно распознаны как IPv4 `/32`. + +Это подтверждает работоспособность production parser не только на unit fixtures, но и на реальном выводе AWG. Поддерживаемый harness topology остаётся IPv4-only `/32`. + +Результат: PASS. + +## 8. Legacy traffic topology — fail-closed ownership + +Перед применением 0.3.1 в network namespace существовала topology старой версии: + +- root HTB на `awg0`; +- `ifb0`; +- per-peer HTB classes; +- `fq_codel`; +- legacy per-peer `u32` filters; +- peer limit 60 Mbit/s. + +Legacy `ifb0` не имел ownership marker: + +`alias awg2-harness:traffic` + +Команда `apply-limits` отказалась удалять такой IFB с ошибкой `refusing to delete unowned IFB`. + +Результат: PASS. + +Это подтверждает safety invariant: существования интерфейса с подходящими именем и типом недостаточно для destructive cleanup; требуется explicit ownership marker. + +## 9. Traffic topology 0.3.1 + +После ручного удаления legacy topology `apply-limits` успешно создал новую topology. Повторный `apply-limits` завершился без ошибки и не изменил healthy state. + +Подтверждено: + +- `ifb0` создан с alias `awg2-harness:traffic`; +- egress root: HTB; +- ingress redirect присутствует; +- IFB root: HTB; +- root rate: 500 Mbit/s; +- peer rate: 50 Mbit/s; +- peer ceil: 50 Mbit/s; +- `rate == ceil` для всех peers; +- leaf qdisc: `fq_codel`; +- egress per-peer filters: `flower pref 10`, `dst_ip /32`; +- ingress per-peer filters: `flower pref 10`, `src_ip /32`. + +После reconciliation `status` сообщил `Traffic actual: drift: no` для всех 28 peers. + +Результат: PASS. + +## 10. Ingress redirect + +На ingress `awg0` реально присутствовал единственный catch-all `u32` filter с действием: + +`mirred Egress Redirect to device ifb0` + +Таким образом, design contract сохранён: + +- `u32` используется только для ingress redirect; +- per-peer classification выполняется через `flower`. + +Результат: PASS. + +## 11. Peer traffic counters + +Для peer `10.8.1.2` использовался HTB class `1:21`. + +Перед дополнительной генерацией трафика: + +- 20 packets; +- 1680 bytes. + +После ещё 20 ICMP packets: + +- 40 packets; +- 3360 bytes. + +`status` также показал для `10.8.1.2` значение `Egress packets = 40`. + +Результат: PASS. + +Это подтверждает, что peer counters читаются из фактических HTB class statistics. Отсутствие ICMP reply не является ошибкой harness: внешний VPN dataplane тестового AWG был нерабочим. + +## 12. Host firewall + +IPv4 и IPv6 проверены отдельно. + +В обоих случаях harness установил единственный owned jump: + +`awg2-harness:host-firewall -> AWG2H_A` + +Managed chain содержит: + +- loopback ACCEPT; +- RELATED/ESTABLISHED ACCEPT; +- TCP/2323 ACCEPT; +- ICMP или IPv6-ICMP ACCEPT; +- terminal DROP. + +Built-in INPUT policy при этом остаётся `ACCEPT`. + +Это ожидаемая архитектура: политика `input_policy = "drop"` реализуется terminal DROP внутри owned managed chain, а не изменением глобальной policy чужой INPUT chain. + +После применения `Host firewall: drift: no`. + +Результат: PASS. + +## 13. Docker / AWG UDP port contract + +При `expected_host_udp_port = 2053` runtime сообщил: + +- Docker published UDP: 2053; +- AWG listen UDP: 2053; +- host IPv4 binding: `0.0.0.0:2053`; +- host IPv6 binding: `[::]:2053`; +- `port_contract: ok`. + +Наличие host IPv6 Docker publication не означает поддержку IPv6 server-peer topology внутри AWG tunnel. + +Результат: PASS. + +## 14. Anti-torrent guard + +Anti-torrent guard был применён повторно без ошибки. + +В Amnezia network namespace подтверждены: + +- harness-owned nftables table; +- IPv4 dynamic set; +- peer CIDR `10.8.1.0/24`; +- `ct state new`; +- threshold `ct count over 500`; +- named drop counter. + +После применения `Anti-torrent: drift: no`. + +Conntrack count читается из Amnezia network namespace, global max — с host. + +Результат: PASS. + +## 15. Network tuning и BBR-off policy + +До reconcile присутствовал drift от старого persisted state. После `apply-network-tuning` получено: + +- persisted sysctl matches desired state; +- obsolete harness BBR modules-load file отсутствует; +- `rmem_max = 67108864`; +- `wmem_max = 67108864`; +- `ip_forward = 1`; +- `drift: no`. + +Тестовый host уже имел runtime `net.ipv4.tcp_congestion_control = bbr` при конфигурации `tcp_bbr = "off"`. + +После reconcile runtime BBR остался активным, одновременно status сообщил: + +- `bbr_desired: no`; +- `bbr_runtime_active: yes`; +- `drift: no`. + +Это ожидаемый policy contract. `off` означает, что harness не требует и не персистит BBR. Он не выполняет принудительное переключение существующего host TCP congestion control на другой алгоритм. + +Результат: PASS. + +## 16. IFB IPv6 observation + +После создания IFB kernel автоматически назначил link-local IPv6 `fe80::/64` и соответствующий link-local route. На IFB наблюдалось несколько direct packets. + +На peer IPv4 path признаков обхода HTB не обнаружено: egress peer class counters корректно увеличивались при IPv4 traffic. + +Эта проверка не заменяет production throughput test. + +## 17. Swap — foreign ownership safety + +На test host уже существовал `/swapfile` и запись в `/etc/fstab`, но ownership record новой версии harness отсутствовал. + +`ensure-swap` дважды отказался изменять файл: + +`/swapfile already exists and there is no record of awg2-harness creating it; refusing to touch it` + +Результат: PASS для fail-closed ownership. + +Подтверждено, что harness не усыновляет и не изменяет произвольный существующий swapfile только по совпадению path. После исправления argv `swapon` эта защита повторно проверена на ревизии `3757302` и сохранилась. + +Положительный сценарий создания нового owned swapfile, resize/replacement и rollback в этом test run не выполнялся. Он остаётся частью clean-host production acceptance. + +## 18. Reboot scheduler + +`awg2-reboot-scheduler.service` успешно: + +- стартовал; +- прочитал production-format config; +- выбрал случайный reboot timestamp внутри окна `01:00–02:00 UTC`; +- оставался active; +- штатно остановился по `systemctl stop`. + +Результат: PASS. + +Фактическая перезагрузка в рамках этого synthetic test намеренно не ожидалась. + +## 19. Daemon startup и periodic recovery + +Попытка запуска `awg2-harness.service` завершилась fail-closed на startup swap reconcile, поскольку существующий `/swapfile` не принадлежал текущему harness. + +Это ожидаемая защитная реакция, а не ошибка traffic reconciliation. Из-за завершения daemon последующий intentional deletion root HTB не мог быть автоматически восстановлен. + +Статус: + +`daemon periodic drift recovery: NOT RUN` + +Причина: + +`BLOCKED BY EXPECTED FOREIGN-SWAP OWNERSHIP FAILURE` + +Для отдельного daemon recovery test необходимо использовать clean/owned swap state либо временно отключить `[swap]`. + +## 20. Сводный результат + +| Контур | Статус | +| --- | --- | +| Debian 13 build gate | PASS | +| `.deb` packaging | PASS | +| Artifact transfer integrity | PASS | +| Config validation | PASS | +| AWG runtime discovery | PASS | +| AWG 2.0 peer parser | PASS | +| IPv4 `/32` topology | PASS | +| Foreign IFB fail-closed | PASS | +| HTB creation | PASS | +| `rate == ceil` | PASS | +| `fq_codel` leaves | PASS | +| `flower pref 10` | PASS | +| ingress `u32 -> mirred -> IFB` | PASS | +| HTB peer counters | PASS | +| repeated `apply-limits` | PASS | +| IPv4 host firewall | PASS | +| IPv6 host firewall | PASS | +| Docker/AWG UDP contract | PASS | +| Anti-torrent / conntrack | PASS | +| Network tuning | PASS | +| BBR-off policy semantics | PASS | +| Documented `swapon` argv on util-linux 2.41.5 | PASS | +| Foreign swap protection | PASS | +| Reboot scheduler | PASS | +| Daemon periodic self-healing | NOT RUN | +| Owned swap create/resize | NOT RUN | +| Actual 50 Mbit throughput cap | NOT RUN | +| End-to-end VPN | NOT RUN | +| AWG 3.1 production acceptance | NOT RUN | + +## 21. Что этим test run не доказано + +Не проверены: + +- реальная пропускная способность peer под длительной нагрузкой; +- фактический hard cap около 50 Mbit/s на живом VPN client; +- simultaneous multi-peer fairness/latency; +- bidirectional ingress load; +- полноценный AWG handshake; +- end-to-end VPN connectivity; +- production AWG 3.1 runtime; +- positive swap create/resize/replacement path; +- periodic daemon self-healing. + +Эти проверки остаются обязательной частью clean production AWG 3.1 acceptance. + +## 22. Итог + +Для полного synthetic runtime-прогона на `7390d1f` и отдельной финальной build/runtime-проверки swap delta на `3757302` подтверждены: + +- Debian 13 build/package viability; +- целостность передачи собранного артефакта; +- AWG runtime discovery; +- IPv4 peer topology parser; +- ownership fail-closed; +- HTB/fq_codel/flower traffic topology; +- ingress IFB redirect; +- peer HTB counters; +- IPv4/IPv6 host firewall; +- Docker/AWG UDP port contract; +- nftables anti-torrent guard; +- network tuning reconciliation; +- reboot scheduler; +- документированный `swapon` argv на реальном util-linux 2.41.5; +- foreign swap protection после исправления. + +**Synthetic runtime validation: PASS for tested components.** + +**Production AWG 3.1 acceptance: NOT RUN.** + +Окончательный production GO требует выполнения [`ACCEPTANCE_CHECKLIST.md`](ACCEPTANCE_CHECKLIST.md) на clean Debian 13 host с рабочим AWG 3.1 и реальным client traffic.