19 KiB
Конфигурация AWG2 Harness
Основной target AWG2 Harness 0.3.1 — self-hosted AmneziaWG 3.1. AWG 3.0/2.x и amnezia-awg сохраняются как regression/legacy compatibility. Точная upstream-база описана в UPSTREAM_COMPATIBILITY.md. Конфиг живёт в /etc/awg2-harness/config.toml, общий для всего пакета и содержит только эксплуатационно значимые параметры.
Создание runtime config
Пакет намеренно не создаёт /etc/awg2-harness/config.toml автоматически. Это снижает риск случайно применить firewall с неверным SSH-портом или проверять не тот Amnezia UDP-порт.
После установки .deb создайте runtime config вручную:
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портом;amnezia.expected_host_udp_port— должен совпадать с published Docker/Amnezia UDP port;amnezia.peer_ipv4_cidr— должен совпадать с реальной peer-подсетью;anti_torrent.peer_ipv4_cidr— обычно тот же CIDR.
Discovery-команды:
docker port amnezia-awg2 2>/dev/null || docker port amnezia-awg
C="$(docker ps --format '{{.Names}}' | grep -E '^(amnezia-awg2|amnezia-awg)$' | head -n1)"
docker exec "$C" ip -4 addr
docker exec "$C" awg show
Рекомендуемый config.toml
[amnezia]
container_names = ["amnezia-awg2", "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"
expected_host_udp_port = 443
[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 = false
netdev_max_backlog = 10000
udp_rmem_min_enabled = true
udp_rmem_min = 8192
[host_firewall]
enabled = true
ssh_port = 2323
allow_icmp = true
default_input_policy = "drop"
[anti_torrent]
enabled = true
max_connections_per_peer = 500
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. Основной официальный вариант — amnezia-awg2, затем проверяется legacy amnezia-awg. Отдельного официального типа amnezia-awg3 в зафиксированной upstream-версии нет. Другое имя можно явно добавить в runtime config; неизвестные имена автоматически не угадываются.
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 официальным awg внутри контейнера. Это единая container-side точка доступа для kernel amneziawg и fallback amneziawg-go, не зависящая от версии host-side tools.
amnezia.peer_ipv4_cidr — подсеть VPN peers. Production default — 10.8.1.0/24; значение конфигурируемое.
amnezia.expected_host_udp_port — ожидаемый published UDP host port Docker для AmneziaWG. Поле обязательное и используется диагностикой status для контракта expected host port ↔ Docker binding ↔ awg/wg listen-port. Оно не создаёт host INPUT allow-rule: трафик к published bridge container проходит через Docker FORWARD/NAT path, а не через host INPUT.
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 и дальше. Допустимый диапазон 10..=9999; каждый фактический peer class также обязан остаться не выше 9999. Это граница текстового представления: tc разбирает class minor как hexadecimal, поэтому следующая десятичная строка 10000 уже вышла бы за 16-bit minor id.
traffic.ifb_name — имя IFB-интерфейса для ingress shaping. Production default — ifb0. Harness ставит созданному IFB alias awg2-harness:traffic и удаляет его только при совпадении type+alias. Существующий чужой интерфейс с тем же именем вызывает ошибку и не заменяется.
sysctl_tuning — отдельный host-level модуль управляемого сетевого baseline через sysctl:
- persistent-файл:
/etc/sysctl.d/90-awg2-harness-vpn.conf; - модуль
swapотдельно управляет/etc/sysctl.d/90-awg2-harness-swap.conf(толькоvm.swappiness); прежний путь/etc/sysctl.d/99-awg2-harness.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".
netdev_backlog_enabled = false — безопасный production default: harness не управляет net.core.netdev_max_backlog, пока оператор не включит это явно после измерений. Значение netdev_max_backlog = 10000 используется только при netdev_backlog_enabled = true.
При обновлении с конфига, где backlog раньше был включён, переключение флага в false удаляет key из managed-файла, но не перезаписывает уже изменённое runtime-значение. До следующей перезагрузки его можно вернуть вручную к baseline дистрибутива. Перед осознанным включением backlog снимите pre/post-метрики /proc/net/softnet_stat (в том числе dropped/time_squeeze) и CPU/latency под реальной нагрузкой.
Отдельно для всего пакета:
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. Harness владеет только помеченным comment переходом из встроенного INPUT и изолированными цепочками AWG2H_A/AWG2H_B в iptables и ip6tables. Встроенная policy, Docker FORWARD/NAT и чужие правила не очищаются и не меняются.
host_firewall.ssh_port — SSH порт хоста, который обязательно должен быть открыт. Production default/example: 2323. Значение обязано совпадать с реально слушающим sshd портом до применения apply-firewall.
Поля host_firewall.amnezia_port и host_firewall.amnezia_protocols удалены. Если они остались в старом runtime config, validate-config завершится ошибкой unknown field, чтобы оператор явно мигрировал значение в amnezia.expected_host_udp_port. Host firewall не должен открывать published Docker port в INPUT.
Не открывайте 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 == amnezia.expected_host_udp_port, затем fallback на ключ "<expected_host_udp_port>/udp" как container port, затем (если UDP binding ровно один) берётся он. Внутри контейнера выполняется awg show <interface> listen-port с fallback на wg. port_contract: ok означает, что host port совпал с config, а container port Docker binding — с фактическим listen-port. При нескольких UDP bindings без однозначного соответствия или ошибке probe выводится unknown, а не произвольный первый порт.
Диагностика FORWARD в этом блоке является best-effort: сначала используется iptables -S FORWARD, затем fallback через nft list ruleset; результаты только отображаются в status и не изменяют host FORWARD/Docker NAT.
Важно: awg2-harness не управляет host FORWARD/Docker NAT-цепочками. Проект сознательно не добавляет универсальные правила в FORWARD и не меняет Docker-managed forwarding path.
host_firewall.allow_icmp — разрешать ICMP/ICMPv6 на входе.
host_firewall.default_input_policy — конечный verdict собственной цепочки (drop или accept). Для production рекомендуется drop. Это поле не меняет policy встроенной цепочки INPUT.
anti_torrent.max_connections_per_peer — максимум одновременных conntrack flows на один VPN peer IP. Штатный production baseline — 500, выбранный по эксплуатационным измерениям проекта так, чтобы connection guard оставался включённым и не вмешивался в нормальный повседневный профиль. Значение ниже 500 является сознательно более агрессивной политикой и требует проверки на реальных клиентах. Поле обязательное и не скрыто в Rust default.
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: iptables/ip6tables host firewall (host namespace) и nftables anti-torrent firewall (configured namespace). Host-цепочки обновляются через A/B staging: обе IP-family сначала строятся и проверяются, после чего jump переключается; при ошибке IPv6 выполненный IPv4 switch откатывается.
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.
Ограничения swap.path: путь должен быть либо /swapfile, либо файлом непосредственно в /var/lib/awg2-harness/. Дополнительно: абсолютный, без компонентов . и .., без пробелов, управляющих символов и обратных слэшей; на этапе применения проверяется, что ни один родительский каталог не является symlink.
Ограничение корней обязательно, а не косметика. Если родительский каталог доступен на запись кому-то кроме root, между проверкой пути и запуском mkswap --file этот пользователь успевает подменить файл на symlink и перенаправить операцию, выполняемую от root. Оба разрешённых корня принадлежат root, поэтому окна не возникает. /swapfile оставлен для совместимости с уже развёрнутыми конфигурациями.
Владение фиксируется в /var/lib/awg2-harness/swap-ownership.conf по device + inode + пути, а не по маркеру рядом с файлом. Файл, которого нет в этой записи, или файл с тем же путём, но другими device/inode (то есть подменённый), считается чужим: он никогда не пересоздаётся и не удаляется, вместо этого возвращается ошибка. Чужие активные swap только выключаются — их файлы пакет не удаляет.
Что не выносить в конфиг
Не нужно выносить:
- 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, а не настройка оператора.