7

使用 bwrap 沙盒

 4 years ago
source link: https://blog.lilydjwg.me/2021/8/12/using-bwrap.215869.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

使用 bwrap 沙盒

本文来自依云's Blog,转载请注明。

bwrap 是命令的名字。这个项目的名字叫 bubblewrap。它是一个使用 Linux 命名空间的非特权沙盒(有用户命名空间支持的话)。

我之前使用过 Gentoo 的 sandbox 工具。它是 Gentoo 用于打包的工具,使用的是 LD_PRELOAD 机制,所以并不可靠。主要用途也就是避免打包软件的时候不小心污染到用户家目录。

使用 bwrap 的话,限制是强制的,没那么容易绕过(至于像 Go 这种因为不使用 libc 而意外绕过就更难得了)。不过 bwrap 不会在触发限制的时候报错。

bwrap 的原理是,把 / 放到一个 tmpfs 上,然后需要允许访问的目录通过 bind mount 弄进来。所以没弄进来的部分就是不存在,写数据的话就存在内存里,用完就扔掉了。这一点和 systemd 也不一样——systemd 会把不允许的地方挂载一个没权限访问的目录过去。

bwrap 的挂载分为只读和可写挂载。默认是 nodev 的,所以在里边是不能挂载硬盘设备啥的。它也提供最简 /proc 和 /dev,需要手动指定。整个 / 都是通过命令行来一点点填充内容的,所以很容易漏掉部分内容(比如需要联网的时候忘记挂载 resolv.conf 或者 TLS 证书),而不会不小心允许不应当允许访问的地方(当然前提是不偷懒直接把外面的 / 挂载过去啦)。

至于别的命名空间,有 --unshare-all 选项,不用写一堆了。如果需要网络,就加个 --share-net(这个选项文档里没写)。没有别的网络方案,因为没特权,不能对网络接口进行各种操作。--die-with-parent 可以保证不会有残留进程一直跑着。

我目前的打包命令长这样:

alias makepkg='bwrap --unshare-all --share-net --die-with-parent \
--ro-bind /usr /usr --ro-bind /etc /etc --proc /proc --dev /dev \
--symlink usr/bin /bin --symlink usr/bin /sbin --symlink usr/lib /lib --symlink usr/lib /lib64 \
--bind $PWD /build/${PWD:t} --ro-bind /var/lib/pacman /var/lib/pacman --ro-bind ~/.ccache ~/.ccache \
--bind ~/.cache/ccache ~/.cache/ccache --chdir /build/${PWD:t} /usr/bin/makepkg'

以后应该随着问题的出现还会修改的。

其实我学 bwrap 主要不是自己打包啦(毕竟基本上都交给 lilac 了),而是给 lilac 加固。Arch 的打包脚本是 shell 脚本,所以很多时候不执行脚本就没办法获取一些信息、进行某些操作。唉,这些发行版都喜欢糙快猛的风格,然后在上边打各种补丁。deb 和 rpm 的打包也都是基于 shell 脚本的。而 lilac 经常通过脚本编辑打包脚本,或者从 AUR 取,万一出点事情,把不该删的东西给删掉了,或者把私钥给上传了,就不好了。所以前些天我给 lilac 执行 PKGBUILD 的地方全部加上了 bwrap。期间还发现 makepkg --printsrcinfo 不就是读取 PKGBUILD 然后打印点信息嘛,竟然不断要求读取 install 脚本,还要对打包目录可写……

另一个用法是,跑不那么干净的软件。有些软件不得不用,又害怕它在自己家里拉屎,就可以让它在沙盒里放肆了。比如使用反斜杠作为文件路径分隔符写一堆奇怪文件名的 WPS Office。再比如不确定软件会不会到处拉屎,所以事先确认一下。我以前使用的是基于 systemd-nspawn 和 overlayfs 的方案(改进自基于 aufs 和 lxc 的方案所以名字没改),不过显然 bwrap 更轻量一些。跑 GUI 的话,我用的命令长这样:

bwrap --unshare-all --die-with-parent --ro-bind / / \
--tmpfs /sys --tmpfs /home --tmpfs /tmp --tmpfs /run --proc /proc --dev /dev \
--ro-bind ~/.fonts ~/.fonts --ro-bind ~/.config/fontconfig ~/.config/fontconfig \
--bind ~/.cache/fontconfig ~/.cache/fontconfig --ro-bind ~/.Xauthority ~/.Xauthority \
--ro-bind /tmp/.X11-unix /tmp/.X11-unix --ro-bind /run/user/$UID/bus /run/user/$UID/bus \
--chdir ~ /bin/bash

其实还可以用来给别的发行版编译东西,取代我之前使用 systemd-nspawn 的方案。bwrap 在命令行上指定如何挂载,倒是十分方便灵活,很适合这种需要共享工作目录的情况呢。以后有需要的时候我再试试看。(好像一般人都是使用 docker / podman 的,但是我喜欢使用自己建立和维护的 rootfs,便于开发和调试,也更安全。)

和 bwrap 类似的工具还有 SELinux 和 AppArmor。它们是作用于整个系统的,Arch Linux 安装会很麻烦,对于我的需求也过于复杂。Firejail 是面向应用程序的,但是配置起来也挺不容易。bwrap 更偏重于提供底层功能而不是完整的解决方案,具体用法可以让用户自由发挥。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK