LWN: mount文件系统后对ID重映射!

关注了就能看到更多这么棒的文章哦~

ID mapping for mounted filesystems

By Jonathan Corbet
November 19, 2020
DeepL assisted translation
https://lwn.net/Articles/837566/

几乎所有的文件系统(除了像 VFAT 这样的古老类型)都实现了给每个文件设置 owner 和 group 的概念。这样一来,操作系统的上层软件就可以使用这些信息来控制对这些文件的访问。过去的几十年看来,只要每个文件有一个独立的 owner 和 group 应该就足够了。但现在有越来越多的使用场景希望根据进程运行的环境来改变这个所有权。开发者们几年来一直在努力寻找这个问题的解决方案。最新的尝试是由 Christian Brauner 提出的 ID-mapped mounts patch set。

事实上,ID-mapping 问题并不完全是个新问题。文件的 User 和 Group ID 只有在有唯一一个负责分配这些 ID 的管理实体时才有意义。由于这种条件并不是总能得到满足,因此像 NFS 这样的网络文件系统在好多年前就已经具备了 remap ID 的能力。虚拟化和容器技术的发展让这个问题变得对普通用户也开始有影响了,一台机器上可以会运行多个管理方(management domain)。如果不使用 NFS 的话,NFS ID-remapping 机制就没什么用处。

例如,container runtime 系统可能希望为每个容器都提供一个公共的 root image。可以利用 user namespace 来确保每个容器在主机系统上可以使用一组非特权 ID 来运行,但同时这些容器应该能够以 root 权限访问它们的根映像。用 ID remapping 来挂载这个 root image 的话就可以实现这个目的了。同样,ID remapping 将会使容器之间共享文件系统变得更容易,而不用去管每个容器内使用的 ID 是什么。或者来看看 systemd-homed 这个项目,它能使得跨越不同机器来使用用户 home 目录的访问可以保持数据一致性。如果一个用户登录到某个系统,并得到一个与 home 目录所有权不相匹配的用户 ID,那么 systemd-homed 将改变 home 目录中所有文件的 ownership,这个操作很低效。ID remapping 就可以让这个问题的解决更加合理。

之前已经有过很多尝试来解决这些问题。shiftfs 文件系统就是被设计成叠加在普通文件系统之上,然后它会在进行访问时对这个访问操作进行用户和组 ID 的重新映射。这个想法随后演变成了 shifting bind mounts,会将 ID remapping 功能移到虚拟文件系统(VFS)层。不久之后,Brauner 提出了 FSID mapping,它重新利用内核的 filesystem-ID 这个概念来执行重新映射。现在,有了 ID-mapped mount 后,这个 remapping 动作又回到了 VFS 中来处理,但有了一些变化。

这个 patchset 在 vfsmount struct 中增加了一个新指针,代表了挂载上来的文件系统。这个被称为 mnt_user_ns 的指针指向一个 user namespace。当然,user namespace 的关键特性之一就是 ID remapping。在 user namespace 内运行的进程,其用户 ID 和组 ID 已经被重映射过,这样执行任何能到达 namespace 之外的操作时都可以用这个新的 ID,包括文件系统操作。但是 user namespace 只有一个单一映射,会用于所有操作,并且所有挂载的文件系统上用的也都是这组相同的映射关系。将 user namespace 填加到 vfsmount 结构中的话就允许每个 mount 上来的文件系统有不同的映射。

因此,设置 ID-mapped mount,就需要创建 user namespace 来放置 ID-mapping tables。这些 user namespace 很可能永远都不会有进程在其中运行。从某种意义上说,它们的主要功能在这种情况下完全没有用到。但这种方法就使得我们可以使用所有现有的 ID-mapping helpers 函数了,否则的话,我们需要创建一个专门用于 ID-mapping 的抽象层,这会导致许多重复代码。

默认情况下,mount 上来的文件系统将会指向初始 user namespace,这就可以认为这里并不需要进行重新映射。想要在 mount 上来的文件系统中添加 ID mapping 的代码必须首先创建一个新的 user namespace。这是一个有点迂回的过程,内核并不直接支持。在 Brauner 编写的一个 mount-idmapped 工具的示例中,这个任务是通过在自己的 user namespace 创建一个新进程来实现的。子进程除了用一个 SIGSTOP 信号暂停自己之外,什么都不用做,而父进程则通过打开相关的/proc 文件来创建一个对子进程的 user namespace 的引用。

下一步,在新创建的用户命名空间中建立 ID mapping。这是通过向子进程的/proc 目录中的 uid_map 和 gid_map 文件写入适当的值来完成。等到完成这些工作之后,就可以直接将子进程 kill 掉。之前打开的指向它的 user namespace 的文件描述符,就可以确保 user namespace 在进程被 kill 后还能继续存在。

实际上真正把 user namespace 关联起来是通过 mount_setattr()系统调用来实现的,这也是这组 patch 里面新增的:

struct mount_attr {
  __u64 attr_set;
  __u64 attr_clr;
  __u64 propagation;
  __u64 userns_fd;
};

int mount_setattr(int dfd, const char *path, unsigned int flags,
                  struct mount_attr *attr, size_t attr_size);

mount_attr 结构中的 attr_set 和 attr_clr 字段分别指定了要设置和清除的属性。propagation 用来规定这个操作是只影响 fd 和 path 所确定的文件系统,还是说也会影响当前挂载在它下面的所有文件系统。要将 ID mapping 添加到文件系统中时,调用者(在当前 patch 中要求必须具有 CAP_SYS_ADMIN 这个 capability)应该在 attr_set 中设置 MOUNT_ATTR_IDMAP,并将 userns_fd 设置为相关的 user namespace 的文件描述符。

虽然 ID mapping 显然可以用来为 mount 上来的任何文件系统进行设置,但该功能主要还是希望用在 bind mount 上的,即为一个现有文件系统来创建一个新的使用界面。这组 patch series 的前言里面就给出了一些如何使用该功能的例子。一个简单例子就是提供一个目录,其中的文件是属于其他用户 ID 的。另一个例子创建了一个 identity mapping(ID 不会改变),但这组映射中不包括用户 ID 0,就能阻止 root 访问。没有 user ID 概念的文件系统(如 VFAT)可以通过 ID-mapped mount 来将这些 ID 嫁接上去使之支持 user ID。就是诸如此类的这些功能。

之前发布的这组 patch set 引起了一定的兴趣。这项工作似乎得到了 VFS 开发者的认可,这也是任何此类 patch 所必须克服的首要障碍。因此,可能大家终于找到了解决 ID-mapping 问题的办法,不需要再继续寻找了,希望如此。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值