fix: harden production topology and runtime state

This commit is contained in:
2026-08-28 15:19:12 +05:00
parent 9dd4732655
commit 0c32c9abbe
26 changed files with 3276 additions and 2815 deletions
+14 -4
View File
@@ -25,9 +25,11 @@
- [ ] `awg2-harness status` показывает PID/netns контейнера.
- [ ] `awg2-harness status` показывает найденный интерфейс.
- [ ] Количество peers совпадает с `docker exec "$C" awg show all allowed-ips`.
- [ ] Peer с несколькими allowed IP даёт отдельный класс на каждый адрес (в этой команде адреса разделены пробелом).
- [ ] Каждый peer имеет ровно один server-side IPv4 `/32` внутри `amnezia.peer_ipv4_cidr`; `(none)` допустим и не создаёт класс.
- [ ] IPv4+IPv6, IPv6-only, несколько allowed IP, маска не `/32` и адрес вне configured CIDR дают явную `unsupported topology` до изменения traffic/anti-torrent state.
- [ ] Одинаковый отказ проверен для `show all allowed-ips`, fallback `show` и `show all dump`; AWG 3.1 interface-row не ошибочно принимается за peer.
- [ ] Offline peers с allowed IP тоже попадают в список shaping.
- [ ] IP вне `amnezia.peer_ipv4_cidr` не попадают в traffic shaping.
- [ ] IP вне `amnezia.peer_ipv4_cidr` не игнорируется молча: весь topology reconcile завершается ошибкой.
- [ ] В секции `WireGuard clients` вывода `status` нет строки, соответствующей самому интерфейсу (столбец `Public key` не содержит приватный ключ устройства).
- [ ] `docker exec "$C" readlink -f /usr/bin/wg` возвращает `/usr/bin/awg`.
@@ -150,7 +152,7 @@
- [ ] `awg2-harness apply-network-tuning` успешно завершается.
- [ ] `awg2-harness inspect-network-tuning` показывает `enabled: true` и `drift: no`.
- [ ] Создаётся `/etc/sysctl.d/90-awg2-harness-vpn.conf`.
- [ ] `/etc/modules-load.d/90-awg2-harness-bbr.conf` создан, если BBR активен, и отсутствует, если BBR неактивен.
- [ ] `/etc/modules-load.d/90-awg2-harness-bbr.conf` создан, если BBR желателен по configured policy и доступен, и отсутствует при `tcp_bbr = off`/недоступном `auto`.
- [ ] `/etc/sysctl.conf` не изменён.
- [ ] В обоих managed-файлах первая строка содержит marker `Managed by awg2-harness sysctl_tuning`.
- [ ] Файл без marker по managed-пути не перезаписывается и не удаляется: команда завершается ошибкой.
@@ -158,7 +160,10 @@
- [ ] В shipped/default config `netdev_backlog_enabled = false`, `netdev_max_backlog = 10000`, а `net.core.netdev_max_backlog` отсутствует в managed-файле.
- [ ] При смене `netdev_backlog_enabled = true` на `false` key удаляется из persistence; оператор учитывает, что текущее runtime-значение до reboot автоматически не сбрасывается.
- [ ] До/после ручного включения backlog сняты `/proc/net/softnet_stat`, CPU и latency под реальной нагрузкой.
- [ ] `net.ipv4.tcp_congestion_control` и `net.core.default_qdisc` присутствуют в managed-файле только при активном BBR.
- [ ] В shipped/default config `tcp_bbr = off`, `default_qdisc = fq`, `udp_rmem_min_enabled = false`; `default_qdisc = fq_codel` отвергается валидацией.
- [ ] `net.ipv4.tcp_congestion_control` и `net.core.default_qdisc` присутствуют в managed-файле только когда BBR желателен по policy.
- [ ] Status отдельно показывает `bbr_available`, `bbr_desired` и фактический `bbr_runtime_active`; при available BBR и runtime `cubic` последнее поле равно `no`.
- [ ] При `tcp_bbr = off` уже активный runtime BBR не выдаётся за desired и не маскируется как неактивный.
- [ ] `swap` пишет `vm.swappiness` в `/etc/sysctl.d/90-awg2-harness-swap.conf`, а не в `99-awg2-harness.conf`.
- [ ] При обновлении с прежней версии `/etc/sysctl.d/99-awg2-harness.conf` удаляется, если несёт маркер `# Managed by awg2-harness`.
- [ ] Файл по пути `99-awg2-harness.conf` без этого маркера остаётся нетронутым и не ломает `ensure-swap`.
@@ -181,6 +186,7 @@
- [ ] Runtime apply меняет только drifted keys (`sysctl -w`), без `sysctl --system`.
- [ ] При `tcp_bbr = auto` и недоступном BBR сервис не падает, в статусе есть warning.
- [ ] При `tcp_bbr = require` и недоступном BBR команда/старт завершается с ошибкой.
- [ ] Sustained per-peer cap проверен в двух конфигурациях, BBR-off и BBR-on; HTB `rate == ceil` даёт одинаковый лимит.
- [ ] При `enabled=false` managed-файл удаляется только если содержит managed marker.
## I. Anti-torrent guard на nftables
@@ -260,10 +266,14 @@
- [ ] Повторный `apply-firewall` идемпотентен и не вносит лишних изменений в healthy state.
- [ ] Legacy fields `host_firewall.amnezia_port`/`amnezia_protocols` отвергаются как `unknown field`.
- [ ] Новая конфигурация строится в inactive A/B chain до switch; искусственный сбой `ip6tables` после IPv4 switch возвращает IPv4 на прежнюю цепочку.
- [ ] Managed jumps переключаются только через insert-new + exact-spec delete; numeric `-R INPUT <n>`/`-D INPUT <n>` не используются.
- [ ] Чужое правило, конкурентно вставленное первым в `INPUT`, не заменяется и не удаляется; rollback удаляет только новый managed jump.
- [ ] После `docker restart` anti-torrent правила продолжают восстанавливаться корректно и не ломаются host firewall логикой.
- [ ] Проверено, где реально проходит VPN-трафик в вашей Docker-схеме (`INPUT` vs `FORWARD`) и это отражено в эксплуатационной заметке.
- [ ] Для release-кандидата зафиксирован итог проверки `INPUT` vs `FORWARD` на реальном сервере (команды/логи/вывод) и подтверждено, что текущие правила firewall покрывают реальный путь трафика.
- [ ] `status` показывает блок `Docker/Amnezia port path` с полями `expected_host_udp_port`, `docker_userland_proxy`, `published_udp_port`, `awg_listen_udp_port`, `port_contract` и note про Docker-managed `FORWARD`.
- [ ] В том же блоке перечислены все UDP `host_bindings`, включая одновременно `0.0.0.0:<port>` и `[::]:<port>`; IPv6 выводится в квадратных скобках.
- [ ] Docker routed binding с пустым `HostPort` сохраняется в диагностике и не превращается в выдуманный numeric port.
- [ ] `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` автоматически.
- [ ] Сверено соответствие: `amnezia.expected_host_udp_port` ↔ опубликованный Docker UDP host port ↔ `docker exec <container> awg show <interface> listen-port`; `port_contract: ok`.