- Зачем:
- ретро прогона 2026-07-12 (задача 20, clickstream-demo): контекст
оркестратора съедали инлайн-диагнозы, переписывание boilerplate и
кириллица в обменных файлах.
- Что:
- SKILL.md: принцип «мастер решает — субагенты выясняют» со стоп-краном
«сам — максимум один заход», обменные файлы на английском, сквозная
проверка до ревьюеров, будильник одной строкой.
- references/prompts/: готовые английские boilerplate-файлы (мандат
ревью, триаж, перепроверка) — подшиваются cat'ом, контекст мастера
не проходят; prompt-templates перестроен под сборку.
- codex-exec: модель gpt-5.6-sol, лестница эффорта, ловушка «&»;
sandbox-справочник переименован в codex-sandbox-apparmor.md;
lessons дистиллирован, добавлена запись прогона 2026-07-12.
- закрыты 14 находок слепого Fable-ревью скилла (согласованность,
единый источник $SCRATCH, скелет ревью постановки).
- Проверка:
- все внутренние ссылки резолвятся; слепое ревью: APPROVED_WITH_MINORS,
восстановление структуры подтверждено без потерь.
121 lines
7.5 KiB
Markdown
121 lines
7.5 KiB
Markdown
# Sandbox Codex на Linux: диагностика и починка (bwrap/AppArmor)
|
||
|
||
Справочник: симптомы, корень и рецепты фикса поломки песочницы Codex CLI
|
||
на Linux с AppArmor (эмпирика Ubuntu 24.04, 2026-07-10). Самодостаточен.
|
||
|
||
## Симптом
|
||
|
||
Любая команда внутри sandbox Codex падает **до запуска процесса**, даже `pwd`:
|
||
|
||
```
|
||
bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted
|
||
```
|
||
|
||
Проявляется и в read-only, и в workspace-write. Codex не может ни читать файлы через shell, ни выполнять команды — задачи либо виснут, либо честно завершаются отказом.
|
||
|
||
## Корневая причина (подтверждена kernel audit log)
|
||
|
||
**Codex CLI 0.144.1 (npm) сменил Linux-sandbox backend**: раньше был Landlock/seccomp без user namespaces, теперь — vendored bubblewrap (`…/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex-resources/bwrap`), который создаёт unprivileged user namespace + network namespace.
|
||
|
||
Ubuntu 24.04 с `kernel.apparmor_restrict_unprivileged_userns = 1` загоняет такой userns в запретительный AppArmor-профиль `unprivileged_userns`, который блокирует два шага, нужных bwrap: запись `/proc/PID/uid_map` и `RTM_NEWADDR` (поднятие loopback). Доказательство из audit log:
|
||
|
||
```
|
||
apparmor="AUDIT" operation="userns_create" comm="bwrap" profile="unconfined" target="unprivileged_userns"
|
||
apparmor="DENIED" operation="open" name="proc/…/uid_map" profile="unprivileged_userns" denied_mask="wr"
|
||
```
|
||
|
||
Важные отрицательные результаты (экономят время диагностики):
|
||
|
||
- **Плагин openai-codex (1.0.5 → 1.0.6) ни при чём** — он лишь передаёт режимы sandbox бинарю codex; даунгрейд плагина не помогает.
|
||
- Системный `/usr/bin/bwrap` падает так же — это не баг vendored-сборки.
|
||
- Система не менялась (машина без ребута 14 недель, политика AppArmor старая) — это дрейф самого Codex CLI, прилетевший с обновлением npm-пакета.
|
||
|
||
## Применённый фикс без sudo (обратимый)
|
||
|
||
В `~/.codex/config.toml` добавлено:
|
||
|
||
```toml
|
||
[features]
|
||
use_legacy_landlock = true
|
||
```
|
||
|
||
Backup рядом: `~/.codex/config.toml.bak-20260710`. Откат — восстановить backup или удалить две строки.
|
||
|
||
## Проверка (сквозная, через codex-companion)
|
||
|
||
- Read-only задача `task --wait --fresh` («pwd + первая строка /etc/os-release») — команды выполняются, вернула корректные значения.
|
||
- **web_search работает**: задача сходила на github.com/openai/codex/releases. Нюанс: сетевой доступ из *shell-команд внутри sandbox* по-прежнему закрыт (`npm view` виснет) — это корректный read-only режим; инструмент web_search исполняется вне sandbox и не страдает.
|
||
- Изоляция сохранена: запись вне workspace запрещена.
|
||
|
||
## Известное ограничение фикса
|
||
|
||
`use_legacy_landlock` чинит **только read-only**. Режим `--write`/workspace-write с этим флагом **паникует**:
|
||
|
||
```
|
||
permission profiles requiring direct runtime enforcement are incompatible with --use-legacy-landlock
|
||
```
|
||
|
||
— workspace-write требует именно bwrap-backend. Полный фикс — ниже, нужен sudo.
|
||
|
||
## Полный фикс (sudo; применён и проверен 2026-07-10)
|
||
|
||
**Ловушка, съевшая первую попытку**: профиль на путь vendored bwrap
|
||
(`**/codex-resources/bwrap`) НЕ работает — Codex ≥0.144 исполняет
|
||
**встроенный** bwrap из memfd (`linux-sandbox/src/bundled_bwrap.rs`,
|
||
`memfd_create` в бинаре), и путевой attachment не цепляется никогда: в audit
|
||
виден `profile="unconfined" comm="bwrap"` → transition в
|
||
`unprivileged_userns`. Прямой запуск vendored bwrap с диска при этом
|
||
проходит — ложное подтверждение, не верь ему.
|
||
|
||
**Рабочий вариант**: вешать профиль на **сам бинарь codex** (родителя) —
|
||
memfd-exec'нутый ребёнок наследует метку через exec, и правило `userns,`
|
||
разрешает namespace. Стандартный приём Ubuntu для браузеров/Electron.
|
||
`/etc/apparmor.d/codex-bwrap`:
|
||
|
||
```
|
||
abi <abi/4.0>,
|
||
include <tunables/global>
|
||
|
||
profile codex-cli /home/dmitry/.npm-global/lib/node_modules/@openai/codex/**/bin/codex flags=(unconfined) {
|
||
userns,
|
||
include if exists <local/codex-cli>
|
||
}
|
||
|
||
profile codex-bwrap /home/dmitry/.npm-global/lib/node_modules/@openai/codex/**/codex-resources/bwrap flags=(unconfined) {
|
||
userns,
|
||
include if exists <local/codex-bwrap>
|
||
}
|
||
```
|
||
|
||
Загрузить: `sudo apparmor_parser -r /etc/apparmor.d/codex-bwrap`. После этого **убрать** `use_legacy_landlock` из config.toml и проверить оба режима:
|
||
|
||
```
|
||
codex sandbox -c 'sandbox_mode="workspace-write"' -- sh -c 'touch ./__p && rm ./__p && echo OK'
|
||
codex sandbox -- sh -c 'touch ./__p 2>/dev/null && echo BROKEN || echo RO_OK'
|
||
```
|
||
|
||
Проверено 2026-07-10: WRITE_OK, read-only блокирует запись, workspace-write
|
||
не пускает в `/etc` — изоляция сохранена.
|
||
|
||
Переносимость: путь в профиле — под конкретную машину (`~/.npm-global/...`); glob `**` переживает обновления версий Codex, но если npm-global живёт в другом месте — путь поправить. Перезагружать профиль нужно только при смене самого расположения.
|
||
|
||
**Вариант B — грубый, не рекомендуется**: `sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0` (+ файл в `/etc/sysctl.d/`). Ослабляет защиту всей машины.
|
||
|
||
## Диагностические команды для другой машины
|
||
|
||
```bash
|
||
codex --version # 0.144.1+ → bwrap-backend
|
||
sysctl kernel.apparmor_restrict_unprivileged_userns # 1 → будет блокировка
|
||
bwrap --ro-bind / / --unshare-net --dev /dev --proc /proc true && echo BWRAP_OK
|
||
sudo dmesg | grep -iE 'apparmor.*(userns|bwrap)' | tail # AUDIT/DENIED — подтверждение
|
||
```
|
||
|
||
## Связанные записи
|
||
|
||
Принесено 2026-07-10 из ветки `codex-sandbox-fix-20260710` (исходная машина —
|
||
Ubuntu 24.04; там сжатая версия жила в codex-companion.md, который в основной
|
||
линии уже поглощён codex-exec.md). Сжатая версия урока здесь — в
|
||
[codex-exec.md](codex-exec.md), сквозное правило «Linux: sandbox может молча
|
||
сломаться обновлением Codex»; датированная запись — в
|
||
[lessons.md](lessons.md).
|