春江暮客

春江暮客的个人学习分享网站

Codex 的 bwrap loopback 报错:Ubuntu 24.04 上的 AppArmor 排查记录

2026-10-09 技术
Codex 的 bwrap loopback 报错:Ubuntu 24.04 上的 AppArmor 排查记录

这次排查从一句报错开始:

bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted

当时在 Ubuntu 24.04.5 LTS 上运行 Codex CLI 0.162.0,连 pwd 都无法通过沙箱执行。后面的对照测试把问题指向了 AppArmor:临时放宽非特权用户命名空间限制后,命令执行成功;恢复限制后,沙箱又失败。最后为系统 bwrap 加载专用配置,再次得到了成功执行的输出。

本文整理自这次 ChatGPT 排查对话,并补充发布时核对的官方文档。原始记录验证了功能恢复,没有验证完整安全策略;下面把案例结果和供读者尝试的修复路径分开说明。用户名、工作目录和会话 ID 已省略,示例输出中的目录使用 /work/project。

报错停在什么地方

bwrap 是 bubblewrap 的命令名。这里失败的是沙箱初始化中的 loopback 地址配置。bubblewrap 的源码显示,loopback_setup() 通过 Netlink 的 RTM_NEWADDR 请求向 lo 添加 127.0.0.1/8,失败时输出这条错误。

因此,先检查进程有没有完成这一步的权限。错误本身还不能确定是 AppArmor、容器外层策略,还是其他限制;它也不说明需要修改宿主机的网卡或 loopback 地址。

先收集环境,再看拒绝日志

在发生报错的 Linux 环境中运行:

cat /etc/os-release
codex --version
systemd-detect-virt
id
sysctl kernel.unprivileged_userns_clone user.max_user_namespaces
sysctl kernel.apparmor_restrict_unprivileged_userns
grep -E 'CapEff|Seccomp|NoNewPrivs' /proc/self/status
command -v bwrap

这次对话里的关键结果如下:

检查项 记录中的结果 能说明什么
系统 Ubuntu 24.04.5 LTS 需要考虑该发行版的 AppArmor 规则
Codex 0.162.0,standalone 安装 这是案例版本,不代表所有版本行为相同
虚拟化检测 none 该工具没有识别出虚拟化环境
kernel.unprivileged_userns_clone 1 此开关允许非特权用户命名空间
user.max_user_namespaces 4126513 配额没有被设为零
AppArmor userns 限制 1 限制已启用,还要查日志确认是否相关
当前检查进程 CapEff=0、NoNewPrivs=0、Seccomp=0 只是检查进程的状态,不能代表后续子进程

普通用户的 CapEff=0 不足以解释这次失败。用户命名空间内的能力还要受到对应命名空间和安全策略的约束。Ubuntu 24.04 的默认策略可以允许命名空间创建,同时拒绝其内部后续使用 capabilities;这解释了为什么 userns 开关已打开,网络配置仍可能失败。Ubuntu 24.04 发布说明

紧接着复现一次报错,再查同一时间段的内核日志:

sudo journalctl -k --since "5 minutes ago" \
  | grep -Ei 'apparmor|DENIED|userns|bwrap'

对话中讨论的关键日志字段是:

apparmor="DENIED" operation="capable" profile="unprivileged_userns" capability=12 capname="net_admin"

还提到了 setpcap 被拒绝。net_admin 与 loopback 配置最直接相关,但要核对时间、PID 和进程名,避免把其他程序的拒绝记录归给 Codex。仅看到系统开关等于 1,证据还不够。

让测试真正执行到沙箱命令

排查途中出现了另一条错误:

Not inside a trusted directory and --skip-git-repo-check was not specified.

这次运行被仓库检查提前拦住了,不能用来判断 bwrap 是否恢复。对于自己控制的测试目录,可以使用:

codex exec --skip-git-repo-check 'pwd'

--skip-git-repo-check 只处理这项仓库检查,不会授予 AppArmor 权限。这个案例中,临时放宽限制后,这条命令出现了实际工具执行记录;恢复限制后,再运行又出现 sandbox error。这组对照结果与拒绝日志一起,把故障指向了 AppArmor。

还要注意成功的标准:Codex 在执行失败时,也曾直接根据环境上下文回答工作目录。只有最终回复里出现路径不算通过;应检查工具记录里是否有 shell 命令和 succeeded,并核对其输出。

优先检查发行版提供的 bwrap 策略

这次最初在 PATH 中找不到 bwrap,但 Codex 输出了使用 bundled bubblewrap 的提示。安装系统 bubblewrap 后,相关沙箱错误仍然存在,说明安装可执行文件和加载正确策略是两件事。

发布时核对的官方 OpenAI 沙箱文档说明:Linux 上优先使用 PATH 中的 bwrap,并提供了 Ubuntu 24.04 加载额外 AppArmor profile 的步骤。下面是文档路径,不是原始对话已经验证过的配置。

先安装并确认系统版本:

sudo apt update
sudo apt install bubblewrap apparmor-profiles apparmor-utils
command -v bwrap
readlink -f "$(command -v bwrap)"
bwrap --version

安装包之后,先检查现有策略和额外策略文件:

sudo aa-status
sudo grep -R -n -E 'bwrap|bubblewrap' /etc/apparmor.d
ls -l /usr/share/apparmor/extra-profiles/bwrap-userns-restrict

包版本和本地规则会影响结果。如果目标文件已经存在,先阅读并备份;不要直接覆盖已有定制策略,也不要让多个 profile 竞争同一可执行路径。如果额外策略文件不存在,应先核对包来源和版本。

确认没有现有冲突,且源文件存在时,再按官方文档复制并加载:

sudo install -m 0644 \
  /usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
  /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict

随后确认限制值和 profile 状态,再执行同一个测试:

sysctl kernel.apparmor_restrict_unprivileged_userns
sudo aa-status | grep -i bwrap
codex exec --skip-git-repo-check 'pwd'

如果仍失败,重新读取内核日志。尤其检查是否还落在 profile="unprivileged_userns",以及实际调用的是哪个 bwrap。PATH 中找到一个文件,只能说明它是候选程序,不能独立证明每次执行都匹配到了预期策略。

原始案例怎样恢复,以及它的边界

对话最后确认系统 bwrap 路径为 /usr/bin/bwrap。在没有看到专用 bwrap profile 的情况下,创建并加载了如下宽松测试配置:

abi <abi/4.0>,

include <tunables/global>

profile bwrap /usr/bin/bwrap flags=(unconfined) {
    userns,

    capability net_admin,
    capability setpcap,
}

配置文件是 /etc/apparmor.d/usr.bin.bwrap。加载后,记录中再次运行 codex exec --skip-git-repo-check 'pwd',得到了下面的工具执行结果,目录已替换为示例值:

sandbox: read-only

/usr/bin/zsh -lc pwd
succeeded in 0ms:
/work/project

这证明该案例中的命令恢复了执行。flags=(unconfined) 是宽松模式;配置中写了两项 capability,不能把它理解为只允许这两项能力的最小权限白名单。规则匹配 /usr/bin/bwrap,也不限于 Codex 的某一次调用。Ubuntu 对这种模式的说明见发布说明。

原始步骤包含把全局限制恢复为 1,但最后一次成功输出之后,没有再贴出 sysctl 和 profile 状态。因此,可以报告功能恢复,不能据此宣称所有安全设置已经复核、配置足够严格,或后续所有任务都会通过。

如果机器已经使用这个自定义配置,改用发行版策略前应先备份、卸载旧的测试 profile,并把旧文件移出 /etc/apparmor.d,再检查新策略的附着路径。不要并行叠加两份配置来碰运气。

临时诊断要保存并恢复原值

原始对话用过 kernel.apparmor_restrict_unprivileged_userns=0 做对照实验。这个设置影响整台主机,应只在自己管理、允许短时变更的测试环境中使用。优先尝试现有发行版策略;如果仍需要这个实验,先保存原值,再保证退出时恢复。

下面的脚本用于 Bash。它恢复读取到的原值,不把原始配置一律假设为 1:

#!/usr/bin/env bash
set -euo pipefail

sudo -v
original_userns=$(sysctl -n kernel.apparmor_restrict_unprivileged_userns)

restore_userns() {
  sudo sysctl -w "kernel.apparmor_restrict_unprivileged_userns=${original_userns}"
}
trap restore_userns EXIT
trap 'exit 130' INT
trap 'exit 143' TERM

sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
codex exec --skip-git-repo-check 'pwd'

退出 trap 无法处理断电或 SIGKILL,恢复命令也可能失败。结束后仍需手动检查 sysctl kernel.apparmor_restrict_unprivileged_userns,必要时恢复此前记录的值。不要把诊断用的 0 写成持久 sysctl 配置。

修复后验证同一条命令,再扩大测试

先保持同一个用户、目录和 Codex 沙箱模式,重复原来的 pwd 测试。确认工具执行成功,确认预期 AppArmor profile 已加载,确认全局开关处于预期值,并检查相同时间段是否还有相关拒绝日志。再测试实际项目需要的读取、写入或联网操作;pwd 成功只覆盖最小执行路径。

如果在 Docker 或 LXC 内遇到类似错误,需要另外检查外层容器限制。本次 systemd-detect-virt 返回 none 的记录不能推广到容器环境。当前检查进程的 Seccomp=0 同样不能证明后续所有子进程都不受限制。

这个案例里最容易误判的两步,是把仓库检查停止当成沙箱通过,以及把模型回答的目录当成命令执行结果。沿着原始报错、对应拒绝日志和真实工具输出核对,才能确认修复究竟改变了哪一步。

标签

1024 12306 ablang adsense agents.md ai ai-agent ai-agents ai-seo algorithm amp antibodies apparmor automation batch-processing binarycif bioinformatics blockchain boltz bootstrapping boxes bubblewrap c-index cca cdn chatgpt checkpoint cli cloudflare codex cofoldarena copy cpu监控 csv cuda curl data-leakage data-processing data-quality data-validation datascience datavisualization deployment desktop-app devtools disown docker dovecot download electron esm2 esm3 esmc esmfold2 faceswap fasta fastmcp ffmpeg file-io flashppi flask folium frontend game generator git github-actions google google-research grep harness hls html http hugo indexnow javascript jev json just k-means kaggle langfuse leecode linux list litellm llm llms.txt logging logs lollipop m3u8 machine-learning macos manacher matplotlib mcp mirror mmcif model-evaluation mp3 mp4 mpnn multiomics mutation mysql nanobert nanobodies networkx nginx normalize ollama omegatherm pandas password pdb pep-723 phaser pillow pip postfix preprocessing print protein-design protein-interactions protein-language-models protein-stability protein-structure proxy pydantic pyecharts pyqt python python3 r raincloud reproducibility requests reservoir-sampling rfoptimization rg ripgrep rosettafold3 roundcube rrsi rsi rsync s-tui sampling scale scikit-learn scrapy screen seaborn security selenium seo sha256 sklearn solana somaticsignatures spl sqlite ssh standardize static-site subprocess system-one tensorflow tkinter tron tronpy turtle typesafe-ai ubuntu usdt uv vhhbert vite webp wordcloud wordpress workflow yaml 后台 寓言 概率 经济 贸易 迅雷解析 钱包

友情链接

其它