bug(#21/#17): make test-netns-ci падает в контейнере — ip netns exec: "mount of /sys failed: Operation not permitted" #56

Closed
opened 2026-10-28 21:07:45 +03:00 by embeddevio · 3 comments
Owner

Найдено QA-ревью PR #53 (ветка feat/21-dev-stand, HEAD f5e8272c). Контейнерный путь netns-стенда — единственный артефакт PR, который не работает; хостовый путь (make test-netns) проходит 8/8.

Шаги воспроизведения

На Docker-хосте 192.168.1.147 (Docker 29.4.1, kernel 6.17.13-2-pve, iproute2 6.11.0 в образе awg-dash:dev), дерево = HEAD ветки:

cd /opt/awg-dash-dev/repo && make test-netns-ci

Фактический результат

Container awg-dash-test-netns-run-5f9508326970 Created
[netns] рабочий каталог: /tmp/awg-netns
[netns] создаю netns: client=awgt21-client gw=awgt21-gw uplink=awgt21-uplink
mount of /sys failed: Operation not permitted
make: *** [Makefile:110: test-netns-ci] Error 255
  • exit 255 (сырой код ip), а не документированные коды стенда 0/1/3/4 — run.sh не различает «заблокировано окружением» и «стенд сломан».
  • docker-compose.test.yml (make test-netns-ci) заявлен в PR как «путь для CI (#17)», т.е. именно его вызовет workflow из #52/#17.

Ожидаемый результат

8/8 тестов в контейнере, либо явный код 3 «заблокировано окружением» с понятным сообщением.

Диагноз (проверен на хосте)

Отказывает не ip netns add, а последующий ip netns exec: iproute2 6.11 при входе в namespace перемонтирует /sys, а в контейнере это EPERM:

проба (в awg-dash:dev) результат
--privileged + ip netns add OK
--privileged + ip netns exec mount of /sys failed: Operation not permitted
--privileged --security-opt apparmor=unconfined --security-opt seccomp=unconfined + exec та же ошибка
--privileged --volume /sys:/sys + exec та же ошибка
--cap-add SYS_ADMIN,NET_ADMIN --volume /sys:/sys + exec та же ошибка
--privileged + ip netns add + nsenter --net=/var/run/netns/<ns> OK

То есть privileged: true в deploy/docker-compose.test.yml сам по себе недостаточен: проблема в ip netns exec, а не в правах.

Варианты исправления

  1. В харнессе (tests/netns/lib.sh, tests/netns/netns_common.py) ходить в namespace через nsenter --net=/var/run/netns/<ns> вместо ip netns exec — проба выше проходит; проверено, что nsenter есть в образе.
  2. Либо честно объявить контейнерный путь неподдерживаемым на таких ядрах: preflight ловит ip netns exec → exit 3 с сообщением, а CI (#17) вызывает стенд на хосте.
  3. В любом случае — привести коды выхода test-netns-ci к документированным (0/1/3/4).

Критерии закрытия

  • make test-netns-ci на 192.168.1.147 даёт 8 ok / 0 fail/err/skip и ip netns list пуст после прогона, или код 3 с внятным сообщением и записью в deploy/VERIFICATION-21.md, что контейнерный путь отключён.
  • deploy/VERIFICATION-21.md содержит реальный прогон этого таргета (сейчас в отчёте есть только make test-netns на хосте).

Refs #21, Refs #15, связано с #17 (runner/CI)

Найдено QA-ревью PR #53 (ветка `feat/21-dev-stand`, HEAD `f5e8272c`). Контейнерный путь netns-стенда — единственный артефакт PR, который **не работает**; хостовый путь (`make test-netns`) проходит 8/8. ## Шаги воспроизведения На Docker-хосте `192.168.1.147` (Docker 29.4.1, kernel 6.17.13-2-pve, iproute2 6.11.0 в образе `awg-dash:dev`), дерево = HEAD ветки: ```bash cd /opt/awg-dash-dev/repo && make test-netns-ci ``` ## Фактический результат ``` Container awg-dash-test-netns-run-5f9508326970 Created [netns] рабочий каталог: /tmp/awg-netns [netns] создаю netns: client=awgt21-client gw=awgt21-gw uplink=awgt21-uplink mount of /sys failed: Operation not permitted make: *** [Makefile:110: test-netns-ci] Error 255 ``` - exit 255 (сырой код `ip`), а не документированные коды стенда 0/1/3/4 — `run.sh` не различает «заблокировано окружением» и «стенд сломан». - `docker-compose.test.yml` (`make test-netns-ci`) заявлен в PR как «путь для CI (#17)», т.е. именно его вызовет workflow из #52/#17. ## Ожидаемый результат 8/8 тестов в контейнере, либо явный код 3 «заблокировано окружением» с понятным сообщением. ## Диагноз (проверен на хосте) Отказывает не `ip netns add`, а последующий `ip netns exec`: iproute2 6.11 при входе в namespace перемонтирует `/sys`, а в контейнере это `EPERM`: | проба (в `awg-dash:dev`) | результат | |---|---| | `--privileged` + `ip netns add` | **OK** | | `--privileged` + `ip netns exec` | `mount of /sys failed: Operation not permitted` | | `--privileged --security-opt apparmor=unconfined --security-opt seccomp=unconfined` + `exec` | та же ошибка | | `--privileged --volume /sys:/sys` + `exec` | та же ошибка | | `--cap-add SYS_ADMIN,NET_ADMIN --volume /sys:/sys` + `exec` | та же ошибка | | `--privileged` + `ip netns add` + **`nsenter --net=/var/run/netns/<ns>`** | **OK** | То есть `privileged: true` в `deploy/docker-compose.test.yml` сам по себе недостаточен: проблема в `ip netns exec`, а не в правах. ## Варианты исправления 1. В харнессе (`tests/netns/lib.sh`, `tests/netns/netns_common.py`) ходить в namespace через `nsenter --net=/var/run/netns/<ns>` вместо `ip netns exec` — проба выше проходит; проверено, что `nsenter` есть в образе. 2. Либо честно объявить контейнерный путь неподдерживаемым на таких ядрах: preflight ловит `ip netns exec` → exit 3 с сообщением, а CI (#17) вызывает стенд на хосте. 3. В любом случае — привести коды выхода `test-netns-ci` к документированным (0/1/3/4). ## Критерии закрытия - `make test-netns-ci` на `192.168.1.147` даёт 8 ok / 0 fail/err/skip и `ip netns list` пуст после прогона, **или** код 3 с внятным сообщением и записью в `deploy/VERIFICATION-21.md`, что контейнерный путь отключён. - `deploy/VERIFICATION-21.md` содержит реальный прогон этого таргета (сейчас в отчёте есть только `make test-netns` на хосте). Refs #21, Refs #15, связано с #17 (runner/CI)
embeddevio added the devopsqa labels 2026-10-28 21:07:45 +03:00
Author
Owner

Исправлено в PR #53 — нужен повторный прогон qa

Диагноз подтверждён

Воспроизведено на awg-dash:dev (iproute2 6.11.0, privileged):

команда в контейнере результат
ip netns add <ns> rc=0
ip netns exec <ns> true mount of /sys failed: Operation not permitted, rc=255
ip -n <ns> link set lo up mount of /sys failed: Operation not permitted, rc=255
nsenter --net=/var/run/netns/<ns> true rc=0

Важно: падает не только ip netns exec, но и ip -n <ns> — поэтому одной замены на nsenter в раннере мало, пришлось убрать ip -n и из topology.sh/netns_common.py.

Что сделано

  • ns_exec() / ns_prefix() (tests/netns/lib.sh) и ns_exec_mode()/ns_exec_direct() (tests/netns/netns_common.py): один раз проба ip netns exec, при отказе — nsenter --net=/var/run/netns/<ns>; режим форсируется AWG_NS_EXEC_MODE=auto|ip|nsenter (проброшен в deploy/docker-compose.test.yml).
  • Все ip -n "$NS" в topology.sh → ns_exec "$NS" ip ...; addrs()/links()/link_is_up() → через ns_sh().
  • Снос топологии: ip netns delete вызывается безусловно и результат проверяется до 5 раз — в контейнере удаление иногда «терялось», из-за чего run.sh отдавал код 4 на зелёных тестах (флак 1 из 5).
  • Коды возврата сведены к документированным 0/1/3/4: trap ... ERR в run.sh/topology.sh нормализует непредвиденные коды утилит (255) в 4; ошибки мока/sink — тоже 4; нет прав — 3; упавшие тесты — 1.

Проверено (дерево ветки, 22/22 blob-SHA, хост 192.168.1.147)

сценарий результат
make test-netns-ci exit 0, вход в netns: nsenter, backend iptables, 8 ok / 0 fail, топология снесена
5 прогонов подряд 5/5 exit 0, ни одного «остался netns»
без --privileged exit 3 ([blocked] не удалось создать netns …)
AWG_NS_EXEC_MODE=ip в контейнере exit 4 (mount of /sys failed → 255 нормализован в 4)
синтетический проваленный тест exit 1
make test-netns (хост) exit 0, 8 ok

DoD «тесты ≥70 %» (второй блокер ревью)

tests/deploy/pytest.ini + .coveragerc (свой профиль для стенда), тесты на api_stub.py (9) и негативные на validate_compose.py (39), make test-deploy: 51 passed, coverage 98.86 %, RC=0 без --no-cov. Детали — deploy/VERIFICATION-21.md §6.

Оставляю issue открытым до повторного прогона qa-engineer. Merge — за TechLead (ADVISORY).

Refs #21, Refs #56

## Исправлено в PR #53 — нужен повторный прогон qa ### Диагноз подтверждён Воспроизведено на `awg-dash:dev` (iproute2 6.11.0, privileged): | команда в контейнере | результат | |---|---| | `ip netns add <ns>` | rc=0 | | `ip netns exec <ns> true` | `mount of /sys failed: Operation not permitted`, rc=255 | | `ip -n <ns> link set lo up` | `mount of /sys failed: Operation not permitted`, rc=255 | | `nsenter --net=/var/run/netns/<ns> true` | rc=0 | Важно: падает не только `ip netns exec`, но и `ip -n <ns>` — поэтому одной замены на `nsenter` в раннере мало, пришлось убрать `ip -n` и из `topology.sh`/`netns_common.py`. ### Что сделано - `ns_exec()` / `ns_prefix()` (`tests/netns/lib.sh`) и `ns_exec_mode()`/`ns_exec_direct()` (`tests/netns/netns_common.py`): один раз проба `ip netns exec`, при отказе — `nsenter --net=/var/run/netns/<ns>`; режим форсируется `AWG_NS_EXEC_MODE=auto|ip|nsenter` (проброшен в `deploy/docker-compose.test.yml`). - Все `ip -n "$NS"` в `topology.sh` → `ns_exec "$NS" ip ...`; `addrs()`/`links()`/`link_is_up()` → через `ns_sh()`. - Снос топологии: `ip netns delete` вызывается безусловно и результат проверяется до 5 раз — в контейнере удаление иногда «терялось», из-за чего `run.sh` отдавал код 4 на зелёных тестах (флак 1 из 5). - Коды возврата сведены к документированным `0/1/3/4`: `trap ... ERR` в `run.sh`/`topology.sh` нормализует непредвиденные коды утилит (255) в 4; ошибки мока/sink — тоже 4; нет прав — 3; упавшие тесты — 1. ### Проверено (дерево ветки, 22/22 blob-SHA, хост 192.168.1.147) | сценарий | результат | |---|---| | `make test-netns-ci` | **exit 0**, `вход в netns: nsenter`, backend iptables, 8 ok / 0 fail, топология снесена | | 5 прогонов подряд | **5/5 exit 0**, ни одного «остался netns» | | без `--privileged` | **exit 3** (`[blocked] не удалось создать netns …`) | | `AWG_NS_EXEC_MODE=ip` в контейнере | **exit 4** (`mount of /sys failed` → 255 нормализован в 4) | | синтетический проваленный тест | **exit 1** | | `make test-netns` (хост) | exit 0, 8 ok | ### DoD «тесты ≥70 %» (второй блокер ревью) `tests/deploy/pytest.ini` + `.coveragerc` (свой профиль для стенда), тесты на `api_stub.py` (9) и негативные на `validate_compose.py` (39), `make test-deploy`: **51 passed, coverage 98.86 %**, RC=0 без `--no-cov`. Детали — `deploy/VERIFICATION-21.md` §6. Оставляю issue открытым до повторного прогона qa-engineer. Merge — за TechLead (ADVISORY). Refs #21, Refs #56
Author
Owner

Критерии закрытия #56 выполнены (раунд 3, PR #53)

Дерево ветки feat/21-dev-stand @ a3396b2b, прогоны на 192.168.1.147.

  1. Код контейнера 0/1/3/4 доходит до пайплайна без make — точка входа tests/netns/ci.sh:
    bash tests/netns/ci.sh → 0 (8 ok, nsenter); AWG_NS_EXEC_MODE=ip … → 4; AWG_NS_PRIVILEGED=0 … → 3;
    синтетический падающий тест → 1. 2 у ci.sh означает сломанный вызов/нет docker/образа — не путается с 3.
  2. Контракт через make зафиксирован — make test-netns-ci при контейнерных 4 и 1 возвращает 2
    (make: *** Error 4/1), правило описано в deploy/VERIFICATION-21.md §6.1 и в комментарии таргета Makefile.
  3. AWG_NS_EXEC_MODE работает как документирован — NETNS_EXEC_MODE ?= $(if $(AWG_NS_EXEC_MODE),$(AWG_NS_EXEC_MODE),auto);
    AWG_NS_EXEC_MODE=ip make test-netns-ci → [ci] … режим netns=ip, код 4.

Гейт покрытия по DoD не отменялся: make test-deploy → 51 passed, coverage 98.86 % без --no-cov.
Отчёт: deploy/VERIFICATION-21.md §7, лог прогонов /opt/awg-dash-dev/verify-r3-final-1793213957.log.

Если со стороны QA возражений нет — #56 можно закрывать; финальное решение по merge PR #53 за TechLead.

Refs #21

## Критерии закрытия #56 выполнены (раунд 3, PR #53) Дерево ветки `feat/21-dev-stand` @ `a3396b2b`, прогоны на `192.168.1.147`. 1. **Код контейнера `0/1/3/4` доходит до пайплайна без make** — точка входа `tests/netns/ci.sh`: `bash tests/netns/ci.sh` → 0 (8 ok, nsenter); `AWG_NS_EXEC_MODE=ip …` → 4; `AWG_NS_PRIVILEGED=0 …` → 3; синтетический падающий тест → 1. `2` у `ci.sh` означает сломанный вызов/нет docker/образа — не путается с 3. 2. **Контракт через make зафиксирован** — `make test-netns-ci` при контейнерных 4 и 1 возвращает 2 (`make: *** Error 4/1`), правило описано в `deploy/VERIFICATION-21.md` §6.1 и в комментарии таргета Makefile. 3. **`AWG_NS_EXEC_MODE` работает как документирован** — `NETNS_EXEC_MODE ?= $(if $(AWG_NS_EXEC_MODE),$(AWG_NS_EXEC_MODE),auto)`; `AWG_NS_EXEC_MODE=ip make test-netns-ci` → `[ci] … режим netns=ip`, код 4. Гейт покрытия по DoD не отменялся: `make test-deploy` → 51 passed, coverage 98.86 % без `--no-cov`. Отчёт: `deploy/VERIFICATION-21.md` §7, лог прогонов `/opt/awg-dash-dev/verify-r3-final-1793213957.log`. Если со стороны QA возражений нет — #56 можно закрывать; финальное решение по merge PR #53 за TechLead. Refs #21
Author
Owner

Закрыто — критерии выполнены (QA, раунд 3 ревью PR #53)

Дерево = head a3396b2b, взято архивом Gitea по точному SHA и прогнано на 192.168.1.147 в отдельном каталоге (не копия автора).

критерий #56 проверка результат
код контейнера доходит до пайплайна без make bash tests/netns/ci.sh 0 (Ran 8 tests, 8 ok, 5.52 s, вход в netns: nsenter)
код 4 AWG_NS_EXEC_MODE=ip bash tests/netns/ci.sh 4 (mount of /sys failed → [warn] код 255 → 4)
код 3 AWG_NS_PRIVILEGED=0 bash tests/netns/ci.sh 3 ([blocked] нет CAP_NET_ADMIN/SYS_ADMIN)
код 1 синтетический падающий тест в смонтированном дереве 1 (9 tests, 8 ok, 1 fail)
контракт через make зафиксирован AWG_NS_EXEC_MODE=ip make test-netns-ci контейнер 4, make: *** Error 4, make=2; описано в VERIFICATION-21.md §6.1
DoD «тесты ≥70 %» без обхода гейта python3 -m pytest tests/deploy -c tests/deploy/pytest.ini (без --no-cov) 51 passed, coverage 98.86 %, rc=0
AWG_NS_EXEC_MODE приоритетнее make-алиаса лог [netns] вход в netns: ip подтверждено

Побочно проверено: make test-netns — 8 ok / 0 fail (5.46 s); shellcheck и validate_compose — rc=0; после прогонов ip netns list пуст, probe-контейнеров не осталось.

Блокирующих дефектов нет. Закрываю. Детали ревью — PR #53 issuecomment-701. Остаётся внешний пункт DoD «CI green» (#52/#17): на момент проверки CI красный repo-wide — в run 37 падают все шаги, включая checkout.

## Закрыто — критерии выполнены (QA, раунд 3 ревью PR #53) Дерево = head `a3396b2b`, взято **архивом Gitea по точному SHA** и прогнано на `192.168.1.147` в отдельном каталоге (не копия автора). | критерий #56 | проверка | результат | |---|---|---| | код контейнера доходит до пайплайна без make | `bash tests/netns/ci.sh` | **0** (`Ran 8 tests`, 8 ok, 5.52 s, `вход в netns: nsenter`) | | код 4 | `AWG_NS_EXEC_MODE=ip bash tests/netns/ci.sh` | **4** (`mount of /sys failed` → `[warn] код 255 → 4`) | | код 3 | `AWG_NS_PRIVILEGED=0 bash tests/netns/ci.sh` | **3** (`[blocked] нет CAP_NET_ADMIN/SYS_ADMIN`) | | код 1 | синтетический падающий тест в смонтированном дереве | **1** (`9 tests, 8 ok, 1 fail`) | | контракт через make зафиксирован | `AWG_NS_EXEC_MODE=ip make test-netns-ci` | контейнер 4, `make: *** Error 4`, **make=2**; описано в `VERIFICATION-21.md` §6.1 | | DoD «тесты ≥70 %» без обхода гейта | `python3 -m pytest tests/deploy -c tests/deploy/pytest.ini` (без `--no-cov`) | **51 passed**, coverage **98.86 %**, rc=0 | | `AWG_NS_EXEC_MODE` приоритетнее make-алиаса | лог `[netns] вход в netns: ip` | подтверждено | Побочно проверено: `make test-netns` — 8 ok / 0 fail (5.46 s); `shellcheck` и `validate_compose` — rc=0; после прогонов `ip netns list` пуст, probe-контейнеров не осталось. Блокирующих дефектов нет. Закрываю. Детали ревью — PR #53 issuecomment-701. Остаётся внешний пункт DoD «CI green» (#52/#17): на момент проверки CI красный repo-wide — в run 37 падают все шаги, включая `checkout`.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: embeddevio/awg-dash#56