为什么普通用户不能删除 btrfs 子卷

loo*_*bee 12 btrfs

使用循环挂载用户创建的 btrfs 文件系统,并正确设置权限,用户可以自由创建 btrfs 子卷:

user@machine:~/btrfs/fs/snapshots$ /sbin/btrfs sub create newsubvol
Create subvolume './newsubvol'
Run Code Online (Sandbox Code Playgroud)

但是,尝试删除新创建的子卷会导致错误:

user@machine:~/btrfs/fs/snapshots$ /sbin/btrfs sub del newsubvol
Delete subvolume '/home/user/btrfs/fs/snapshots/newsubvol'
ERROR: cannot delete '/home/user/btrfs/fs/snapshots/newsubvol'
Run Code Online (Sandbox Code Playgroud)

当然,root 用户可以删除它:

root@machine:/home/user/btrfs/fs/snapshots# /sbin/btrfs sub del newsubvol
Delete subvolume '/home/user/btrfs/fs/snapshots/newsubvol'
Run Code Online (Sandbox Code Playgroud)

create 和 delete 操作之间的这种行为差异似乎有点奇怪。任何人都可以对此有所了解吗?

这是命令的确切顺序:

user@machine:~$ dd if=/dev/zero of=btrfs_disk bs=1M count=100
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 1.2345 s, 84.9 MB/s
user@machine:~$ mkdir mountpoint
user@machine:~$ /sbin/mkfs.btrfs btrfs_disk

WARNING! - Btrfs Btrfs v0.19 IS EXPERIMENTAL
WARNING! - see http://btrfs.wiki.kernel.org before using

SMALL VOLUME: forcing mixed metadata/data groups
Created a data/metadata chunk of size 8388608
fs created label (null) on btrfs_disk
    nodesize 4096 leafsize 4096 sectorsize 4096 size 100.00MB
Btrfs Btrfs v0.19
user@machine:~$ sudo mount btrfs_disk mountpoint/
user@machine:~$ cd mountpoint/
user@machine:~/mountpoint$ /sbin/btrfs sub create test
Create subvolume './test'
user@machine:~/mountpoint$ /sbin/btrfs sub delete test
Delete subvolume '/home/user/mountpoint/test'
ERROR: cannot delete '/home/user/mountpoint/test' - Operation not permitted
Run Code Online (Sandbox Code Playgroud)

以下是权限:

user@machine:~/mountpoint$ ls -la
total 4
drwxr-xr-x 1 user user    8 Set  4 09:30 .
drwx------ 1 user user 4486 Set  4 09:29 ..
drwx------ 1 user user    0 Set  4 09:38 test
Run Code Online (Sandbox Code Playgroud)

和相关的行df -T

Filesystem              Type     1K-blocks      Used Available Use% Mounted on
/dev/loop0              btrfs       102400        32     98284   1% /home/user/mountpoint
Run Code Online (Sandbox Code Playgroud)

该发行版是 Debian Wheezy、3.2.0-4-686-pae内核、v0.19btrfs-tools。这种情况在 Ubuntu Saucy、3.11.0-4-generic内核、v0.20-rc1btrfs-tools上仍然出现。

Bra*_*ley 14

嗯,这对我来说是一次学习经历,但我最终想通了。我将在这里解释我的过程,以便更容易知道如何自己解决这些问题(BTRFS 文档,我相信你已经发现,目前相对不完整)。

起初我认为创建子卷是一个ioctl没有做任何能力检查的处理程序(这可能是也可能不是安全问题,取决于它是否有一些逻辑),而删除它是直接修改元数据(因此用户可能需要CAP_SYS_RAWIO正常工作)。

为了验证,我破解了btrfs-utils源代码,这是我发现的:

Create subvolume, cmds-receive.c Line 180:
         ret = ioctl(r->dest_dir_fd, BTRFS_IOC_SUBVOL_CREATE, &args_v1);

Delete subvolume, cmds-subvolume.c Line 259:
         res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
Run Code Online (Sandbox Code Playgroud)

好吧,这没有帮助,它们都是ioctl 的(有趣的旁注:“快照”由于某种原因在源代码中经常与“子卷”互换使用)。所以我转到内核源代码并在fs/btrfs/ioctl.c.

最终,我将其追溯到btrfs_ioctl_snap_destroy()并在线 2116 上:

     if (!capable(CAP_SYS_ADMIN)){
Run Code Online (Sandbox Code Playgroud)

具体来说,这是检查他们是否没有能力,但如果他们有能力,则逻辑直接跳到执行操作。if 语句的主体检查它是否是子卷 inode 的所有者的普通用户,并且USER_SUBVOL_RM_ALLOWEDBTRFS 选项启用它继续执行处理程序。如果他们没有,ioctl 处理程序将退出并显示错误。

因此,看起来销毁“快照”(又名“子卷”)通常需要具有CAP_SYS_ADMIN(或USER_SUBVOL_RM_ALLOWED要启用并且用户“拥有”给定子卷)的用户。太好了,如何创建快照/卷?

ioctl 的btrfs_ioctl_snap_create()处理程序似乎是此处理程序似乎不包含capable()直接或间接调用。由于这是代理访问的主要方式,因此我认为这意味着子卷创建总是成功的。这在功能层面解释了为什么你会看到你所看到的。

除了 BTRFS 的主要用例是使用受限用户访问的服务器之外,我无法解释为什么这被认为是可取的。这还不够,但我没有看到任何实际停止操作的代码。如果您找不到原因的答案(并且您想拥有它),您可能需要在内核邮件列表中询问。

结论

我的研究似乎表明任何人都可以创建子卷,但为了删除子卷,您要么需要拥有,要么需要CAP_SYS_ADMIN调用用户是子卷 inode 的所有者并USER_SUBVOL_RM_ALLOWED启用。

子卷创建没有意义,所以我可能错过了一些拒绝操作的间接方式,因为这似乎是对系统进行 DoS 的一种简单方法。

注意:我不在可以验证此功能的地方,但是一旦我回到家,我就可以设置setcap魔法是否如何预测。


Gab*_*iel 5

删除子卷允许某人取消链接不属于他们的文件。在我看来,特权用户在较低特权用户选择的位置写入的文件是公平的游戏,但提供非 root 删除功能的人可能对这些语义的安全性和内容的安全性没有足够的信心将它们作为新的挂载选项提交 ( mount -o user_subvol_rm_allowed)。