关注了就能看到更多这么棒的文章哦~
Private loop devices with loopfs
May 7, 2020
This article was contributed by Marta Rybczyńska
原文来自:https://lwn.net/Articles/819625/
主译:DeepL
loop device这种设备是内核里面抽象出来的一个虚拟设备,可以把文件呈现为一个物理上的块设备(physical block device)一样。lopp device的典型用途是把存放在一个文件里面的文件系统影响给mount出来。loop device是全局性的,各个用户之间都可以看到,这对于container容器工作场景来说会有一些问题,因为在这些场景中,实例(instance)之间应该要保证相互隔离的。Christian Brauner一直在研究这个问题。他发布了一个patch set,通过添加一个名为loopfs的小虚拟文件系统来解决这个问题。
loop device通常都出现在/dev下,其名称如/dev/loopN这样。可以用一个专门设置的/dev/loop-control文件来创建和销毁loop device,或用它来找到第一个可用的loop device。如果需要把某个文件跟指定的loop device关联起来,或者想设置其他参数比如偏移(offset)或块大小(block size),都可以通过loop device本身的ioctl() 调用来完成。loop(4) 的man page上有关于其工作原理的细节介绍。
用户一般不需要指定使用某个特定的loop device,他们常用的方式是使用一个mount参数来配置loop device。
mount /tmp/myimage.img /mnt/disk -o loop
mount就会去找一个可用的loop device,将其与/tmp/myimage.img关联,然后将该设备挂载到/mnt/disk上。有些管理员可能更喜欢使用一些与此类似的命令参数,可以控制得更精确:
mount /tmp/myimage.img /mnt/disk -o loop=/dev/loop1
管理员用这种参数就可以指定要使用哪个loop device。如果管理员需要对loop device进行更多的控制,也可以使用losetup命令查询和设置loop device的属性。
如上所述,loop device是全局可见的,并且在用户之间是公用的。也就是说/dev/loop3在所有namespace中都是同一个设备。如果一个应用程序需要一个自己私有的loop device,它没有办法请求一个loop device给自己一个人用。loop device在容器之间也是共享的,因此一个容器就可以监控到其他容器的操作,或者访问到其他容器的数据。
在这个patch set的讨论中过程中提到了loop device的许多不同使用场景。Dmitry Vyukov举了一个例子(https://lwn.net/ml/linux-kernel/CACT4Y+aDeSAARG0b9FjDFyWuhjb=YVxpGtsvBmoKnHo+0TF4gA@mail.gmail.com/ )就是在测试进程使用loop device时可以确保这些测试进程相互隔离。他描述了他所遇到的问题如下:
目前,所有的loop device和loop-control都是全局的,导致测试进程相互争抢,这又会导致测试中覆盖不全,也会出现一些不可重现的crash。
Brauner也从container的应用中举了一些例子。例如,systemd-nspawn就不支持loop device,因为loop device不能被动态发现并被容器所占用。Chromium OS不允许使用loop device。Kubernetes也遇到了由loop device的全局性而导致的问题:一个文件在用户退出后,可能还继续绑定在原来的loop device上。
loopsfs
于是就创建了这个内核内的新虚拟文件系统loopfs,它实现了那些loop device和loop-control文件。这个文件系统可以被多次挂载,每个实例中的loop device与所有其他实例中的所有其他loop device是独立的。这样,可以为应用程序和容器提供私有专用的loop device。loopfs中的loop device和loop-control文件都可以使用传统的loop device相同的操作方式。
loopfs的一个好处是可以保证兼容旧有的应用程序的兼容,只是替换成了新增的virtualized loop-device文件而已。在这种情况下,管理员可以正常mount文件系统,然后把缺省的loop control文件替换成loopfs中的文件。考虑可以看一下下面这个来源于patch cover letter里面的例子:
# Mount a new loopfs instance in /dev/loopfs/
mount -t loop loop /dev/loopfs/
# Replace the standard loop control file with the ones from loopfs
ln -sf /dev/loopfs/loop-control /dev/loop-control
# Find the first available loop device
loopdev=`losetup -f` # will be something like /dev/loop0
deventry=`basename $loopdev` # now just "loop0"
# Redirect that loop device to loopfs
ln -sf /dev/loopfs/$deventry /dev/$deventry
# mount an image
mount -o loop /image.img /mnt/disk
还可以使用/proc/sys/user/max_loop_devices来控制在每个 loopfs 实例中可以创建的loop device的最大数量。
Christoph Hellwig不同意loopfs这种方法,他说代码量太大,而它提供的好处不足以证明需要这么大的改动。Brauner解释了loopfs能带来的新的用法,但讨论到此就结束了。对于这个提议,目前并没有太大的反对意见。
Loopfs不只是创建了一个可以独立使用的loop-device池,它还可以有助于非特权用户来挂载loop device。这只需要将loopfs与Brauner早期做过的系统调用拦截的工作相结合就可以。后者(https://lwn.net/ml/linux-kernel/20190920083007.11475-1-christian.brauner%40ubuntu.com/)会使用seccomp来建立一个独立的进程,用来决定哪些操作是可以允许的。搭建好这样的环境之后,非特权用户就可以像往常一样运行mount,而拦截系统调用的特权进程将执行实际操作动作。
Jann Horn提出了非特权应用使用loop device可能存在的一个问题:大多数文件系统的实现里面没有考虑过如何处理恶意的文件系统映像。虽然已经做了一些工作,但文件系统映像一般仍被视为可信数据;这也是为什么之前有人提出允许非特权用户进行mount文件系统操作的时候,遭到人们反对的原因。如果攻击者能访问到loop device并mount,同时也有能力在运行时修改映像,那么问题就会变得更加复杂。
Stéphane Graber 指出,基于系统调用拦截的实现方式不需要直接mount文件系统,可以使用基于 FUSE 的mount。这样就可以防止任何文件系统级的漏洞转化为内核漏洞。LXD 的实现里面就允许这两种类型的mount。
Next steps
Loopfs似乎解决了用户在实践中遇到的一个真实问题。它在一周的时间里已经进行了三次迭代,解决了review期间收到的意见。它可能还需要一些时间才能被批准进入mainline kernel。不过,很明显,有许多用户在等待能解决loop device共享问题。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~