# AWG2 Harness `AWG2 Harness` — host-side orchestrator для выделенного Debian-хоста с self-hosted AmneziaWG. Пакет управляет инфраструктурным состоянием вокруг Amnezia: per-peer traffic shaping, IFB ingress shaping, nftables host firewall, anti-torrent connection guard, managed swap, диагностикой drift и плановым reboot. Проект разрабатывался и проверялся под AmneziaWG/AWG 2.0 и совместимый legacy-сценарий AmneziaWG/AWG 1.5. Он не заменяет AmneziaVPN, не генерирует ключи клиентов и не меняет продуктовую логику Amnezia. Amnezia сначала ставится официальным способом, после этого устанавливается AWG2 Harness. ## Что ставит пакет ```text /usr/sbin/awg2-harness # основной orchestrator /usr/sbin/awg2-reboot-scheduler # отдельный reboot scheduler /lib/systemd/system/awg2-harness.service /lib/systemd/system/awg2-reboot-scheduler.service ``` `awg2-harness.service` запускает `ensure-swap` перед стартом daemon, затем в фоне держит drift recovery. Daemon слушает Docker events и дополнительно делает периодический reconcile. `awg2-reboot-scheduler.service` отдельно читает общий config, выбирает случайное время в заданном UTC-окне и запрашивает reboot через `systemctl --no-block reboot`. Пакет **не** устанавливает Docker, **не** устанавливает AmneziaVPN, **не** включает сервисы автоматически и **не** создаёт боевой `/etc/awg2-harness/config.toml`. Это сделано намеренно: SSH, Amnezia и боевой config должны быть подготовлены оператором до включения firewall/reconcile. ## Репозиторий ```text awg2_harness/ ├── reboot_module/ # исходники awg2-reboot-scheduler ├── traffic_core/ # исходники awg2-harness ├── tools/ # build/package tooling ├── docs/ # эксплуатационная документация ├── config.example.toml # пример боевого конфига ├── awg2-harness.service # основной systemd unit └── awg2-reboot-scheduler.service # reboot scheduler unit ``` ## Runtime-план production-развёртывания Ниже целевой порядок установки. Его смысл простой: сначала безопасный SSH, потом официальная Amnezia, потом AWG2 Harness. ### 1. Подготовить Debian-хост Целевая платформа AWG2 Harness — только Debian 13 amd64 на KVM/полноценной VM или dedicated host. Минимальный baseline перед установкой Amnezia: ```bash apt update apt install -y openssh-server ca-certificates curl ``` ### 2. Настроить SSH до установки Amnezia Production-пример использует порт `2323` и вход только по ключу. Сначала убедитесь, что публичный ключ уже добавлен в `~root/.ssh/authorized_keys` или в ключевой файл нужного sudo-пользователя. ```bash install -d -m 0700 /root/.ssh nano /root/.ssh/authorized_keys chmod 0600 /root/.ssh/authorized_keys ``` Затем включите отдельный drop-in для порта и key-only login: ```bash install -d -m 0755 /etc/ssh/sshd_config.d cat >/etc/ssh/sshd_config.d/99-awg2-production.conf <<'EOF' Port 2323 PermitRootLogin prohibit-password PasswordAuthentication no KbdInteractiveAuthentication no PubkeyAuthentication yes EOF sshd -t systemctl reload ssh 2>/dev/null || systemctl reload sshd ss -ltnp | grep ':2323' ``` Критично: не закрывайте старую SSH-сессию, пока не проверили новый вход с другого терминала: ```bash ssh -p 2323 root@ ``` Если сервер уже переведён на 2323 случайно и нужно временно вернуть 22 для тестов, поменяйте `Port 2323` на `Port 22`, выполните `sshd -t`, `systemctl reload ssh` и проверьте `ss -ltnp | grep ':22'`. ### 3. Установить AmneziaVPN официальным способом AWG2 Harness не устанавливает Amnezia. Используйте официальный self-hosted flow AmneziaVPN: 1. Установите актуальное приложение AmneziaVPN на рабочую машину. 2. В приложении выберите self-hosted server. 3. Укажите публичный IPv4 сервера, SSH-порт, SSH-пользователя и приватный ключ. 4. Установите AmneziaWG/AWG. Для AWG 2.0 нужен актуальный AmneziaVPN, где протокол AmneziaWG 2.0 доступен в списке протоколов. 5. После установки убедитесь, что контейнер поднят и UDP-порт опубликован. Проверка на сервере: ```bash docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}' C="$(docker ps --format '{{.Names}}' | grep -E '^(amnezia-awg2|amnezia-awg)$' | head -n1)" echo "container=$C" docker port "$C" docker exec "$C" wg show ``` Ожидаемые имена контейнера по умолчанию: `amnezia-awg2` или `amnezia-awg`. Ожидаемый интерфейс внутри container netns: обычно `awg0`, fallback-кандидаты описаны в `config.example.toml`. ### 4. Собрать пакет на build-machine На production-хосте не нужны Rust, cargo и source tree. Сборка делается отдельно. ```bash cd awg2_harness ./tools/build.sh --prepare-build-env --bootstrap-rust --yes ./tools/build.sh --package --version 0.1.6 sha256sum -c dist/SHA256SUMS dpkg-deb -I dist/awg2-harness_0.1.6_amd64.deb dpkg-deb -c dist/awg2-harness_0.1.6_amd64.deb | sort ``` `--version` можно не указывать, если версия в `traffic_core/Cargo.toml` уже соответствует release. Если version override указан, он меняет версию Debian-пакета, а не версию crate в `Cargo.toml`. ### 5. Перенести `.deb` и config на production ```bash scp -P 2323 dist/awg2-harness_*_amd64.deb root@:/root/ scp -P 2323 dist/SHA256SUMS root@:/root/ scp -P 2323 config.example.toml root@:/root/awg2-harness.config.toml ``` Перед установкой отредактируйте `/root/awg2-harness.config.toml`: SSH-порт должен совпадать с реальным sshd, `amnezia_port` — с опубликованным UDP-портом контейнера, `peer_ipv4_cidr` — с подсетью клиентов Amnezia. ### 6. Установить пакет и config ```bash cd /root sha256sum -c SHA256SUMS apt install ./awg2-harness_*_amd64.deb install -d -m 0755 /etc/awg2-harness install -m 0644 /root/awg2-harness.config.toml /etc/awg2-harness/config.toml ``` Пакет ставит пример config в `/usr/share/doc/awg2-harness/repo/config.example.toml`, но не копирует его в `/etc` автоматически. ### 7. Ручная preflight-проверка ```bash awg2-harness --config /etc/awg2-harness/config.toml validate-config awg2-harness --config /etc/awg2-harness/config.toml validate-runtime awg2-harness --config /etc/awg2-harness/config.toml ensure-swap awg2-harness --config /etc/awg2-harness/config.toml apply-firewall awg2-harness --config /etc/awg2-harness/config.toml apply-limits awg2-harness --config /etc/awg2-harness/config.toml status ``` Ожидаемый healthy state: ```text System: service: inactive # до enable --now нормально Traffic actual: drift: no Host firewall: drift: no Anti-torrent: drift: no Swap: drift: no ``` Если peers ещё нет, `apply-limits` корректно напишет `no peers found, skip tc apply`. Это не ошибка: traffic topology строится, когда есть хотя бы один peer allowed IP в целевой подсети. ### 8. Включить systemd services ```bash systemctl daemon-reload systemctl enable --now awg2-harness.service systemctl enable --now awg2-reboot-scheduler.service systemctl status awg2-harness.service --no-pager -l systemctl status awg2-reboot-scheduler.service --no-pager -l journalctl -u awg2-harness.service --since '5 minutes ago' --no-pager journalctl -u awg2-reboot-scheduler.service --since '5 minutes ago' --no-pager ``` После 30–60 секунд: ```bash awg2-harness --config /etc/awg2-harness/config.toml status ``` Healthy daemon показывает `service: active`, `traffic=ok`, `firewall=ok`, `swap=ok` в recent events и пустой `last_error`. ### 9. Проверить после reboot ```bash reboot ``` После возврата: ```bash systemctl status docker --no-pager -l systemctl status awg2-harness.service --no-pager -l systemctl status awg2-reboot-scheduler.service --no-pager -l awg2-harness --config /etc/awg2-harness/config.toml status ``` Дополнительно можно проверить фактическую `tc` topology внутри network namespace Amnezia: ```bash C="$(docker ps --format '{{.Names}}' | grep -E '^(amnezia-awg2|amnezia-awg)$' | head -n1)" PID="$(docker inspect -f '{{.State.Pid}}' "$C")" nsenter -t "$PID" -n -- tc class show dev awg0 nsenter -t "$PID" -n -- tc class show dev ifb0 nsenter -t "$PID" -n -- nft list table inet awg2_harness nft list table inet awg2_host_firewall ``` Для default config на одном peer ожидаются классы `1:10` и `2:10` с `rate 50Mbit ceil 50Mbit`, root classes `1:1` и `2:1` с `500Mbit`, host firewall table `inet awg2_host_firewall` и anti-torrent table `inet awg2_harness` внутри Amnezia netns. ## CLI команды Все команды принимают `--config`. Если параметр не указан, используется `/etc/awg2-harness/config.toml`. ```bash awg2-harness --config /etc/awg2-harness/config.toml ``` `validate-config` проверяет TOML и смысловые ограничения: пустые списки, формат rate (`50mbit`, `500mbit`), CIDR, reboot window, swap path, SSH/Amnezia ports, protocol list. `validate-runtime` проверяет host tools, которые реально нужны выбранным модулям: `tc`, `ip`, `wg`, `docker`, `modprobe`, `nsenter`, а также `nft`, `systemctl`, swap tooling при включённых firewall/reboot/swap. `ensure-swap` приводит swap к desired state: создаёт/проверяет swapfile, включает его с заданным priority, применяет `vm.swappiness`, делает swap state exclusive и переписывает swap entries в `/etc/fstab` под управляемый swapfile. `apply-firewall` делает два reconcile: host INPUT firewall в таблице `inet awg2_host_firewall` и anti-torrent guard в выбранном namespace. Команда не управляет Docker NAT и host `FORWARD`. `apply-limits` находит Amnezia container, заходит в его network namespace, читает peers через `wg`, строит per-peer HTB/fq_codel классы на AWG/WG интерфейсе и IFB ingress path. Если drift не найден, пишет `traffic rules up-to-date, skip apply`. `status` печатает фактическую диагностику: сервис, контейнер, netns, интерфейс, peers, WireGuard runtime, traffic desired/actual, host firewall drift, Docker/Amnezia port path, anti-torrent counters, swap state, conntrack, recent events и planned reboot. `daemon` — режим для systemd. На старте делает startup reconcile, затем слушает Docker events и периодически сверяет traffic/swap/firewall. Вручную обычно не запускается, кроме отладки. `awg2-reboot-scheduler --config /etc/awg2-harness/config.toml` читает только секцию `[reboot]`, пишет план в `/run/awg2-harness/reboot.plan` и ждёт назначенного UTC-времени. ## Что происходит под капотом Traffic shaping реализован через `tc` в network namespace контейнера Amnezia. На egress используется root qdisc `htb 1:` на AWG/WG интерфейсе, root class `1:1`, per-peer classes `1:` и `fq_codel` под каждым peer class. На ingress ставится `ingress ffff:` на AWG/WG интерфейс, входящие пакеты перенаправляются через `mirred` в `ifb0`, а там строится отдельная HTB topology `2:` с такими же per-peer лимитами. Peer discovery берётся из WireGuard/AmneziaWG allowed IPs, а не из handshake state. Поэтому offline peer с `allowed ips: 10.8.1.1/32` тоже получает лимит заранее. Host firewall — отдельная AWG2-owned таблица `inet awg2_host_firewall`. Она открывает loopback, established/related, SSH-порт из конфига, Amnezia-порт по указанным протоколам и optionally ICMP/ICMPv6. Docker-managed `FORWARD` и NAT не трогаются. Anti-torrent guard — отдельная AWG2-owned таблица `inet awg2_harness` обычно внутри Amnezia netns. Правило ограничивает число новых conntrack flows per source VPN IP. Это не DPI и не распознавание BitTorrent-протокола; это грубый, дешёвый guard против сотен одновременных P2P-соединений. Swap module намеренно строгий: если `swap.enabled=true`, desired state — один управляемый swapfile. Старые swap entries удаляются из `/etc/fstab`, block-device swap выключается, но block device не удаляется. ## Production-границы ответственности AWG2 Harness владеет только собственным state: - `tc` topology на Amnezia interface и configured `ifb_name`; - nft table `inet awg2_host_firewall` в host namespace; - nft table `inet awg2_harness` для anti-torrent; - `/run/awg2-harness/*` runtime state; - managed swapfile и swap entries, если `swap.enabled=true`. AWG2 Harness не владеет: - установкой AmneziaVPN; - Docker daemon lifecycle; - Docker NAT/FORWARD chains; - генерацией client configs/keys; - политикой пользователя внутри Amnezia app; - сторонними firewall-таблицами. ## Документация - [`docs/INSTALLATION.md`](docs/INSTALLATION.md) — установка и preflight. - [`docs/CONFIGURATION.md`](docs/CONFIGURATION.md) — параметры `config.toml`. - [`docs/ACCEPTANCE_CHECKLIST.md`](docs/ACCEPTANCE_CHECKLIST.md) — production checklist. - [`docs/ANTI_TORRENT.md`](docs/ANTI_TORRENT.md) — connection-count guard. - [`tools/README.md`](tools/README.md) — сборка и упаковка. - [`reboot_module/README.md`](reboot_module/README.md) — внешний reboot scheduler.