Bubblewrap
Bubblewrap 是一款轻量化的沙箱应用,常为 Flatpak 和其他容器工具所用。安装和运行 bubblewrap 所需的资源非常少。虽然软件包名称是 bubblewrap,实际的命令行接口却是 bwrap(1)。Bubblewrap 所值得注意的特性包括支持 cgroup、IPC(进程间通信)、mount、network、PID、user、UTS namespaces 和 seccomp 过滤。注意 bubblewrap 在沙箱内会丢弃所有的 capabilities,因此子任务的权限不能超过其父任务。但 bubblewrap 对文件路径的黑白名单缺乏明确支持。
安装 bubblewrap包 或 bubblewrap-gitAUR。
- 有关 Arch Linux 内核所支持的 user_namespaces(7) 的更多信息,参见安全#沙盒程序。
- linux-hardened包 用户请忽略上述两个软件包并请考虑安装 bubblewrap-suid包。更多相关信息请参阅 FS#63316。
可通过直接在命令行中调用或是作为组成复杂包装器的一部分 shell 脚本中使用 bubblewrap。与一些自动在沙箱内将 /var 和 /etc 设置为只读的应用程序(例如 Firejail)不同,bubblewrap 不会做这样的操作假设。应该由用户根据想要沙箱化运行的程序来决定需要传入何种配置选项。以 setuid 权限运行的 bubblewrap 不会自动创建用户命名空间(user namespaces),但可以接受典型的环境变量,如 $HOME 和 $USER。
强烈建议安装 strace包 软件包用于了解要以沙箱运行的程序的所需文件。
除了手动配置,也可以使用配置管理器并通过更简单的操作来自动化配置 bubblewrap。
- Bubblejail — 基于 bubblewrap 的沙箱,有基于资源的权限模型(提供了用于权限配置的图形界面)。
- portableAUR:简单易用的沙箱化框架,包括 D-Bus 代理过滤、数据隔离、访问控制以及其他功能。
参阅 Bubblewrap 示例以了解如何使用 bubblewrap。 除此以外,许多项目已证实 bubblewrap 可用于通常应用程序:
无操作调用(No-op invocation)bubblewrap 的示例如下:
$ bwrap --dev-bind / / bash
上述命令会创建一个大多数情况下与沙箱外部表现完全一致的 Bash 进程。如果沙箱化的程序出现异常行为,可以通过上述的无操作调用启动,然后逐步调整至更安全的配置。
nobody,因此运行某些程序如 sudo,将不能正常工作。创建一个简单的 Bash 沙箱:
- 确定可用的内核命名空间(kernel namespaces):
$ ls /proc/self/ns
cgroup ipc mnt net pid user uts
user 的存在意味着内核已经通过 CONFIG_USER_NS=y 暴露出对用户命名空间(user namespaces)的支持。$ bwrap --ro-bind / / --unshare-user --uid 256 --gid 512 bash
bash-4.4$ id uid=256 gid=512 groups=512,65534(nobody) bash-4.4$ ls -l /usr/bin/bash -rwxr-xr-x 1 nobody nobody 811752 2017-01-01 04:20 /usr/bin/bash
在桌面项中使用 bubblewrap:
- 将宿主的整个
/目录以读写模式绑定到沙箱内的/。 - 将沙箱内的
/var和/etc目录重新以只读模式绑定。 - 挂载一个新的 devtmpfs 文件系统到沙箱内的
/dev。 - 在沙箱化的
/run目录中创建一个 tmpfs 文件系统。 - 创建一个新的网络命名空间(network namespace)以禁用网络访问。
[Desktop Entry] Name=nano Editor Exec=bwrap --bind / / --ro-bind /var /var --ro-bind /etc /etc --dev /dev --tmpfs /run --unshare-net st -e nano -o . %f Type=Application MimeType=text/plain;
/dev/pty,--dev /dev是必需的。- MuPDF 的桌面项示例(配合一个
mupdf.sh的 shell 包装器):
[Desktop Entry] Name=MuPDF Exec=mupdf.sh %f Icon=application-pdf.svg Type=Application MimeType=application/pdf;application/x-pdf;
mupdf.sh 位于 PATH 中,例如 PATH=$PATH:$HOME/bwrap
要进一步隐藏文件系统的内容(例如 /var、/usr/bin 和 /usr/lib 中的文件),甚至是用沙箱保护软件的安装,pacman 可以将软件包安装到隔离的文件系统中。
为能让 pacman 将软件安装到文件系统树中,需要先安装 fakeroot包 和 fakechroot包。
假设用户想用 pacman 将 xterm 软件包安装到一个隔离的文件系统树中,用户应该先准备好文件系统树,就像下面的示例那样:
$ MYPACKAGE=xterm
$ mkdir -p ~/sandboxes/${MYPACKAGE}/files/var/lib/pacman
$ mkdir -p ~/sandboxes/${MYPACKAGE}/files/etc
$ cp /etc/pacman.conf ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf
可能需要编辑 ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf 并调整 pacman 的配置文件:
- 移除任何不需要的自定义仓库以及仅被宿主系统需要的
IgnorePkg、IgnoreGroup、NoUpgrade和NoExtract设置。 - 可能需要移除
CheckSpace选项,这样 pacman 就不会在检查磁盘空间的时候抱怨找不到根文件系统。
然后将 base 包组和 fakeroot 安装到隔离的文件系统树中:
$ fakechroot fakeroot pacman -Syu \
--root ~/sandboxes/${MYPACKAGE}/files \
--dbpath ~/sandboxes/${MYPACKAGE}/files/var/lib/pacman \
--config ~/sandboxes/${MYPACKAGE}/files/etc/pacman.conf \
base fakeroot
因为接下来要以相同的选项重复调用 bubblewrap,可以设置一个别名(alias):
$ alias bw-install='bwrap \
--bind ~/sandboxes/${MYPACKAGE}/files/ / \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--tmpfs /tmp \
--proc /proc \
--dev /dev \
--chdir / '
编辑 ~/sandboxes/${MYPACKAGE}/files/etc/locale.gen 并运行下面的命令以设置 locale:
$ bw-install locale-gen
然后设置 pacman 的 keyring:
$ bw-install fakeroot pacman-key --init $ bw-install fakeroot pacman-key --populate
现在就可以安装所需的 xterm 软件包了:
$ bw-install fakeroot pacman -S ${MYPACKAGE}
如果 pacman 在这一步执行失败,尝试再次运行用于 populate keyring 的命令。
成功后,用户现在拥有一个包含 xterm 的隔离的文件系统树。之后可以再次使用 bw-install(上面的例子中配置好的别名)来更新文件系统树。
接下来就可以使用 bubblewrap 来运行软件了。本例中的 command 应该替换成 xterm。
$ bwrap \
--ro-bind ~/sandboxes/${MYPACKAGE}/files/ / \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--tmpfs /tmp \
--proc /proc \
--dev /dev \
--chdir / \
command
注意,一些文件会在软件包中共享。用户可以将一个已存在的父文件系统树中的所有文件硬链接到一个新的文件系统树中来重用这些文件:
$ cp -al ~/sandboxes/${MYPARENTPACKAGE} ~/sandboxes/${MYPACKAGE}
然后像平时那样从 bw-install fakechroot fakeroot pacman ... 开始调用 pacman 进行安装。
绑定挂载宿主的 X11 socket 到另一个 X11 socket 可能无法工作:
--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X8 --setenv DISPLAY :8
一种解决方法是将宿主的 X11 socket 绑定挂载到沙箱中的同一个 socket:
--bind /tmp/.X11-unix/X0 /tmp/.X11-unix/X0 --setenv DISPLAY :0
虽然 bwrap 为沙箱化的应用程序提供了一些非常不错的隔离功能,但对于应用程序来说,只要能访问 X11 socket,就仍然有能够逃离沙箱隔离的方法。
X11 下的应用程序之间没有互相隔离,这会让恶意软件能够监听输入、注入按键或记录其他应用程序的图像。
一种应对方式是换用 Wayland 混成器,该混成器不能从沙箱中访问 X 服务。Wayland 实现了不允许应用程序交互的特性。
而如果要继续使用 X11,应该使用 xpra 或 xephyr。这些工具能够创建一个次要且仅用于运行被沙箱化的应用程序的 X11 实例,该实例会在用户目前使用的环境中的一个窗口中显示。以这种方式运行的应用程序窗口无法与其自身 X11 实例以外的环境交互。上述方式同样可与 bwrap 共同使用。
要测试 X11 是否被隔离,运行 xinput test id(使用 xinput list 查找所需的 id)。
在没有额外隔离的情况下运行 X11 时,上述命令会显示任何能访问 X11 且够抓取键盘输入的其他应用程序,这种情况下恶意软件将能够记录按键。
xdg-desktop-portal,让它认为被沙箱化的程序是一个 Flatpak 程序。再次提醒,运行不受信任的代码永远是危险的,就算是通过 Portals 在沙箱中运行也是。通过一些解决方法,或许可以利用 XDG Desktop Portals 来沙箱化一些应用程序。
这么做的优点是,使用文件系统 portals,能够在不给予应用程序对于家目录(home directory)的访问权限的情况下,让应用程序依然可以访问文件。
然而出于安全原因,使用 portals 需要欺骗 xdg-desktop-portal,让它认为被沙箱化的程序是 Flatpak 的一部分。这可以通过在沙箱的根文件系统中添加一个 .flatpak-info 文件来实现。
此外,用户还应该运行 xdg-dbus-proxy 以更好地颗粒化控制 portals 的访问权限。此命令应该在沙箱环境下运行,因此也需要一个 .flatpak-info 文件。但 proxy 至少需要能够有 org.freedesktop.portal.Flatpak 的 talk 访问权限。更多 portals 请访问 Flatpak 文档。
常见的案例是允许一个被完全限制 home 目录访问权限的程序仅仅能够访问用户在文件选择器中选择的文件和目录。用以下参数启动 xdg-dbus-proxy 可以实现这个目的:
--talk=org.freedesktop.portal.Documents --talk=org.freedesktop.portal.Flatpak --talk=org.freedesktop.portal.Desktop --talk=org.freedesktop.portal.FileChooser
完整示例如下:
APP_NAME=app.application.Name
APP_FOLDER="$XDG_RUNTIME_DIR/app/$APP_NAME"
mkdir -p "$APP_FOLDER"
set_up_dbus_proxy() {
bwrap \
--new-session \
--symlink /usr/lib64 /lib64 \
--ro-bind /usr/lib /usr/lib \
--ro-bind /usr/lib64 /usr/lib64 \
--ro-bind /usr/bin /usr/bin \
--bind "$XDG_RUNTIME_DIR" "$XDG_RUNTIME_DIR" \
--ro-bind-data 3 "/.flatpak-info" \
--die-with-parent \
-- \
env -i xdg-dbus-proxy \
"$DBUS_SESSION_BUS_ADDRESS" \
"$APP_FOLDER/bus" \
--filter \
--log \
--talk=org.freedesktop.portal.Flatpak \
--call="org.freedesktop.portal.Desktop=org.freedesktop.portal.Settings.Read@/org/freedesktop/portal/desktop" \
--broadcast="org.freedesktop.portal.Desktop=org.freedesktop.portal.Settings.SettingChanged@/org/freedesktop/portal/desktop" 3<<EOF
[Application]
name=$APP_NAME
EOF
}
set_up_dbus_proxy &
sleep 0.1
bwrap \
...
--ro-bind-data 3 /.flatpak-info \
...
3<<EOF
[Application]
name=$APP_NAME
EOF
当被隔离的 IRC 或电子邮件客户端尝试打开一个 URL,通常会尝试启动一个浏览器进程,而被启动的浏览器进程会与被隔离的应用程序一样运行在同一个沙箱中,而一个被良好隔离的应用程序很有可能没法正常工作。Firejail 所用的处理方式是给予被隔离的应用程序所有浏览器也拥有的权限,然而这也意味着分配大量的权限。
对于这个问题,更好的解决方法是将打开 URL 的请求发送到沙箱外。参照下列示例使用 snapd-xdg-open:
- 安装 snapd-xdg-open-gitAUR
- 在
bwrap命令行中添加:
$ bwrap ... \ --ro-bind /run/user/$UID/bus /run/user/$UID/bus \ --ro-bind /usr/lib/snapd-xdg-open/xdg-open /usr/bin/xdg-open \ --ro-bind /usr/lib/snapd-xdg-open/xdg-open /usr/bin/chromium \ ...
对于不使用 XDG 惯例的应用程序而言,绑定 /usr/bin/chromium 是必需的,比如 Mozilla Thunderbird。
一个关于 TIOCSTI 的安全问题(CVE-2017-5226)会引起沙箱逃逸。 为防止这个问题,bubblewrap 已引入一个新选项“--new-session”,该选项会调用 setsid()。 然而,这个选项在一些情况下会引起行为问题(behavioural issues)从而难以使用。 例如,这个选项会让 shell 的作业控制(job control)无法应用于 bwrap 命令。
如果应用程序开发人员有其他方式解决上述漏洞(CVE-2017-5226),可以不用这个选项(比如 Flatpak 所处理的用于 SECCOMP 的方式),但除此以外建议尽量使用“--new-session”选项。
特定应用程序如 Chromium 已经使用 suid 帮助文件实现了其自身的沙箱环境。这个机制会在以 bubblewrap 容器中运行这些软件时被阻止。
一种解决方法是让应用程序使用 bubblewrap 创建的命名空间。比如可以使用 zypakAUR,同时它也是 Flatpak 所使用的方式,用于在额外的命名空间中运行基于 electron 的应用程序。这个链接中有关于如何使用 zypak 运行 Chromium 或 Electron 的示例代码。
对于使用 ALSA 声音系统的特定程序,添加如下选项:
--dev-bind /dev/snd /dev/snd
即可获得声音输出。