fix: harden firewall and traffic reconciliation
This commit is contained in:
+13
-18
@@ -4,7 +4,7 @@
|
||||
|
||||
## Создание runtime config
|
||||
|
||||
Пакет намеренно **не** создаёт `/etc/awg2-harness/config.toml` автоматически. Это снижает риск случайно применить firewall с неверным SSH-портом или неверным Amnezia-портом.
|
||||
Пакет намеренно **не** создаёт `/etc/awg2-harness/config.toml` автоматически. Это снижает риск случайно применить firewall с неверным SSH-портом или проверять не тот Amnezia UDP-порт.
|
||||
|
||||
После установки `.deb` создайте runtime config вручную:
|
||||
|
||||
@@ -17,7 +17,7 @@ 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.expected_host_udp_port` — должен совпадать с published Docker/Amnezia UDP port;
|
||||
- `amnezia.peer_ipv4_cidr` — должен совпадать с реальной peer-подсетью;
|
||||
- `anti_torrent.peer_ipv4_cidr` — обычно тот же CIDR.
|
||||
|
||||
@@ -40,6 +40,7 @@ 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
|
||||
@@ -66,8 +67,6 @@ udp_rmem_min = 8192
|
||||
[host_firewall]
|
||||
enabled = true
|
||||
ssh_port = 2323
|
||||
amnezia_port = 443
|
||||
amnezia_protocols = ["udp"]
|
||||
allow_icmp = true
|
||||
default_input_policy = "drop"
|
||||
|
||||
@@ -104,6 +103,8 @@ swappiness = 1
|
||||
|
||||
`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`.
|
||||
@@ -112,9 +113,9 @@ swappiness = 1
|
||||
|
||||
`traffic.container_ready_timeout_secs` — сколько ждать появления интерфейса после старта контейнера.
|
||||
|
||||
`traffic.class_start_id` — первый class id. Production default — `10`, что даёт классы `1:10`, `2:10` и дальше.
|
||||
`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`. Этот параметр можно оставить в конфиге, потому что бинарь реально создаёт/удаляет этот интерфейс.
|
||||
`traffic.ifb_name` — имя IFB-интерфейса для ingress shaping. Production default — `ifb0`. Harness ставит созданному IFB alias `awg2-harness:traffic` и удаляет его только при совпадении type+alias. Существующий чужой интерфейс с тем же именем вызывает ошибку и не заменяется.
|
||||
|
||||
`sysctl_tuning` — отдельный host-level модуль управляемого сетевого baseline через `sysctl`:
|
||||
|
||||
@@ -153,31 +154,25 @@ swappiness = 1
|
||||
|
||||
`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.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` — внешний UDP/TCP порт Amnezia, который AWG2 открывает в host `INPUT` (в зависимости от `host_firewall.amnezia_protocols`). В текущей целевой конфигурации default — `443`; значение должно совпадать с реально опубликованным Amnezia UDP/TCP port.
|
||||
Поля `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`.
|
||||
|
||||
Важно: `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-профиля.
|
||||
Не открывайте `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`.
|
||||
Для диагностического блока `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.
|
||||
|
||||
`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`.
|
||||
`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.
|
||||
|
||||
@@ -190,7 +185,7 @@ swappiness = 1
|
||||
|
||||
Для production рекомендуется `true`, если peer source IP (`10.8.1.x`) видны именно внутри netns контейнера.
|
||||
|
||||
`apply-firewall` в текущей модели делает **оба** reconcile: host firewall (host namespace) и anti-torrent firewall (configured namespace).
|
||||
`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` — включает плановую перезагрузку.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user