非根用户下的容器与设备

Non-root Containers And Devices

Author: Mikko Ylinen (Intel)

当用户希望在Linux上部署使用加速器设备的容器(通过Kubernetes Device Plugins )时,PodsecurityContext中与用户/组ID相关的安全设置会导致一个问题

在这篇博文中,我将讨论这个问题,并描述到目前为止为解决这个问题所做的工作。解决该issue 并不需要长篇大论。

相反,这篇文章的目的是提高人们对这个问题的认识,并强调重要的设备用例。这是Kubernetes需要的,因为Kubernetes要处理新的相关特性,比如对用户名称空间的支持。

为什么非根容器无权使用设备?

Kubernetes中运行容器的关键安全原则之一是最小权限原则:

Pod/container securityContext指定要设置的配置选项,例如Linux功能(CAP)、MAC策略和用户/组ID值。 此外,集群管理员还可以使用PodSecurityPolicy(已弃用)或PodSecurity Admission(alpha)等工具来对部署在集群中的Pod实施所需的安全设置

例如,这些设置可能要求容器必须是runAsNonRoot,或者禁止它们在runAsGroupsupplementalGroups中使用root的组ID运行

Kubernetes中,kubelet构建容器可用的设备资源列表(基于来自设备插件的输入), 该列表包含在发送给CRI容器运行时的CreateContainer CRI消息中。

每个设备包含很少的信息: 主机/容器设备路径和所需的设备组权限。

{
        "type": "<string>",
        "path": "<string>",
        "major": <int64>,
        "minor": <int64>,
        "fileMode": <uint32>,
        "uid": <uint32>,
        "gid": <uint32>
},

CRI容器运行时(containerd, CRI-O)负责从主机获取每个设备的信息。默认情况下,运行时复制主机设备的用户和组id:

  • uid(uint32,可选)-容器命名空间中设备所有者的id
  • gid(uint32,可选)-容器命名空间中设备组的id

类似地,运行时还提供了一些其他配置选项。基于CRI字段的config.json部分进行定义, 包括securityContext: runAsUser/runAsGroup中定义的部分内容, 它通过以下方式成为POSIX平台用户结构的一部分:

  • uid(int, 必需): 指定容器名称空间中的用户ID
  • gid(int, 必需): 指定容器名称空间中的组ID
  • additionalGids(int数组, 可选): 在容器名称空间中指定要添加到进程的附加组id

然而,以config.json中的配置运行容器时将导致以下问题:

当运行容器既添加了设备,又通过runAsUser/runAsGroup设置了非根用户uid/gid的容器时,将导致以下问题: 容器用户进程没有使用设备的权限(即使设备的组id是允许非根用户组使用的)。

这是因为容器用户不属于那个主机组(例如,通过additionalGids)。

如何解决这个问题呢?

您可能已经从问题定义中注意到,至少可以通过手动将设备gid添加到supplementalGroups来解决问题。 或者在只有一个设备的情况下,将runAsGroup设置为设备的组id

然而,这是有问题的,因为设备的gid可能有不同的值,这取决于集群中的节点的发行版/版本。 例如,对于不同的发行版和版本,下面的命令返回不同的gid:

  • Fedora 33:
$ ls -l /dev/dri/
total 0
drwxr-xr-x. 2 root root         80 19.10. 10:21 by-path
crw-rw----+ 1 root video  226,   0 19.10. 10:42 card0
crw-rw-rw-. 1 root render 226, 128 19.10. 10:21 renderD128
$ grep -e video -e render /etc/group
video:x:39:
render:x:997:
  • Ubuntu 20.04:
$ ls -l /dev/dri/
total 0
drwxr-xr-x 2 root root         80 19.10. 17:36 by-path
crw-rw---- 1 root video  226,   0 19.10. 17:36 card0
crw-rw---- 1 root render 226, 128 19.10. 17:36 renderD128
$ grep -e video -e render /etc/group
video:x:44:
render:x:133:

所以说在securityContext中应该设置哪个数字?

此外,如果runAsGroup/runAsUser值是通过外部安全策略自动分配的,不能硬编码,该怎么办?

与带有fsGroup属性的卷不同,这些设备没有CRI运行时(或kubelet)能够使用的deviceGroup/deviceUser的正式概念。

如果使用由设备插件设置的容器注释(例如,io.kubernetes.cri.hostDeviceSupplementalGroup/)来获得自定义的OCIconf.jsonuid/gid值。 这将需要改变所有现有的设备插件,这不是理想的。

相反,这里有一个对终端用户更好的解决方案 -> 设备复用securityContextrunAsUserrunAsGroup值:

  • 设备uid对应runAsUser
  • 设备gid对应runAsGroup
{
        "type": "c",
        "path": "/dev/foo",
        "major": 123,
        "minor": 4,
        "fileMode": 438,
        "uid": <runAsUser>,
        "gid": <runAsGroup>
},

使用runc OCI运行时(在non-rootless模式下)下,设备将在容器名称空间中创建(通过mknod(2)),并且使用chmod(2)将所有权更改为runAsUser/runAsGroup

在容器名称空间中更新所有权是合理的,因为用户进程是唯一访问设备的进程。 只考虑runAsUser/runAsGroup,例如,容器中的USER设置当前被忽略。

containerdCRI-O中通过配置下面参数生效(设备权限从安全上下文中获取: 默认false)

device_ownership_from_security_context (bool)

使用样例



Copyright © weiliang 2021 all right reserved,powered by Gitbook本书发布时间: 2024-04-22 16:03:41

results matching ""

    No results matching ""