news 2026/2/5 2:37:24

Bubblewrap:轻量级非特权容器运行时

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bubblewrap:轻量级非特权容器运行时

Bubblewrap:轻量级非特权容器运行时

项目标题与描述

Bubblewrap 是一个专注于为非特权用户提供沙箱和容器运行时的工具。与systemd-nspawnDocker等面向系统管理员和编排工具的传统容器运行时不同,Bubblewrap 的设计目标是安全地提供给普通用户使用,避免将此类访问权限转变为对主机的完全特权 root shell。

项目核心价值在于实现用户命名空间功能的子集,特别是当 Linux 内核的用户命名空间功能在某些生产发行版中尚未对非特权用户可用或存在安全顾虑时,Bubblewrap 可以作为其 setuid 实现方案。

功能特性

  • 非特权容器运行:允许非特权用户运行容器化应用,无需授予完整的 root 访问权限。
  • 最小权限原则:使用PR_SET_NO_NEW_PRIVS关闭 setuid 二进制文件的权限提升,限制容器内进程的权限。
  • 命名空间隔离:支持创建新的命名空间(如 PID、网络、挂载、IPC 等)进行环境隔离。
  • 绑定挂载控制:提供精细的绑定挂载功能,支持只读 (BIND_READONLY)、递归 (BIND_RECURSIVE) 等选项,灵活构建容器文件系统视图。
  • 叠加文件系统支持:通过--overlay--tmp-overlay--ro-overlay--overlay-src选项创建 overlay 挂载,实现高效的层叠文件系统。
  • 用户命名空间映射:可与newuidmapnewgidmap工具配合,实现容器内外用户/组 ID 的映射。
  • 网络命名空间管理:包含设置环回接口 (loopback_setup) 和更复杂的网络接口配置能力(通过rtnetlink)。
  • Seccomp 沙箱:支持通过 BPF 过滤器(如flatpak.bpf)进一步限制系统调用,增强安全性。
  • SELinux 集成:可选支持 SELinux 上下文设置(setexeccon,setfscreatecon),实现强制访问控制。
  • 详细的错误处理:定义清晰的错误码枚举(如BIND_MOUNT_ERROR_MOUNT),并提供die_with_bind_result等函数进行错误报告。

安装指南

项目使用 Meson 构建系统(要求 Meson ≥ 0.49.0),已移除了 Autotools 构建系统。

构建依赖

  • gcc 或 clang
  • libcap-dev
  • libselinux1-dev (可选,用于 SELinux 支持)
  • meson (≥ 0.49.0)
  • pkg-config

在 Debian/Ubuntu 系统上安装依赖

apt-getupdateapt-getinstallbuild-essential libcap-dev libselinux1-dev meson pkg-config

在 RHEL/CentOS/Fedora 系统上安装依赖

yuminstallgcc libcap-devel'pkgconfig(libselinux)'meson

从源码构建与安装

  1. 克隆代码仓库:git clone https://github.com/containers/bubblewrap.git
  2. 进入目录:cd bubblewrap
  3. 配置构建:meson setup build
  4. 编译:meson compile -C build
  5. 安装(可能需要root):sudo meson install -C build

注意:Bubblewrap 可以以 setuid root 模式安装,此时它旨在遵循与允许非特权用户创建新用户命名空间的内核相同的安全边界。若不设置为 setuid,则它本身不构成用户与操作系统之间的安全边界。

使用说明

Bubblewrap 通过命令行参数定义沙箱环境。其安全模型完全由调用者构建的命令行参数决定。

基础示例:复用主机 /usr,隔离其他目录
此示例创建一个共享主机/usr(只读)但拥有独立/tmp/home/var/run/etc的 shell 环境,并启用网络共享。

#!/bin/bash(exec bwrap --ro-bind /usr /usr\--dir /tmp\--dir /var\--symlink../tmp var/tmp\--proc /proc\--dev /dev\--ro-bind /etc/resolv.conf /etc/resolv.conf\--symlink usr/lib /lib\--symlink usr/lib64 /lib64\--symlink usr/bin /bin\--symlink usr/sbin /sbin\--chdir /\--unshare-all\--share-net\--die-with-parent\--dir /run/user/$(id-u)\--setenvXDG_RUNTIME_DIR"/run/user/`id-u`"\--setenvPS1"bwrap-demo$ "\--file11/etc/passwd\--file12/etc/group\/bin/sh)\11<<(getentpasswd$UID65534)\12<<(getent group$(id-g)65534)

运行 Flatpak 应用(如 GNOME Weather)
此示例展示了如何利用 Bubblewrap 手动构造一个类似 Flatpak 的运行时环境来启动一个应用。

#!/bin/bash(execbwrap\--ro-bind ~/.local/share/flatpak/runtime/org.gnome.Platform/x86_64/master/active/files /usr\--ro-bind ~/.local/share/flatpak/app/org.gnome.Weather/x86_64/master/active/files/ /app\--dev /dev\--proc /proc\--dir /tmp\--symlink /tmp /var/tmp\--symlink usr/lib /lib\--symlink usr/lib64 /lib64\--symlink usr/bin /bin\--symlink usr/sbin /sbin\--symlink usr/etc /etc\--dir /run/user/`id-u`\--ro-bind /etc/resolv.conf /run/host/monitor/resolv.conf\--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X99\--unshare-pid\--setenvXDG_RUNTIME_DIR"/run/user/`id-u`"\--setenvDISPLAY:99\--setenvPATH/app/bin:/usr/bin\--seccomp13\/bin/sh)13<`dirname$0`/flatpak.bpf

使用用户命名空间和 ID 映射
此 Python 脚本示例演示了如何结合newuidmap/newgidmap创建用户命名空间并进行 UID/GID 映射。

#!/usr/bin/env python3importos,select,subprocess,sys,json pipe_info=os.pipe()userns_block=os.pipe()pid=os.fork()ifpid!=0:# 父进程:设置映射os.close(pipe_info[1]);os.close(userns_block[0])select.select([pipe_info[0]],[],[])data=json.load(os.fdopen(pipe_info[0]))child_pid=str(data['child-pid'])subprocess.call(["newuidmap",child_pid,"0",str(os.getuid()),"1"])subprocess.call(["newgidmap",child_pid,"0",str(os.getgid()),"1"])os.write(userns_block[1],b'1')# 解除子进程阻塞else:# 子进程:启动 bwrapos.close(pipe_info[0]);os.close(userns_block[1])os.set_inheritable(pipe_info[1],True)os.set_inheritable(userns_block[0],True)args=["bwrap","--unshare-all","--unshare-user","--userns-block-fd","%i"%userns_block[0],"--info-fd","%i"%pipe_info[1],"--bind","/","/","cat","/proc/self/uid_map"]os.execlp(*args)

常用参数概览

  • --bind <src> <dest>: 绑定挂载目录或文件。
  • --ro-bind <src> <dest>: 只读绑定挂载。
  • --dev /dev: 挂载/dev设备目录。
  • --proc /proc: 挂载/proc文件系统。
  • --dir <dir>: 创建一个空目录。
  • --unshare-<namespace>: 不共享指定的命名空间(如 pid, net, ipc, uts)。
  • --share-net: 共享网络命名空间(与主机网络相同)。
  • --die-with-parent: 当父进程退出时终止沙箱。
  • --setenv <VAR> <value>: 设置环境变量。
  • --seccomp <fd>: 从文件描述符读取并加载 seccomp BPF 过滤器。
  • --overlay <upper> <work> <lower> <dest>: 创建 overlay 挂载。

核心代码

以下是 Bubblewrap 项目中几个核心模块的代码片段,展示了其关键实现。

1. 绑定挂载功能头文件 (bind-mount.h)

/* bubblewrap * Copyright (C) 2016 Alexander Larsson * SPDX-License-Identifier: LGPL-2.0-or-later */#pragmaonce#include"utils.h"/* 绑定挂载选项标志位 */typedefenum{BIND_READONLY=(1<<0),/* 只读挂载 */BIND_DEVICES=(1<<2),/* 允许创建设备节点 */BIND_RECURSIVE=(1<<3),/* 递归挂载 */}bind_option_t;/* 绑定挂载操作结果枚举 */typedefenum{BIND_MOUNT_SUCCESS=0,BIND_MOUNT_ERROR_MOUNT,/* mount() 系统调用失败 */BIND_MOUNT_ERROR_REALPATH_DEST,/* 解析目标路径失败 */BIND_MOUNT_ERROR_REOPEN_DEST,/* 重新打开目标路径失败 */BIND_MOUNT_ERROR_READLINK_DEST_PROC_FD,/* 读取 /proc/self/fd/ 链接失败 */BIND_MOUNT_ERROR_FIND_DEST_MOUNT,/* 查找目标挂载点失败 */BIND_MOUNT_ERROR_REMOUNT_DEST,/* 重新挂载目标失败 */BIND_MOUNT_ERROR_REMOUNT_SUBMOUNT,/* 重新挂载子挂载点失败 */}bind_mount_result;/* 执行绑定挂载的核心函数 * @proc_fd: 指向 /proc 目录的文件描述符 * @src: 源路径 * @dest: 目标路径 * @options: 绑定选项组合 * @failing_path: 输出参数,失败时相关的路径 * @return: 绑定挂载结果 */bind_mount_resultbind_mount(intproc_fd,constchar*src,constchar*dest,bind_option_toptions,char**failing_path);/* 根据绑定挂载结果输出错误信息并终止进程 */voiddie_with_bind_result(bind_mount_result res,intsaved_errno,constchar*failing_path,constchar*format,...)__attribute__((__noreturn__))__attribute__((format(printf,4,5)));

2. 工具函数与宏定义头文件 (utils.h)

/* bubblewrap * Copyright (C) 2016 Alexander Larsson * SPDX-License-Identifier: LGPL-2.0-or-later */#pragmaonce#include<assert.h>#include<dirent.h>#include<errno.h>#include<fcntl.h>#include<stdarg.h>#include<stdbool.h>#include<stdio.h>#include<stdlib.h>#include<string.h>#include<syslog.h>#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>/* 计算静态数组元素个数的宏 */#defineN_ELEMENTS(arr)(sizeof(arr)/sizeof((arr)[0]))/* 处理因信号中断而失败的系统调用的重试宏 */#ifndefTEMP_FAILURE_RETRY#defineTEMP_FAILURE_RETRY(expression)\(__extension__\({longint__result;\do__result=(longint)(expression);\while(__result==-1L&&errno==EINTR);\__result;}))#endif/* 管道端点定义 */#definePIPE_READ_END0#definePIPE_WRITE_END1/* 全局日志级别前缀标志 */externbool bwrap_level_prefix;/* 日志记录函数,支持 syslog 级别的 severity 和 printf 格式 */voidbwrap_log(intseverity,constchar*format,...)__attribute__((format(printf,2,3)));/* 便捷的警告日志宏 */#definewarn(...)bwrap_log(LOG_WARNING,__VA_ARGS__)/* 因错误而终止进程的函数,会打印错误信息和 errno 对应的描述 */voiddie_with_error(constchar*format,...)__attribute__((__noreturn__))__attribute__((format(printf,1,2)));

3. 网络命名空间设置(环回接口)头文件 (network.h)

/* bubblewrap * Copyright (C) 2016 Alexander Larsson * SPDX-License-Identifier: LGPL-2.0-or-later */#pragmaonce/* 设置网络命名空间中的环回接口。 * 通常在创建新的网络命名空间后调用,以确保 lo 接口处于 UP 状态。 */voidloopback_setup(void);

4. 绑定挂载实现代码片段 (bind-mount.c部分)

/* bubblewrap * Copyright (C) 2016 Alexander Larsson * SPDX-License-Identifier: LGPL-2.0-or-later */#include"config.h"#include<sys/mount.h>#include"utils.h"#include"bind-mount.h"/* 解析 /proc/self/mountinfo 文件时的辅助函数:跳过当前 token */staticchar*skip_token(char*line,bool eat_whitespace){while(*line!=' '&&*line!='\n')line++;if(eat_whitespace&&*line==' ')line++;returnline;}/* 将 mountinfo 中八进制转义的字符串(如 `\134` 表示反斜杠)进行原地解码 */staticchar*unescape_inline(char*escaped){char*unescaped,*res;constchar*end;res=escaped;end=escaped+strlen(escaped);unescaped=escaped;while(escaped<end){if(*escaped=='\\')/* 遇到转义序列 */{/* 将八进制序列 \ooo 解码为一个字符 */*unescaped++=((escaped[1]-'0')<<6)|((escaped[2]-'0')<<3)|((escaped[3]-'0')<<0);escaped+=4;/* 跳过转义序列 */}else{*unescaped++=*escaped++;/* 复制普通字符 */}}*unescaped=0;/* 添加字符串终止符 */returnres;}/* 解码挂载选项字符串(如 "ro,nodev")为相应的 mount() 系统调用标志位 */staticunsignedlongdecode_mountoptions(constchar*options){constchar*token,*end_token;inti;unsignedlongflags=0;staticconststruct{intflag;constchar*name;}flag_names[]={{MS_RDONLY,"ro"},{MS_NOSUID,"nosuid"},{MS_NODEV,"nodev"},{MS_NOEXEC,"noexec"},{MS_SYNCHRONOUS,"sync"},{MS_REMOUNT,"remount"},{MS_MANDLOCK,"mand"},{MS_DIRSYNC,"dirsync"},{MS_NOATIME,"noatime"},{MS_NODIRATIME,"nodiratime"},{MS_BIND,"bind"},{MS_MOVE,"move"},{MS_REC,"rec"},{MS_SILENT,"silent"},{MS_POSIXACL,"acl"},{MS_UNBINDABLE,"unbindable"},{MS_PRIVATE,"private"},{MS_SLAVE,"slave"},{MS_SHARED,"shared"},{MS_RELATIME,"relatime"},{MS_KERNMOUNT,"kernmount"},{MS_I_VERSION,"iversion"},{MS_STRICTATIME,"strictatime"},{MS_LAZYTIME,"lazytime"},};/* ... 实际解析逻辑 ... */}

这些代码展示了 Bubblewrap 如何通过精细的绑定挂载控制、健壮的错误处理、命名空间操作和系统调用封装,来构建一个安全、可控的非特权沙箱环境。其模块化设计使得它可以作为底层工具被更高层的容器框架(如 Flatpak)所集成和使用。FINISHED
JwWylm2/DJz0H/UvRktemHCdGgLP0PsR1WU4BWi+LMY=
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/4 4:07:23

FaceRecon-3D镜像免配置优势:比源码部署节省90%环境配置时间实测

FaceRecon-3D镜像免配置优势&#xff1a;比源码部署节省90%环境配置时间实测 1. 为什么一张自拍就能生成3D人脸&#xff1f;这背后省下的不是时间&#xff0c;是耐心 你有没有试过在本地跑一个3D人脸重建项目&#xff1f;我试过三次——第一次卡在CUDA版本和PyTorch的兼容性上…

作者头像 李华
网站建设 2026/2/4 22:15:31

translategemma-4b-it体验:笔记本电脑也能跑的专业级翻译AI

translategemma-4b-it体验&#xff1a;笔记本电脑也能跑的专业级翻译AI 1. 引言 你有没有过这样的经历&#xff1a;出差途中收到一封密密麻麻的英文技术文档&#xff0c;手机翻译App翻得生硬拗口&#xff0c;还卡在“the aforementioned methodology”这种表达上&#xff1b;…

作者头像 李华
网站建设 2026/2/3 15:42:16

亲测好用10个降AIGC平台 千笔轻松降AI率

AI降重工具如何帮你轻松应对论文挑战 在当前学术研究中&#xff0c;越来越多的研究生开始使用AI写作工具辅助论文撰写&#xff0c;但随之而来的AIGC率过高、查重率超标等问题也成为了大家关注的焦点。为了确保论文符合学校或期刊的要求&#xff0c;许多学生都在寻找高效、可靠…

作者头像 李华
网站建设 2026/2/4 16:23:22

语义分割十年演进

语义分割&#xff08;Semantic Segmentation&#xff09; 的十年&#xff08;2015–2025&#xff09;&#xff0c;是从“像素级分类”向“全场景语义理解”与“通用分割大模型”的飞跃。 语义分割的目标是为图像中的每个像素分配一个类别标签&#xff08;如“道路”、“人”、“…

作者头像 李华
网站建设 2026/2/3 14:50:39

实测VibeThinker-1.5B-WEBUI:HMMT真题准确率超预期

实测VibeThinker-1.5B-WEBUI&#xff1a;HMMT真题准确率超预期 你有没有试过——在RTX 3060笔记本上&#xff0c;不到2分钟就跑起一个能解HMMT代数压轴题的模型&#xff1f;不是调用云端API&#xff0c;不是等待排队&#xff0c;而是本地加载、实时响应、步骤清晰、逻辑闭环。…

作者头像 李华