771 lines
29 KiB
Markdown
771 lines
29 KiB
Markdown
# AWG2 Harness 0.3.1 — отчёт о валидации
|
||
|
||
Даты проверок:
|
||
|
||
- 2026-08-28 — synthetic runtime validation;
|
||
- 2026-08-29 — clean production acceptance.
|
||
|
||
## 1. Назначение
|
||
|
||
Этот документ фиксирует фактически выполненные build-, package- и runtime-проверки AWG2 Harness 0.3.1 по сохранённым локальным логам оператора.
|
||
|
||
Он не заменяет [`ACCEPTANCE_CHECKLIST.md`](ACCEPTANCE_CHECKLIST.md). Его задача — сохранить доказанный на практике baseline:
|
||
|
||
- какая ревизия проверялась;
|
||
- в каком окружении выполнялись проверки;
|
||
- какие свойства подтверждены реальным Linux runtime;
|
||
- какие проверки были negative/fail-closed;
|
||
- какие сценарии оставались после synthetic-прогона;
|
||
- какие из них затем закрыты clean 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 <peer>/32`;
|
||
- ingress per-peer filters: `flower pref 10`, `src_ip <peer>/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. Сводный результат synthetic run
|
||
|
||
| Контур | Статус |
|
||
| --- | --- |
|
||
| 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. Что не доказал synthetic 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 run
|
||
|
||
Для полного 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 в этом synthetic run: NOT RUN.**
|
||
|
||
Окончательный production GO требует выполнения [`ACCEPTANCE_CHECKLIST.md`](ACCEPTANCE_CHECKLIST.md) на clean Debian 13 host с рабочим AWG 3.1 и реальным client traffic.
|
||
|
||
Требуемый clean production acceptance был выполнен 2026-08-29; результаты приведены в следующем разделе.
|
||
|
||
## 23. Clean production acceptance — Debian 13 / AmneziaWG 3.1
|
||
|
||
Дата проверки: 2026-08-29
|
||
|
||
Проверенный release artifact:
|
||
|
||
- tag: `v0.3.1`;
|
||
- package: `awg2-harness_0.3.1_amd64.deb`;
|
||
- SHA256: `90dc8bb85f60740ca3358e40c31592b5165e250f878169ad6d05bdee3656f1b8`.
|
||
|
||
Проверка выполнена после публикации релиза. Этот раздел фиксирует post-release evidence и не изменяет tag или опубликованный artifact `v0.3.1`.
|
||
|
||
### 23.1. Окружение
|
||
|
||
- Debian GNU/Linux 13.6 (trixie);
|
||
- amd64;
|
||
- kernel: `6.12.105+deb13-amd64`;
|
||
- Docker: `26.1.5+dfsg1`;
|
||
- container: `amnezia-awg2`;
|
||
- AmneziaWG tools: `v3.1.20260812`;
|
||
- AWG interface: `awg0`;
|
||
- AWG peer CIDR: `10.8.1.0/24`;
|
||
- server peer `AllowedIPs`: IPv4 `/32`;
|
||
- host UDP port: `2053`;
|
||
- SSH port: `2323`.
|
||
|
||
Production limits:
|
||
|
||
- per-peer limit: `60mbit`;
|
||
- root rate: `600mbit`;
|
||
- anti-torrent connection limit: `500` per peer.
|
||
|
||
### 23.2. Clean installation
|
||
|
||
До установки AWG2 Harness:
|
||
|
||
- `/etc/awg2-harness` отсутствовал;
|
||
- harness iptables/ip6tables rules отсутствовали;
|
||
- harness nftables table отсутствовала;
|
||
- `ifb0` отсутствовал;
|
||
- swap отсутствовал;
|
||
- harness sysctl persistence files отсутствовали.
|
||
|
||
Установка release package завершилась успешно.
|
||
|
||
После установки:
|
||
|
||
- production config автоматически не создавался;
|
||
- `awg2-harness.service` был `disabled/inactive`;
|
||
- `awg2-reboot-scheduler.service` был `disabled/inactive`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.3. AmneziaWG 3.1 discovery
|
||
|
||
Runtime discovery определил:
|
||
|
||
- container `amnezia-awg2`;
|
||
- interface `awg0`;
|
||
- listen port `2053`;
|
||
- peer `10.8.1.1/32`.
|
||
|
||
`validate-config` и `validate-runtime` завершились успешно.
|
||
|
||
Docker UDP contract:
|
||
|
||
- `0.0.0.0:2053`;
|
||
- `[::]:2053`;
|
||
- inner AWG listen port `2053`;
|
||
- `port_contract: ok`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.4. Swap — clean positive path
|
||
|
||
На исходном сервере active swap отсутствовал.
|
||
|
||
`ensure-swap` создал managed `/swapfile`:
|
||
|
||
- size: 2048 MiB;
|
||
- mode: `0600`;
|
||
- priority: `1`;
|
||
- `/etc/fstab`: `/swapfile none swap sw,pri=1 0 0`;
|
||
- `vm.swappiness = 1`;
|
||
- ownership record создан;
|
||
- `status`: `drift: no`.
|
||
|
||
Повторный `ensure-swap` сохранил тот же inode, размер и содержимое `/etc/fstab`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.5. Network tuning
|
||
|
||
До reconciliation:
|
||
|
||
- `net.core.rmem_max = 212992`;
|
||
- `net.core.wmem_max = 212992`;
|
||
- `net.ipv4.ip_forward = 1`;
|
||
- congestion control: `cubic`.
|
||
|
||
После `apply-network-tuning`:
|
||
|
||
- `net.core.rmem_max = 67108864`;
|
||
- `net.core.wmem_max = 67108864`;
|
||
- `net.ipv4.ip_forward = 1`;
|
||
- persisted config совпадает с desired state;
|
||
- `tcp_bbr = "off"` не включает и не персистит BBR;
|
||
- runtime congestion control остался `cubic`;
|
||
- `drift: no`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.6. Traffic shaping — clean AWG 3.1
|
||
|
||
На исходном `awg0` присутствовал только штатный root `fq_codel`. `ifb0`, HTB classes и peer filters отсутствовали.
|
||
|
||
Первый `apply-limits` построил managed topology. Повторный `apply-limits` был идемпотентным.
|
||
|
||
Получено:
|
||
|
||
- egress HTB root: `600Mbit`;
|
||
- ingress IFB HTB root: `600Mbit`;
|
||
- peer `10.8.1.1`:
|
||
- egress: `60Mbit/60Mbit`;
|
||
- ingress: `60Mbit/60Mbit`;
|
||
- per-peer egress filter: `flower pref 10`, destination peer IPv4;
|
||
- per-peer ingress filter: `flower pref 10`, source peer IPv4;
|
||
- AWG ingress redirect: `u32 -> mirred -> ifb0`;
|
||
- IFB alias: `awg2-harness:traffic`;
|
||
- leaf qdisc: `fq_codel`;
|
||
- `status`: `drift: no`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.7. Live peer and traffic classification
|
||
|
||
После подключения реального AmneziaWG 3.1 клиента:
|
||
|
||
- peer определён как `online`;
|
||
- endpoint определён;
|
||
- latest handshake корректно читается;
|
||
- AWG RX/TX корректно читаются;
|
||
- egress class counter растёт;
|
||
- ingress class counter растёт.
|
||
|
||
`direct_packets_stat` не рос пропорционально пользовательской нагрузке; IPv4 peer traffic проходил через managed peer classes.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.8. Real bandwidth enforcement
|
||
|
||
Speedtest через реального клиента:
|
||
|
||
- configured peer ceiling: `60 Mbit/s`;
|
||
- download: `57.81 Mbit/s`;
|
||
- upload: `55.26 Mbit/s`.
|
||
|
||
Во время нагрузки:
|
||
|
||
- egress и ingress peer counters существенно выросли;
|
||
- HTB `overlimits` существенно выросли;
|
||
- topology сохранила `drift: no`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.9. Host firewall
|
||
|
||
Применены managed IPv4 и IPv6 INPUT chains.
|
||
|
||
Разрешены:
|
||
|
||
- loopback;
|
||
- ESTABLISHED/RELATED;
|
||
- TCP `2323`;
|
||
- ICMP / IPv6 ICMP.
|
||
|
||
Завершающее действие owned chain — DROP. Built-in INPUT policy не изменялась. Docker-managed FORWARD rules harness не изменял.
|
||
|
||
После применения подтверждены:
|
||
|
||
- новая SSH-сессия на TCP/2323;
|
||
- новый AWG handshake;
|
||
- работающий VPN dataplane;
|
||
- `Host firewall: drift: no`;
|
||
- `port_contract: ok`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.10. Anti-torrent guard
|
||
|
||
В AWG network namespace создана `table inet awg2_harness`.
|
||
|
||
Production rule использует:
|
||
|
||
- peer CIDR `10.8.1.0/24`;
|
||
- `ct state new`;
|
||
- dynamic IPv4 set;
|
||
- `ct count over 500`;
|
||
- named drop counter;
|
||
- terminal drop.
|
||
|
||
Inspector:
|
||
|
||
- enabled: true;
|
||
- max_connections_per_peer: 500;
|
||
- `drift: no`.
|
||
|
||
Threshold load-test выше 500 соединений на production намеренно не выполнялся.
|
||
|
||
Результат: PASS для control-plane и production configuration.
|
||
|
||
### 23.11. Daemon startup and self-healing
|
||
|
||
После включения `awg2-harness.service`:
|
||
|
||
- service: active;
|
||
- startup network tuning reconcile: ok;
|
||
- startup swap reconcile: ok;
|
||
- startup traffic reconcile: ok;
|
||
- startup firewall reconcile: ok.
|
||
|
||
Для проверки periodic self-healing вручную удалён root HTB qdisc на `awg0`.
|
||
|
||
Сразу после удаления inspector зарегистрировал:
|
||
|
||
`missing egress root htb qdisc`
|
||
|
||
Через следующий daemon reconciliation root HTB был автоматически восстановлен, peer classes и filters появились снова, `Traffic actual: drift: no`.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.12. Reboot scheduler
|
||
|
||
`awg2-reboot-scheduler.service`:
|
||
|
||
- enabled;
|
||
- active;
|
||
- планирует reboot внутри configured UTC window `01:00–02:00`.
|
||
|
||
Пример после production reboot:
|
||
|
||
`planned_reboot_utc=2026-08-30T01:00:38Z`
|
||
|
||
Фактического ожидания scheduler-triggered reboot в этом validation run не выполнялось.
|
||
|
||
Результат: PASS для startup/planning.
|
||
|
||
### 23.13. Reboot recovery
|
||
|
||
Выполнен ручной reboot production host.
|
||
|
||
После загрузки:
|
||
|
||
- Docker active;
|
||
- `amnezia-awg2` active;
|
||
- `awg2-harness.service` active;
|
||
- `awg2-reboot-scheduler.service` active;
|
||
- swap active с тем же UUID, размером 2 GiB и priority 1;
|
||
- network tuning: `drift: no`;
|
||
- traffic topology: `drift: no`;
|
||
- host firewall: `drift: no`;
|
||
- anti-torrent: `drift: no`;
|
||
- Docker/AWG UDP contract: `ok`.
|
||
|
||
AWG client автоматически выполнил новый handshake после reboot, peer определён как `online`, traffic counters росли.
|
||
|
||
Результат: PASS.
|
||
|
||
### 23.14. Наблюдение за Docker FORWARD state
|
||
|
||
До reboot `status` показывал `host_forward_policy_drop: true`, после reboot — `false`. При этом `docker_forward_rules_present: true`, VPN dataplane после reboot работал, а `port_contract` оставался `ok`.
|
||
|
||
Harness по контракту не управляет built-in FORWARD policy и Docker-managed FORWARD rules. Наблюдаемое изменение host state после reboot не классифицируется как drift или regression AWG2 Harness.
|
||
|
||
### 23.15. Сводный результат clean production acceptance
|
||
|
||
| Контур | Статус |
|
||
| --- | --- |
|
||
| Debian 13 clean host | PASS |
|
||
| Release `.deb` + SHA256 | PASS |
|
||
| Clean package installation | PASS |
|
||
| AWG 3.1 discovery | PASS |
|
||
| AWG 3.1 real dataplane | PASS |
|
||
| UDP 2053 contract | PASS |
|
||
| Owned swap creation | PASS |
|
||
| Swap persistence / idempotence | PASS |
|
||
| Network tuning | PASS |
|
||
| HTB 600 Mbit root | PASS |
|
||
| 60 Mbit per-peer egress | PASS |
|
||
| 60 Mbit per-peer ingress | PASS |
|
||
| Flower classification | PASS |
|
||
| IFB ingress | PASS |
|
||
| `fq_codel` | PASS |
|
||
| No material direct bypass | PASS |
|
||
| Real download: 57.81 Mbps | PASS |
|
||
| Real upload: 55.26 Mbps | PASS |
|
||
| IPv4 host firewall | PASS |
|
||
| IPv6 host firewall | PASS |
|
||
| New SSH through firewall | PASS |
|
||
| New AWG handshake through firewall | PASS |
|
||
| Docker forwarding preserved | PASS |
|
||
| Anti-torrent / `ct count over 500` configuration | PASS |
|
||
| Anti-torrent inspection | PASS |
|
||
| Daemon startup | PASS |
|
||
| Startup reconciliation | PASS |
|
||
| Periodic self-healing | PASS |
|
||
| Manual drift recovery | PASS |
|
||
| Service enablement | PASS |
|
||
| Manual reboot recovery | PASS |
|
||
| Post-reboot AWG handshake | PASS |
|
||
| Post-reboot traffic shaping | PASS |
|
||
| Post-reboot firewall | PASS |
|
||
| Post-reboot anti-torrent | PASS |
|
||
| Post-reboot swap | PASS |
|
||
| Post-reboot network tuning | PASS |
|
||
| Reboot scheduler startup/planning | PASS |
|
||
| Actual scheduled-time reboot | NOT OBSERVED |
|
||
| Multi-peer production load | NOT RE-RUN |
|
||
|
||
Production-critical функции, проверенные на реальном сервере:
|
||
|
||
- package installation;
|
||
- runtime discovery;
|
||
- swap lifecycle;
|
||
- network tuning;
|
||
- per-peer bidirectional shaping;
|
||
- real 60 Mbit/s enforcement;
|
||
- host firewall;
|
||
- Docker/AWG port contract;
|
||
- anti-torrent control-plane;
|
||
- daemon startup reconciliation;
|
||
- periodic drift self-healing;
|
||
- reboot persistence and recovery;
|
||
- reboot scheduler startup/planning.
|
||
|
||
Ограничения текущего validation:
|
||
|
||
- scheduler-triggered reboot не ожидался до фактического времени;
|
||
- multi-peer production load-test отдельно не повторялся;
|
||
- anti-torrent threshold выше 500 не создавался искусственно.
|
||
|
||
Эти ограничения зафиксированы как непроверенные сценарии и не признаны blockers для перевода пользователей на сервер: запуск и планирование scheduler проверены, обычный reboot и полное восстановление системы проверены отдельно.
|
||
|
||
**Clean production acceptance: PASS.**
|
||
|
||
**Production GO: подтверждён.**
|