# 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) 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 — оба). Код возврата `3` означает «заблокировано окружением»: нет прав на создание netns. ## Состав | Файл | Назначение | |---|---| | `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` | каталог логов и отчёта стенда | ## Pitfalls * **`ip netns add` в контейнере** требует `--privileged` (CAP_SYS_ADMIN) — иначе `run.sh` завершится кодом 3 с сообщением «заблокировано окружением». * **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`.