Files
awg-dash/tests/netns/README.md
T
embeddevio 4aa69132ad netns: nsenter в topology.sh, netns_common и документации (#56)
- topology.sh: ns_exec для всех ip -n <ns> и ip netns exec, коды 0/1/3/4.
- netns_common.py: ns_exec_mode()/ns_exec_direct(), addrs/links/link_is_up через ns_sh.
- docker-compose.test.yml: проброс AWG_NS_EXEC_MODE в netns-сервис.
- tests/netns/README.md: режимы входа в netns, коды возврата, новый pitfall.

Refs #21, Refs #15, Refs #56
2026-10-28 21:30:22 +03:00

119 lines
8.7 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.
# tests/netns — сетевой тестовый стенд
Задача **#21**, используется тестами split-tunnel (#40, #41, #43).
Цель: проверять маршрутизацию, policy routing, метки трафика и fail-closed
**без реального VPN-провайдера** и без доступа к продакшн-сети.
## Топология
```
client ns gw ns (шлюз awg-dash) uplink ns («ISP»)
10.213.0.2/24 ──veth──► 10.213.0.1/24 10.214.0.1/24 ──veth──► 10.214.0.2/24
(LAN-устройство) ip_forward=1, ip rule fwmark 51820 мок-endpoint udp/51820
→ table 51820 → default via uplink детектор утечки
198.51.100.9:9999
```
| Namespace | По умолчанию | Роль | Адреса |
|---|---|---|---|
| `awgt21-client` | `AWG_NS_CLIENT` | LAN-устройство за шлюзом | `10.213.0.2/24` |
| `awgt21-gw` | `AWG_NS_GW` | хост со шлюзом: forwarding, policy routing, netfilter | `10.213.0.1/24`, `10.214.0.1/24` |
| `awgt21-uplink` | `AWG_NS_UPLINK` | «ISP»: мок WireGuard-endpoint + детектор утечки | `10.214.0.2/24`, `198.51.100.9/32` |
Правила в `gw`: `ip rule fwmark 51820 lookup 51820` + `table 51820 default via 10.214.0.2`
(как в `wg-quick`), метки ставит netfilter на исходящий трафик шлюза (OUTPUT) и на
транзитный трафик LAN (FORWARD). Kill-switch — правило DROP в FORWARD для трафика
LAN, уходящего напрямую в «публичную» сеть `198.51.100.0/24` (RFC 5737 TEST-NET-2).
Адрес детектора утечки поднят на `lo` аплинка: если при включённом kill-switch пакет
дошёл до детектора — тест падает.
## Запуск
```bash
make test-netns # поднять стенд, прогнать тесты, снести стенд
make test-netns-keep # оставить топологию и логи для отладки
make test-netns NETNS_ARGS="--no-tests" # только поднять стенд
make test-netns-ci # то же в привилегированном контейнере (путь для CI)
# в контейнере вход в netns идёт через nsenter (issue #56)
bash tests/netns/topology.sh status # состояние топологии
bash tests/netns/topology.sh down # снести топологию вручную
bash tests/netns/topology.sh killswitch on # включить fail-closed правило
```
Требования: Linux, **root** (или CAP_NET_ADMIN + CAP_SYS_ADMIN), `ip`, `python3`;
backend netfilter — `nft` или `iptables` (в alpine-образе приложения есть только
`iptables`, на Debian/Ubuntu — оба).
Коды возврата: `0` — тесты прошли, `1` — тесты упали, `3` — «заблокировано окружением»
(нет прав на создание netns), `4` — ошибка самого стенда (сюда же нормализуются
неожиданные коды утилит, например `255` от `ip netns exec` в контейнере).
Вход в namespace выполняется через `ns_exec()`: `ip netns exec` в контейнере падает
(`mount of /sys failed`, issue #56), поэтому харнесс сам переключается на
`nsenter --net=/var/run/netns/<ns>`; режим форсируется `AWG_NS_EXEC_MODE`.
## Состав
| Файл | Назначение |
|---|---|
| `run.sh` | точка входа: preflight → топология → мок → тесты → снос |
| `lib.sh` | конфигурация стенда + правила netfilter (nft/iptables) |
| `topology.sh` | `up`/`down`/`status`/`killswitch on|off` |
| `mock_wg_endpoint.py` | мок WireGuard-endpoint: отвечает на handshake initiation |
| `leak_sink.py` | детектор утечки: UDP+TCP `198.51.100.9:9999` |
| `probe.py` | генератор тестового трафика из указанного netns |
| `netns_common.py` | утилиты для тестов (ns_exec, счётчики правил, JSONL-логи) |
| `run_tests.py` | прогон unittest + JSON-отчёт |
| `test_netns_stand.py` | 8 интеграционных тестов стенда |
## Что проверяют тесты (`test_netns_stand.py`)
1. `test_01` — netns и veth-пары созданы, адреса выданы, `ip_forward=1`.
2. `test_02` — policy routing: `ip rule fwmark 51820` → `table 51820` → аплинк.
3. `test_03` — правила меток (OUTPUT/FORWARD) и DROP kill-switch на месте.
4. `test_04` — мок-endpoint и детектор утечки слушают и залогировали `listen`.
5. `test_05` — handshake initiation из LAN доходит до мока, мок отвечает
(148 → 92 байта), счётчик правила метки растёт.
6. `test_06` — то же для трафика, инициированного самим шлюзом (OUTPUT-метка).
7. `test_07` — режим `drop` мока имитирует недоступность провайдера: ответа нет,
пакеты фиксируются; возврат в `respond` восстанавливает стенд.
8. `test_08` — kill-switch: при выключенном правиле трафик доходит до детектора
(положительный контроль), при включённом — утечки нет.
## Параметры (переменные окружения)
| Переменная | По умолчанию | Назначение |
|---|---|---|
| `AWG_NETNS_PREFIX` | `awgt21` | префикс имён namespace/veth |
| `AWG_NS_CLIENT` / `AWG_NS_GW` / `AWG_NS_UPLINK` | `awgt21-*` | имена namespace |
| `AWG_MOCK_PORT` | `51820` | порт мока WireGuard-endpoint |
| `AWG_LEAK_IP` / `AWG_LEAK_PORT` | `198.51.100.9` / `9999` | адрес и порт детектора утечки |
| `AWG_FWMARK` / `AWG_ROUTE_TABLE` | `51820` / `51820` | метка и таблица policy routing |
| `AWG_FW_BACKEND` | `auto` | `nft`, `iptables` или автоопределение |
| `AWG_NETNS_WORKDIR` | `mktemp -d` | каталог логов и отчёта стенда |
| `AWG_NS_EXEC_MODE` | `auto` | вход в netns: `ip`, `nsenter` или `auto` (проба) — issue #56 |
## Pitfalls
* **`ip netns add` в контейнере** требует `--privileged` (CAP_SYS_ADMIN) — иначе
`run.sh` завершится кодом 3 с сообщением «заблокировано окружением».
* **`ip netns exec` и `ip -n <ns>` в контейнере не работают** (iproute2 6.x
перемонтирует `/sys`: `mount of /sys failed: Operation not permitted`, rc=255),
хотя `ip netns add` и `nsenter --net=...` работают. Поэтому в скриптах только
`ns_exec`/`ns_prefix` (`lib.sh`) и `ns_exec_mode()` (`netns_common.py`), а все
`ip -n <ns>` заменены на `ns_exec <ns> ip ...` (issue #56). Форсировать режим:
`AWG_NS_EXEC_MODE=nsenter|ip`.
* **rp_filter** в шлюзе выставлен в `2` (loose): маркированная политика даёт
асимметрию reverse-пути, strict-режим молча дропает ответы.
* **Мок не умеет криптографию**: он повторяет раскладку сообщений WireGuard
(тип и длины), но не выполняет Noise IK handshake. Годится для топологии,
меток, policy routing и fail-closed — **не** для проверки шифрования, MTU и
пропускной способности.
* **`nft` в alpine-образе нет**: в контейнере используется `iptables`
(nft-backend). Стенд поддерживает оба backend; при отладке форсируйте
`AWG_FW_BACKEND=iptables`.
* **`ping` в образе нет** (пакет `iputils` не установлен) — тесты не используют
ICMP: связность проверяется UDP-трафиком через мок и детектор утечки.
* **Остатки после падения**: `bash tests/netns/topology.sh down` и `ip netns list`.