Files
AWG2_harness/docs/CONFIGURATION.md
T
founder d98ade42ce docs: unify language contract to Russian and reconcile the spec with the code
The docs folder had drifted into a mixed-language contract: IMPLEMENTATION_SPEC.md was
entirely in English while the rest was Russian with English headings. Prose is now
Russian throughout; English is kept only where it is technically load-bearing (commands,
tool output, config keys, file/module/table names, tc handles) and for terms with no
settled Russian equivalent in this context (drift, reconcile, peer, namespace, handshake).
docs/README.md now states that contract explicitly.

Rewriting the spec surfaced several places where it described intent rather than the
code. Corrected against traffic_core/src:

- module layout: the spec showed traffic/ and firewall/ as directories with
  model/inspect/apply submodules. They are single files, and sysctl_tuning.rs was
  missing from the tree entirely.
- data model: Peer was documented as carrying public key, handshake and counters. It
  carries only ipv4; runtime state lives in a separate PeerRuntime, and there is no
  desired-state struct at all - it is derived inline from config plus sorted peers.
  RuntimeTarget.netns_path is a String, not a PathBuf.
- interface detection: the spec listed a third step taking the first non-loopback
  interface with an IPv4 address. No such fallback exists; detection bails with an
  explicit error, which is safer and is now documented as deliberate.
- reconcile: the pseudo-flow folded firewall into reconcile_traffic. They are two
  independent reconciles with separate locks.
- locking: only traffic.lock was documented. There are three locks, and
  sysctl_tuning.lock has a 30s timeout rather than 10s. Stale-lock recovery via
  /proc/<pid> was undocumented.
- sysctl_tuning: the module also manages /etc/modules-load.d/90-awg2-harness-bbr.conf
  when BBR is active, and refuses to touch either managed file without its marker.
  Both docs and the acceptance checklist claimed only the sysctl.d file was written.
- status contract: the documented output was outdated. All 15 printed sections are now
  listed in the order status.rs emits them, including the degraded path when no container
  is found, and the fact that the Network tuning block is printed between the System
  service line and the container lines.
- swap: size was documented as a constant 2048 MB instead of swap.size_mb, and the
  /etc/fstab.awg2-harness.bak backup was undocumented.
- apply/verify: the tolerated-error lists are now the actual closed sets from the code.

Acceptance checklist gained checks for the daemon startup failure policy (sysctl/swap
fatal, traffic/firewall not), lock contention and stale-lock recovery, degraded status,
the modules-load file, and the marker guard. The stale host wg requirement was dropped
from its host-tools item.

Every path, table, counter and log string quoted in the docs was verified to exist in
the source. cargo test --workspace: 110 + 4 passed.
2026-08-06 16:45:00 +05:00

212 lines
14 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 разрабатывался для self-hosted AmneziaWG/AWG 2.0 и совместимого legacy-сценария AmneziaWG/AWG 1.5. Конфиг живёт в `/etc/awg2-harness/config.toml`. Он общий для всего пакета и должен содержать только эксплуатационно значимые параметры.
## Создание runtime config
Пакет намеренно **не** создаёт `/etc/awg2-harness/config.toml` автоматически. Это снижает риск случайно применить firewall с неверным SSH-портом или неверным Amnezia-портом.
После установки `.deb` создайте runtime config вручную:
```bash
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
```
Перед первым `apply-firewall`/`apply-limits` оператор обязан сверить:
- `host_firewall.ssh_port` — должен совпадать с реально слушающим `sshd` портом;
- `host_firewall.amnezia_port` — должен совпадать с published Docker/Amnezia UDP port;
- `amnezia.peer_ipv4_cidr` — должен совпадать с реальной peer-подсетью;
- `anti_torrent.peer_ipv4_cidr` — обычно тот же CIDR.
Discovery-команды:
```bash
docker port amnezia-awg2 2>/dev/null || docker port amnezia-awg3 2>/dev/null || docker port amnezia-awg
C="$(docker ps --format '{{.Names}}' | grep -E '^(amnezia-awg2|amnezia-awg3|amnezia-awg)$' | head -n1)"
docker exec "$C" ip -4 addr
docker exec "$C" awg show
```
## Рекомендуемый `config.toml`
```toml
[amnezia]
container_names = ["amnezia-awg2", "amnezia-awg3", "amnezia-awg"]
interface_candidates = ["awg0", "wg0", "amnezia0", "tun0"]
interface_patterns = ["awg*", "wg*", "amnezia*", "tun*"]
wg_binaries = ["awg", "wg"]
peer_ipv4_cidr = "10.8.1.0/24"
[traffic]
enabled = true
peer_limit = "50mbit"
root_rate = "500mbit"
reconcile_interval_secs = 15
container_ready_timeout_secs = 120
class_start_id = 10
ifb_name = "ifb0"
[sysctl_tuning]
enabled = true
apply_on_daemon_start = true
ip_forward = true
tcp_bbr = "auto" # off | auto | require
default_qdisc = "fq" # none | fq | fq_codel
rmem_max = 67108864
wmem_max = 67108864
netdev_backlog_enabled = true
netdev_max_backlog = 50000
udp_rmem_min_enabled = true
udp_rmem_min = 8192
[host_firewall]
enabled = true
ssh_port = 2323
amnezia_port = 443
amnezia_protocols = ["udp"]
allow_icmp = true
default_input_policy = "drop"
[anti_torrent]
enabled = true
max_connections_per_peer = 150
peer_ipv4_cidr = "10.8.1.0/24"
apply_in_amnezia_netns = true
[reboot]
enabled = true
window_start_utc = "01:00"
window_end_utc = "02:00"
[swap]
enabled = true
path = "/swapfile"
size_mb = 2048
priority = 1
swappiness = 1
```
## Пояснения
`amnezia.container_names` — имена контейнеров, которые считаются Amnezia/AWG. Порядок важен, сопоставление точное. По текущему production-снимку основной вариант — `amnezia-awg2`, fallback — `amnezia-awg`. `amnezia-awg3` перечислен на будущее: в AmneziaVPN 5.0 отдельного типа контейнера под AWG 3.0 нет, клиент по-прежнему знает только `Awg` и `Awg2`.
`amnezia.interface_candidates` — точные имена интерфейса, которые пробуются первыми.
`amnezia.interface_patterns` — fallback-паттерны для устойчивости к обновлениям Amnezia. Они нужны для снижения риска переименования интерфейса между `awg0`, `wg0`, `amnezia0`, `tun0`.
`amnezia.wg_binaries` — утилита чтения peer state внутри контейнера, пробуется по порядку до первого успеха. Default — `["awg", "wg"]`. В официальном образе AmneziaWG реальный бинарь `/usr/bin/awg`, а `/usr/bin/wg` — symlink на него, поэтому `awg` идёт первым; `wg` остаётся для контейнеров с чистым WireGuard. Значения выполняются внутри контейнера и валидируются как имена команд: непустые, без пробелов и без разделителей пути. Параметр опциональный — конфиги, созданные до его появления, продолжают работать с default.
Хостовый `wireguard-tools` не нужен и не проверяется в preflight. Peer state читается только через `docker exec`: AmneziaWG — userspace-реализация, её UAPI-сокет находится в `RUNSTATEDIR/amneziawg/` внутри mount namespace контейнера, а `nsenter -n` входит только в network namespace, поэтому хостовый `wg` до него не достаёт.
`amnezia.peer_ipv4_cidr` — подсеть VPN peers. Production default — `10.8.1.0/24`; значение конфигурируемое.
`traffic.peer_limit` — per-peer лимит в обе стороны. Текущий production default — `50mbit`.
`traffic.root_rate` — верхний HTB-класс, в текущем production default — `500mbit`.
`traffic.reconcile_interval_secs` — период фоновой проверки. Он нужен не вместо Docker events, а вместе с ними.
`traffic.container_ready_timeout_secs` — сколько ждать появления интерфейса после старта контейнера.
`traffic.class_start_id` — первый class id. Production default — `10`, что даёт классы `1:10`, `2:10` и дальше.
`traffic.ifb_name` — имя IFB-интерфейса для ingress shaping. Production default — `ifb0`. Этот параметр можно оставить в конфиге, потому что бинарь реально создаёт/удаляет этот интерфейс.
`sysctl_tuning` — отдельный host-level модуль управляемого сетевого baseline через `sysctl`:
- persistent-файл: `/etc/sysctl.d/90-awg2-harness-vpn.conf`;
- дополнительный managed-файл `/etc/modules-load.d/90-awg2-harness-bbr.conf` создаётся только при фактически активном BBR и удаляется, когда BBR неактивен;
- оба managed-файла несут в первой строке маркер `# Managed by awg2-harness sysctl_tuning`; файл без маркера не перезаписывается и не удаляется, вместо этого возвращается ошибка;
- `net.ipv4.tcp_congestion_control` и `net.core.default_qdisc` управляются только при активном BBR;
- runtime apply: только `sysctl -w` и только для drifted keys;
- на установке `.deb` модуль не применяется;
- на старте daemon модуль применяется, если `enabled=true` и `apply_on_daemon_start=true`;
- `tcp_bbr = auto` не валит запуск при недоступном BBR (warning), `require` — валит;
- для `sysctl_tuning`: `modprobe` обязателен только для `tcp_bbr = "require"`.
Отдельно для всего пакета:
- `traffic`-модуль требует `modprobe` для IFB при `traffic.enabled = true`.
Совместимость обновлений:
- если в существующем runtime-конфиге отсутствует блок `[sysctl_tuning]`, он не включается автоматически (`enabled=false` по default для missing block);
- для новых установок рекомендуется явный блок `[sysctl_tuning]` из примера, где `enabled=true`.
Ограничения модуля:
- не использует `/etc/sysctl.conf`;
- не запускает `sysctl --system`;
- не меняет `nftables`, Docker bridge и container-local sysctl;
- не управляет `net.ipv4.udp_wmem_min` (в Linux документации параметр указан как не имеющий эффекта).
При drift orchestrator всегда делает controlled rebuild owned topology.
`anti_torrent.enabled` — включает nftables connection-count guard против high-peer-count P2P-нагрузки. Это отдельный AWG2 Harness module и часть текущего production-контракта.
`host_firewall.enabled` — включает host-level INPUT firewall в отдельной AWG2-owned таблице `inet awg2_host_firewall`.
`host_firewall.ssh_port` — SSH порт хоста, который обязательно должен быть открыт. Production default/example: `2323`. Значение обязано совпадать с реально слушающим sshd портом до применения `apply-firewall`.
`host_firewall.amnezia_port` — внешний UDP/TCP порт Amnezia, который AWG2 открывает в host `INPUT` (в зависимости от `host_firewall.amnezia_protocols`). В текущей целевой конфигурации default — `443`; значение должно совпадать с реально опубликованным Amnezia UDP/TCP port.
Важно: `host_firewall.amnezia_port` — это transport-порт AmneziaWG/AWG (например, `443/udp`). Это не порт AmneziaDNS.
- Не задавайте `amnezia_port = 53`.
- Не добавляйте DNS/53 в `amnezia_protocols`.
- Не открывайте `53/tcp` или `53/udp` в AWG2 host firewall для ожидаемого production-профиля.
В expected production layout AmneziaDNS работает как внутренний Docker/VPN сервис и не публикуется на public host interface.
Для диагностического блока `Docker/Amnezia port path` в `status` приоритет сопоставления такой: сначала ищется published UDP binding с `HostPort == host_firewall.amnezia_port`, затем fallback на ключ `"<amnezia_port>/udp"` как container port, затем (если UDP binding ровно один) берётся он. Если UDP bindings несколько и однозначного совпадения нет, статус показывает `published_udp_port: unknown`.
Диагностика `FORWARD` в этом блоке является best-effort: сначала используется `iptables -S FORWARD`, затем fallback через `nft list ruleset`; результаты только отображаются в `status` и не изменяют host `FORWARD`/Docker NAT.
`host_firewall.amnezia_protocols` — протоколы для порта Amnezia (`udp`, `tcp` или оба).
Важно: `awg2-harness` не управляет host `FORWARD`/Docker NAT-цепочками. Проект сознательно не добавляет универсальные правила в `FORWARD` и не меняет Docker-managed forwarding path.
`host_firewall.allow_icmp` — разрешать ICMP/ICMPv6 на входе.
`host_firewall.default_input_policy` — политика INPUT chain (`drop` или `accept`). Для production рекомендуется `drop`.
`anti_torrent.max_connections_per_peer` — максимум одновременных conntrack flows на один VPN peer IP. Текущий пример использует `150`: это мягче, чем строгий `100`, и обычно безопаснее как первый production baseline. `100` можно использовать как более жёсткий режим; `200/250` — более permissive варианты.
`anti_torrent.peer_ipv4_cidr` — подсеть, на которую распространяется правило. Обычно должна совпадать с `amnezia.peer_ipv4_cidr`.
`anti_torrent.apply_in_amnezia_netns` — выбор namespace для применения anti-torrent правил:
- `true` (default): применять в network namespace контейнера Amnezia;
- `false`: применять в host namespace.
Для production рекомендуется `true`, если peer source IP (`10.8.1.x`) видны именно внутри netns контейнера.
`apply-firewall` в текущей модели делает **оба** reconcile: host firewall (host namespace) и anti-torrent firewall (configured namespace).
`reboot.enabled` — включает плановую перезагрузку.
`reboot.window_start_utc` / `reboot.window_end_utc` — UTC-окно плановой перезагрузки. Production default/example — `01:00`–`02:00` UTC.
`swap.enabled` — включает setup-задачу swap.
`swap.path`, `swap.size_mb`, `swap.priority`, `swap.swappiness` — параметры managed swap. При `swap.enabled=true` пакет приводит swap-state к exclusive состоянию: активен ровно один swap (`swap.path`), остальные выключаются и swap-entries в `/etc/fstab` пересобираются в одну managed строку. Рекомендуемый low priority — `1`, `swappiness = 1`.
## Что не выносить в конфиг
Не нужно выносить:
- handles `1:`, `2:`, `ffff:`;
- имена внутренних Rust-модулей;
- формат команд `tc`;
- retry micro-tuning;
- формат логов;
- путь lock-файла, если нет эксплуатационной причины;
- hardening systemd;
- nftables hook/priority/table names, если нет осознанной причины.
Handles и структура `HTB + fq_codel + ingress + ifb` — это contract traffic module, а не настройка оператора.