Files
AWG2_harness/README.md
T

285 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-пользователя.
Для SSH через Amnezia используйте ключ в стандартном OpenSSH public key формате (строка вида `ssh-rsa AAAA... comment` в `authorized_keys`). Практически это означает, что Amnezia ожидает именно публичный ключ в формате OpenSSH authorized_keys.
```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@<server-ip>
```
Если сервер уже переведён на 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@<server-ip>:/root/
scp -P 2323 dist/SHA256SUMS root@<server-ip>:/root/
scp -P 2323 config.example.toml root@<server-ip>:/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 <command>
```
`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:<id>` и `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.