关注了就能看到更多这么棒的文章哦~
Finer-grained BPF tokens
By Jonathan Corbet
October 12, 2023
ChatGPT translation
https://lwn.net/Articles/947173/
在 BPF 虚拟机中运行的程序可以根据其挂载(attach)方式执行许多特权操作;因此,加载和运行这些程序的相关 capability 本身必须是需要特权才能操作的。extended-BPF 时代开始以来,开发人员一直在努力找到一种方式,使用户能够运行他们需要的程序却不需要授予不必要的权限。在今年早些时候,关于BPF 令牌的想法遭到了一些注重安全的开发人员的反对。Andrii Nakryiko 现在已经回来,提供了一套更新的补丁集,让通过 BPF 令牌可以授予的特权的粒度变得更细致。
在早期,要加载大多数 BPF 程序,该进程就必须具有 CAP_SYS_ADMIN
能力。然而,这个能力(capability)给用户授予的权限远远超出了加载 BPF 程序本身;它基本上等同于完全的 root 访问权限。在 5.8 版本中,添加了CAP_BPF 能力,以规范对大多数 BPF 操作的访问;对于某些特定的操作来说可能还需要其他能力。尽管如此, CAP_BPF
仍然允许进程执行许多操作,管理员可能不希望给人授予这么多的权限。
因此,人们长期以来一直在寻找方法进一步限制能使用 BPF 的进程所能做的事情。已经尝试过各种方法,包括在 Linux 安全模块机制中添加authoritative挂钩 以及 一个特殊的 BPF 设备,但这些方法都被拒绝了。BPF 令牌,是一种类似数字 cookie 的东西,赋予加载 BPF 程序的权利,有可能最终也会被拒绝。然而,Nakryiko 和其他 BPF 开发人员仍然相信,BPF 的安全需求是非常独特的,因此需要针对这些需求来提供特有的解决方案;他们尚未放弃将令牌作为该解决方案的想法。
通常情况下,如果需要有限特权,那么就在某个容器内运行需要特权的进程。用户命名空间(user namespaces)通常可以用于此目的,可以跟合理创造出来的 mount namespace 结合使用。然而,BPF 程序可以执行的许多操作例如挂接到到 tracepoint 这种,本质上是全局性的动作,因此不能以这种方式进行约束。所以在用户命名空间内直接给予进程 CAP_BPF
并不是解决问题的方法;内核在命名空间级别会直接忽略 CAP_BPF
。
BPF 令牌是一种可以使容器内的进程具有等效于 CAP_BPF
能力的方法。然而,在原始提案中表达了一个担忧,令牌可能会从其预期的容器中逃逸,导致特权也被系统的其他部分所获取。在当前提案中,BPF 令牌与特定实例的 BPF 文件系统(保存持久性 BPF 对象,如 BPM maps)和用户命名空间是相关联的。因此,任何一个特定的令牌在其预期的上下文之外应该是无效的。
启用此功能的第一步是通过新的一组 mount 选项扩充 BPF 文件系统,以控制特定实例与 BPF 令牌的交互:
delegate_cmds
列出了这个 mount 相关联的 BPF 令牌可以允许的命令。举例来说,可以 mount BPF 文件系统来支持使用一个令牌允许从 maps 中读取元素,但不允许其他操作,而另一个令牌可能允许创建 map、加载程序或创建令牌。delegate_maps
控制令牌可以允许进程创建哪些类型的映射。如果BPF_MAP_CREATE
包含在delegate_cmds
中,那么这个选项才有意义。delegate_progs
指定了令牌可以允许进程加载的程序类型;对于任何允许程序加载的情况来说,BPF_PROG_LOAD
也必须包含在delegate_cmds
中。delegate_attachs
(很遗憾,不是attaches
)控制令牌可以允许的加载类型。再强调一下,首先需要允许程序加载。参见此页面来了解程序和附加类型的列表。
所有这些值都是与<uapi/linux/bpf.h>中的定义相对应的 bitmap。因此,如果使用下面的参数来 mount BPF 文件系统:
delegate_cmds=0x21
就会启用 BPF_PROG_LOAD
(0x20)和 BPF_MAP_CREATE
(0x01)。Nakryiko 承认这不是让用户体验友好的控制接口,有其是因为这些值在 enum
中定义,用户必须仔细计算来找到相关的 bit;在将来可能会有办法来用符号名称来设置。同时,特殊值 "any
" 等同于为给定选项设置所有 bit。
一旦 BPF 文件系统已经挂载到容器内,具有合适特权的程序可以使用 BPF_TOKEN_CREATE
命令创建一个新的 BPF 令牌。必须传递指示要使用的 BPF 文件系统挂载的一个已经打开了的文件描述符作为参数;生成的令牌将永远与该 BPF 文件系统相关联,该文件系统定义了令牌可以允许的操作。它还附加到与 BPF 文件系统挂载相关联的用户命名空间。关联起来之后就能阻止令牌在命名空间之外使用,但还有另一个重要的意义。
BPF_TOKEN_CREATE
操作的返回值是表示令牌的新文件描述符;然后可以通过通常的机制将此令牌传递给预期用户。该用户可以通过将其放入每个命令的庞大的 bpf_attr 联合体中添加的新字段中,来将令牌包含在其进行的任何一个 bpf()
调用中。例如,在使用 BPF_MAP_CREATE
创建新映射时,可以将令牌放入新的 map_token_fd
字段中。值得注意的是,与 BPF 子系统的约定一致,零不是一个合法的文件描述符,因此不能用于令牌描述符。
当内核考虑是否允许特定的 bpf()
调用成功时,它将检查是否存在允许这个请求的操作的令牌。然而,仅仅拥有令牌并不足以允许操作;调用进程还必须具有所需的能力(CAP_BPF,对于某些操作可能还有其他能力)。在当前内核中,这些能力必须来自全局 init 命名空间;应用了此补丁后,它们可以由包含请求进程的用户命名空间授予。因此,可以使容器能够执行特定的 BPF 操作,而不必将该特权授予容器内的每个进程。
这样一来,运行在用户命名空间内的进程将能够执行 BPF 操作,但必须要拥有有效的 BPF 令牌,该令牌要能允许请求的特定操作,并且进程在其用户命名空间中得具有所需的能力(capability)。这组补丁似乎提供了一种紧密控制可以使用 BPF 做什么的方法。目前,在 BPF 文件系统挂载中设置的能力适用于使用该文件系统创建的所有令牌。未来,可能会有一个额外操作来允许持有令牌的用户删除特定的 capability,从而进一步限制令牌允许的操作。
到目前为止,对这组 patch 的评论很少,没有任何一个反馈在质疑其核心方案;大多数讨论集中在集成 Linux 安全模块支持相关的细节上。因此,似乎已经解决了以前版本的大多数异议。如果这种情况持续下去,那么这项工作很可能会合入 mainline 了。到目前为止,在 Linux 中,基于令牌的安全机制一直没有立足之地,因此这里实际上是正在开辟一个新的领域。这也很自然,毕竟此时此刻 BPF 已经在 Linux 中颠覆了一些传统的做事方式。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~