# 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 для `traffic`/`firewall`/`swap`; `sysctl_tuning` применяется только на старте daemon (если включён) или вручную отдельной командой. `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` и вход только по ключу. Сгенерируйте отдельную пару ключей для Amnezia на Windows (RSA PEM-формат): ```cmd ssh-keygen -t rsa -b 4096 -m PEM -f %USERPROFILE%\.ssh\amnezia_key ``` Дальше контракт строго такой: - на сервер добавляется **публичный** ключ: содержимое `%USERPROFILE%\.ssh\amnezia_key.pub` в `/root/.ssh/authorized_keys`; - в приложении AmneziaVPN в поле **Password or SSH private key** вставляется **приватный** ключ: содержимое `%USERPROFILE%\.ssh\amnezia_key` (без `.pub`). Критично: `.pub` никогда не вставляется в AmneziaVPN как private key. ```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 -i ~/.ssh/amnezia_key -p 2323 root@ ``` Windows-проверки: ```cmd type %USERPROFILE%\.ssh\amnezia_key.pub type %USERPROFILE%\.ssh\amnezia_key ssh -i %USERPROFILE%\.ssh\amnezia_key -p 2323 root@ ``` Поля в интерфейсе AmneziaVPN: ```text Server IP address [:port] :2323 SSH Username root Password or SSH private key содержимое %USERPROFILE%\.ssh\amnezia_key На сервер содержимое %USERPROFILE%\.ssh\amnezia_key.pub в /root/.ssh/authorized_keys ``` Если сервер уже переведён на 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`. ### 3.1 Установить AmneziaDNS (обязательный production-шаг) Для production-профиля AWG2 установите AmneziaDNS из приложения AmneziaVPN после установки AmneziaWG/AWG: ```text AmneziaVPN: Servers → → Services → AmneziaDNS → Install ``` Затем включите использование DNS для подключения: ```text Settings → Connection → DNS servers → Use AmneziaDNS ``` Security note: В ожидаемой production-схеме AmneziaDNS слушает `53/tcp` и `53/udp` внутри Docker и не публикуется на host interface. Не открывайте публично `53/tcp` или `53/udp` в host firewall; DNS должен быть доступен только через VPN-path. ### 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 (cd dist && sha256sum -c 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 Есть два production-сценария установки. #### Вариант A: config передаётся вместе с `.deb` ```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. #### Вариант B (основной): config создаётся после установки пакета ```bash scp -P 2323 dist/awg2-harness_*_amd64.deb root@:/root/ scp -P 2323 dist/SHA256SUMS root@:/root/ ``` ### 6. Установить пакет и config Вариант A: ```bash cd /root sha256sum -c SHA256SUMS chmod 644 /root/awg2-harness_0.2.6_amd64.deb 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` автоматически. Вариант B (основной): ```bash cd /root sha256sum -c SHA256SUMS chmod 644 /root/awg2-harness_0.2.6_amd64.deb apt install ./awg2-harness_*_amd64.deb install -d -m 0755 /etc/awg2-harness cp /usr/share/doc/awg2-harness/repo/config.example.toml /etc/awg2-harness/config.toml nano /etc/awg2-harness/config.toml ``` В варианте B после `cp` обязательно проверьте перед первым `apply-firewall`: - `host_firewall.ssh_port` совпадает с реальным sshd-портом; - `host_firewall.amnezia_port` совпадает с published Docker UDP port; - `amnezia.peer_ipv4_cidr` и `anti_torrent.peer_ipv4_cidr` совпадают с реальной peer-подсетью. ### 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. - [`docs/PRODUCTION_EXAMPLE.md`](docs/PRODUCTION_EXAMPLE.md) — эталонный production-пример. - [`tools/README.md`](tools/README.md) — сборка и упаковка. - [`reboot_module/README.md`](reboot_module/README.md) — внешний reboot scheduler.