LWN:非特权容器的进展!

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

Progress for unprivileged containers

By Jake Edge
September 28, 2022
LSS EU
DeepL assisted translation
https://lwn.net/Articles/909627/

在过去的几年里,内核里针对创建无特权容器(containers without requiring privileges)相关的许多功能都有了很大的改善。现在的大多数容器都是采用 root 身份来运行的,这意味着那些可以逃离容器的漏洞都会导致系统承担风险。Stéphane Graber 在 2022 年欧洲 Linux 安全峰会(LSS EU)上发表了演讲,给出了他和其他人一直在做的将容器作为非特权代码(unprivileged code)运行的工作中的不少细节。

这次演讲原定有两位演讲者,因为 Christian Brauner 也计划要来进行演讲;不幸的是,Brauner 在会议期间被困扰都柏林的旅行限制所影响,在演讲时正在机场等待回家的飞机。这次演讲是他们 6 月份在北美 LSS 会议上关于非特权容器的系统调用拦截的演讲的后续。Graber 是 LXC 和 LXD 容器项目的负责人,我们最近介绍过这个项目;而 Brauner 是内核开发者,也是 LXC/LXD 的维护者之一。

User namespaces

讲座的题目是 "What's new in the user namespace",但内容却相当广泛。Graber 首先快速介绍了用户命名空间(user namespaces),它是由 Eric Biederman 在 2013 年加入内核的;它们 "目前已经差不多有十年的历史了"。简而言之,用户命名空间允许进程及其后代将命名空间内的用户 ID(UID)和组 ID(GID)映射到运行这个命名空间的真实 Linux 系统(即 "根命名空间")中的另一个值上。这样以来,每个命名空间里都可以有一个 UID 0,在这个命名空间内看起来以及用起来就像是 root 用户一样,但是实际映射到 host 系统上时候只是一个非特权 ID。

233b31ede8a3b22f835fef53ca44cff2.png

[Stéphane Graber]

普通用户可以创建自己的用户命名空间,并将自己的 UID/GID 映射给命名空间内的 root 用户;其他那些更复杂的工作就都需要使用一个带有特权的 helper 程序来给命名空间映射 ID 区域了。他说,这就类似于一个 network namespace,在创建时并没有网络设备,需要一个拥有特权的 helper 程序在非特权的网络命名空间内创建(大多数)网络设备。他做了一个快速演示,展示了如何用 unshare 命令来创建用户命名空间,该命令使用 -r 选项将他自己的 UID 映射到命名空间内的 root。

不过,通常情况下,容器里需要的不仅仅是例子中的这个单个 ID 的映射。POSIX 实际上会希望有 64K 大小的 UID 和 GID 可用,而且需要有一个 "nobody" 用户和 "nogroup" 组,这样才能让 POSIX 正常运行;也就是说通常要把整个 64K 的 ID 都映射到用户命名空间内。此外,容器可能还需要一些额外的命名空间,包括 mount、process ID(PID)、UTS(主要用于使用不一样的 hostname)、网络、以及 cgroup 命名空间。可能把多种类型的命名空间混合和配对使用会更有价值,这主要取决于容器是如何使用的。

如前所述,通过使用有特权的 helper(当然,也可以以 root 身份来配置命名空间),就可以让用户命名空间映射到许多 ID,甚至使用 host 系统的整个 ID 区域。也可以创建出一个实际上没有任何映射的命名空间,就是把 host 上的每个 ID 映射到命名空间中的一个 ID,但这并不是一个好主意。在用户命名空间内,"你永远不应该把真正的 UID 0 映射进来"。

命名空间内的 UID 0 看起来具有 root 用户的所有权限,但这并不是真的,当然,至少在 host 系统上是这样的。例如,所有的 Linux capability 都会被授予这个命名空间内的 root 用户,但它们在 host 系统上并不生效。is_capable() 内核能力检查将只会使用 host 上对应的 UID 来确定具体的 capability;而 is_ns_capable() 则会报告出在命名空间内所能看到的 capability。

他说,如果有多个容器在同一台 host 上运行,那么让每个容器所映射的 64K ID 集合都不一样,就可以提供更好的安全性。这样一来,如果需要给一个特定的 host ID 施加一些资源限制,那么每个容器就都不可能对共享该 host ID 的其他容器造成拒绝服务(denial of service)攻击了。Graber 演示了使用 LXC 创建两个 ID 互不重叠的容器。

Filesystem woes

尽管 ID 也被映射到了命名空间内,但文件系统却看待事物的角度却并不按照这个;它会使用 host ID 进行权限检查以及标记写入文件的属主 ID。这是长期存在于真实文件系统(而不是虚拟文件系统)中的一个用户命名空间的问题。一些文件系统,如 tmpfs 或 FUSE,在 mount 命名空间和用户命名空间的组合使用中会实际使用映射后的 ID,但还是无法访问使用不同 ID 写入过的现有文件系统。在多个容器之间共享文件系统也很困难。

第一个试图解决这个问题尝试就是 shiftfs("偶尔我们会忘记这里的第一个'f',他笑着说)。它是由 James Bottomley 创建的,然后由 Canonical 的 Graber 团队接手。它并没有被合并到 mainline 上,但在一些 Ubuntu 版本中仍然有使用,因为 mainline 解决方案(后面会讲到)还不能支持人们感兴趣的所有文件系统。Shiftfs 的功能很像一个 overlay 文件系统,可以重新映射 UID 和 GID。他说,这个方案有一些问题,特别是在处理文件系统特有的 ioctl()命令以及跟各种虚拟文件系统(VFS)层的 cache 缓存的交互方面都有问题;这些问题清楚地表明,Shiftfs 的方法 "不是一条正确的路"。

Graber 说,解决这个问题的正确方法就是使用 ID-mapped mount,这是由 Brauner 开发的。该功能大部分都是在 VFS 层实现的,但确实需要修改少数几个文件系统从而可以支持它;目前,ext4、XFS、Btrfs、VFAT、F2FS、overlayfs,可能还有其他一些文件系统已经支持了。ZFS 和 Ceph 也都在等待合入,但目前还没有支持哪个网络文件系统。ID-mapped mount 在 VFS 层中非常干净地解决了这个问题,所以 shiftfs 会有问题的一些特殊情况也都被顺利地处理了。

New namespaces

Graber 说,他前几天参加了 Linux Plumbers Conference(LPC)及其中的 Kernel Summit track;他又了解了一些即将到来的新的命名空间的信息。现在有一个趋势,新增的命名空间不会开发成具有自己的 clone() flag 的完整的命名空间,而是将它们挂在(hang them off)用户命名空间上。他说,它们变成了可以为特定的用户命名空间启用的一个功能,这样在实现和使用时更简单。

其中第一个命名空间就是完整性测量架构(IMA,Integrity Measurement Architecture)命名空间,该命名空间开发已经持续了一段时间;第 14 版的 patch set 在演讲的当天早上发布了。可以在容器中使用 IMA 了,这样,在容器中使用的每个文件都可以被测量(be measured)从而确保其完整性。然后不同的命名空间也可以有不同的 IMA 策略。

另一个新增命名空间是由 Mathieu Desnoyers 在 LPC 介绍的 tracing namespace,Graber 说。可以允许在容器内运行一些 tracing 工具,这会是非常有用的,但实现时会很 "有趣"。要想安全使用它会很困难,所以它是那种可能需要随着时间来慢慢实现的功能;一开始会允许一些简单的东西加进来,其他的内容会慢慢加上来。

Graber 说,在社区中存在着关于如何限制用户命名空间的功能的一些讨论。用户命名空间已经存在了近十年,最近发现的 bug 并不在用户名字空间的代码中,而是在内核的其他地方,只是被该功能暴露了出来,但它仍然会导致攻击面增加。因此,人们正在寻找方法来限制它的使用。

有一个 "大锤子"(杀手锏),就是不把该功能编译到内核中,但现在这并不可行,因为越来越多的应用程序都在使用用户命名空间了。可以对能够创建的用户命名空间的数量加上一些资源限制,但这是一个全系统的设置,而不是针对每个用户或每个进程的设置。同样,可以使用 seccomp() filter 来限制可以进行哪些系统调用,但 seccomp() 不能根据 clone3() 的指针参数来进行过滤,所以技术上也不太可行。许多发行版都已经通过 sysctl 添加了他们自己的控制,但这些都不在 mainline 上。

还有人在努力给用户命名空间的创建动作添加一个安全模块钩子(security-module hook),这会给 SELinux 和其他 module 一个机会来评判这个操作。这种方法对 Linux 安全模块(LSM)社区和其他人来说是有价值的,但作为命名空间维护者的 Biederman 并不赞同。他不希望看到为用户命名空间增加更多的限制,但也许他可以被说服,Graber 说,或者能有一个更通用的机制可以通过 review。他希望看到各大发行版和其他机构能对用户命名空间的使用有更精细的控制,所以他希望这个问题能尽快得到解决。

他花了一点时间来介绍他和 Brauner 在 LSS NA 上展示的系统调用拦截的工作。简单来说,他们有一个特权进程,通过 seccomp() 的方式针对容器协调那些特权系统调用。他们希望通过这种方式实现的一些操作听起来很 "吓人",比如加载内核模块和 BPF 程序,或者挂载文件系统,但其目的是只在受信任的资源上执行这些操作。

所谓的受信任的资源(trusted resource)是 host 系统所信任的资源,因为它知道其内容没有被不信任的用户修改过。比如说一个容器中可能会请求使用一个特定的 netfilter 内核模块,那么容器管理器会检查这个模块,看它是否是受信任的模块。容器所传递过来的 module 是 "绝对不会被加载" 的,但如果容器所请求的 module 在受信任的 module 列表中,那么容器管理器就可以加载 host 上拥有的这个 module 副本。

最后,Graber 说,引入 ID-mapped mount,是采用用户命名空间和容器的一个 "游戏改变者"。多年来一直没有该功能就意味着 Docker 和 Kubernetes 容器通常必须是特权容器;由于这些容器更加主流,非特权容器的使用率一直很少。但随着 ID-mapped mount 功能的出现,Docker 风格的分层文件系统(layered filesystem)上的 ID 问题将逐渐消失,所以在未来有希望能使用更多的非特权容器。"也许再过十年,没有人会再使用特权容器……也许"。

他还认为,作为用户命名空间一部分的这种新增命名空间模式是非常有意义的。这有助于减少 review 负担,并且应该允许添加更多有趣的命名空间。seccomp() 系统调用拦截也很令人兴奋,因为它将允许绕过一些存在于非特权容器的限制。看起来未来是光明的。

[感谢 LWN 的用户支持作者去都柏林参加欧洲 Linux 安全峰会。]

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

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

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

format,png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值