中文内容
SELinux 卷标签变更已正式可用(以及在 v1.37 中可能产生的影响)
如果你在 Linux 上以强制模式运行启用 SELinux 的 Kubernetes,请提前规划:预计在未来某个版本(预计为 v1.37)中,SELinuxMount 特性门控将默认开启。这会让大多数工作负载的卷设置更快,但也可能以不易察觉的方式破坏那些仍依赖旧的递归重新标记模型的应用(例如,在同一节点上的特权 Pod 和非特权 Pod 之间共享一个卷)。Kubernetes v1.36 是审计你的集群并修复此变更或选择退出此变更的合适版本。
如果你的节点不使用 SELinux,那么对你来说不会有任何变化:当 SELinux 在 Linux 内核中不可用或被禁用时,kubelet 会跳过整个 SELinux 逻辑。你可以完全跳过本文。
这篇博客基于此前在 Kubernetes 1.27: Efficient SELinux Relabeling (Beta) 一文中描述的工作,其中介绍了 SELinuxMountReadWriteOncePod 特性门控。不过,要解决的问题仍然相同;本文将同样的方法扩展到了所有卷。
问题
启用了 Security Enhanced Linux(SELinux)的 Linux 系统使用附加到对象(例如文件和网络套接字)的标签来做出访问控制决策。过去,容器运行时会将 SELinux 标签应用于 Pod 及其所有卷。Kubernetes 只会将 Pod 的 securityContext 字段中的 SELinux 标签传递给容器运行时。
随后,容器运行时会递归更改 Pod 容器可见的所有文件上的 SELinux 标签。如果卷上有许多文件,这可能会很耗时,尤其是当该卷位于远程文件系统上时。
注意:
If a container usessubPath of a volume, only that subPath of the whole
volume is relabeled. This allows two Pods that have two different SELinux labels
to use the same volume, as long as they use different subpaths of it.如果 Pod 在 Kubernetes API 中未分配任何 SELinux 标签,容器运行时会分配一个唯一的随机标签,因此,即使某个进程可能逃逸出容器边界,也无法访问主机上任何其他容器的数据。容器运行时仍会使用这个随机 SELinux 标签递归地重新标记所有 Pod 卷。
Kubernetes 正在改进的内容
在堆栈支持的情况下,kubelet 可以使用 -o context=<label> 挂载卷,使内核无需递归遍历 inode,就能为该挂载上的所有 inode 应用正确的标签。该路径受功能标志控制,并且要求满足若干条件,其中包括 Pod 暴露足够的 SELinux 标签(例如 spec.securityContext.seLinuxOptions.level),以及卷驱动选择加入(对于 CSI,即 CSIDriver 字段 spec.seLinuxMount: true)。
该项目分阶段推出了这一功能:
- ReadWriteOncePod 卷由 SELinuxMountReadWriteOncePod 功能门控处理,该功能门控自 v1.28 起默认启用,并在 v1.36 中达到 GA。
- 更广泛的覆盖范围由 SELinuxMount 标志处理,并与 Pod 上的 spec.securityContext.seLinuxChangePolicy 字段配合使用。
如果一个 Pod 及其卷满足以下所有条件,Kubernetes 将直接以正确的 SELinux 标签挂载该卷。这样的挂载将在常量时间内完成,容器运行时无需递归地重新标记其中的任何文件。要发生这样的挂载,需要满足:
- 操作系统必须支持 SELinux。如果未检测到 SELinux 支持,kubelet 和容器运行时在 SELinux 方面不会执行任何操作。
- 必须启用特性门控 SELinuxMountReadWriteOncePod。如果你运行的是 Kubernetes v1.36,该特性会无条件启用。
- Pod 必须使用具有适用 accessModes 的 PersistentVolumeClaim:要么卷具有 accessModes: ["ReadWriteOncePod"],要么卷可以使用任何其他访问模式,前提是特性门控 SELinuxChangePolicy 和 SELinuxMount 均已启用,并且 Pod 的 spec.securityContext.seLinuxChangePolicy 设置为 nil(默认)或 MountOption。特性门控 SELinuxMount 处于 Beta 阶段,并且在 Kubernetes 1.36 中默认禁用。所有其他与 SELinux 相关的特性门控现在均已正式发布(GA)。如果禁用了这些特性门控中的任何一个,SELinux 标签将始终由容器运行时通过递归遍历卷(或其 subPath)来应用。
- Pod 必须在其安全上下文中至少分配 seLinuxOptions.level,或者该 Pod 中的所有容器都必须在其容器级安全上下文中设置该项。Kubernetes 将从操作系统默认值中读取默认的 user、role 和 type(通常为 system_u、system_r 和 container_t)。如果 Kubernetes 至少不知道 SELinux level,容器运行时将在卷挂载后分配一个随机 level。在这种情况下,容器运行时仍会递归地重新标记这些卷。
- 负责该卷的卷插件或 CSI 驱动程序支持使用 SELinux 挂载选项进行挂载。以下内置卷插件支持使用 SELinux 挂载选项进行挂载:fc 和 iscsi。支持使用 SELinux 挂载选项进行挂载的 CSI 驱动程序必须在其 CSIDriver 实例中通过设置 seLinuxMount 字段来声明此能力。由其他卷插件管理的卷,或由未设置 seLinuxMount: true 的 CSI 驱动程序管理的卷,将由容器运行时以递归方式重新标记。
破坏性变更
SELinuxMount 特性开关会以一种微妙的方式改变哪些卷可以在多个 Pod 之间共享。
以下两种情况都适用于递归重新标记:
- 两个具有不同 SELinux 标签的 Pod 共享同一个卷,但它们各自使用该卷的不同 subPath。
- 一个特权 Pod 和一个非特权 Pod 共享同一个卷。
在 SELinux 处于启用状态时,上述场景将无法在 Kubernetes 挂载的现代目标行为下正常工作。相反,其中一个 Pod 会卡在 ContainerCreating 状态,直到另一个 Pod 被终止。
第一种情况非常小众,实践中尚未见到。尽管第二种情况仍然相当少见,但在应用程序中已经观察到这种设置。Kubernetes v1.36 提供了指标和事件来识别这些 Pod,并允许集群管理员通过 Pod 字段 spec.securityContext.seLinuxChangePolicy 选择退出该挂载选项。
正文:seLinuxChangePolicy
新的 Pod 字段 spec.securityContext.seLinuxChangePolicy 用于指定如何将 SELinux 标签应用到所有 Pod 卷。在 Kubernetes v1.36 中,该字段是稳定版 Pod API 的一部分。
共有三种可用选择:
field not set (default)In Kubernetes v1.36, the behavior depends on whether theSELinuxMount feature gate is enabled. By default that feature gate is not enabled, and the SELinux label is applied recursively. If you enable that feature gate in your cluster, and all other conditions are met, labelling will be applied using the mount option.Recursivethe SELinux label is applied recursively. This opts out from using the mount option.MountOptionthe SELinux label is applied using the mount option, if all other conditions are met.
This choice is available only when the SELinuxMount feature gate is enabled.SELinux 警告控制器(可选)
Kubernetes v1.36 在控制平面中提供了一个新的控制器 selinux-warning-controller。该控制器在 kube-controller-manager 控制器内运行。要使用它,需要在 kube-controller-manager 命令行中传入 --controllers=*,selinux-warning-controller;同时,不能已显式将 SELinuxChangePolicy 特性门控覆盖为禁用。
该控制器会监视集群中的所有 Pod,并在发现两个 Pod 以与 SELinuxMount 特性门控不兼容的方式共享同一卷时发出一个 Event。所有此类存在冲突的 Pod 都会收到一个事件,例如:
SELinuxLabel "system_u:system_r:container_t:s0:c98,c99" conflicts with pod my-other-pod that uses the same volume as this pod with SELinuxLabel "system_u:system_r:container_t:s0:c0,c1". If both pods land on the same node, only one of them may access the volume.
当存在冲突的 Pod 运行在不同命名空间中时,实际的 Pod 名称可能会被隐藏,以防止跨命名空间边界泄露信息。
即使这些 Pod 未在同一节点上运行,该控制器也会报告此类事件,以确保无论 Kubernetes 调度器如何决策,所有 Pod 都能正常工作。它们下一次可能会运行在同一节点上。
此外,该控制器会发出指标 selinux_warning_controller_selinux_volume_conflict,列出 Pods 之间当前所有冲突。该指标带有用于识别发生冲突的 Pods 及其 SELinux 标签的标签,例如:
selinux_warning_controller_selinux_volume_conflict{pod1_name="my-other-pod",pod1_namespace="default",pod1_value="system_u:object_r:container_file_t:s0:c0,c1",pod2_name="my-pod",pod2_namespace="default",pod2_value="system_u:object_r:container_file_t:s0:c0,c2",property="SELinuxLabel"} 1
启用这个选择加入的控制器会带来一个安全影响:它可能会暴露命名空间名称,而命名空间名称始终存在于该指标中。Kubernetes 项目假定只有集群管理员才能访问 kube-controller-manager 指标。
建议的升级路径
为确保从 v1.36 平滑升级到启用 SELinuxMount 的版本(预计为 v1.37),我们建议你遵循以下步骤:
- 在 kube-controller-manager 中启用 selinux-warning-controller。
- 检查 selinux_warning_controller_selinux_volume_conflict 指标。它会显示 Pod 之间所有潜在的冲突。对于每个发生冲突的 Pod(Deployment、StatefulSet 等),要么应用选择退出(设置 Pod 的 spec.securityContext.seLinuxChangePolicy: Recursive),要么重新设计应用程序架构以消除此类冲突。例如,你的 Pod 真的需要以特权模式运行吗?
- 检查 volume_manager_selinux_volume_context_mismatch_warnings_total 指标。该指标由 kubelet 在实际启动某个 Pod 时发出:该 Pod 在 SELinuxMount 被禁用时可以运行,但在 SELinuxMount 被启用时将无法启动。此指标列出了会遇到真实冲突的 Pod 数量。遗憾的是,此指标不会将确切的 Pod 名称作为标签公开。完整的 Pod 名称仅在 selinux_warning_controller_selinux_volume_conflict 指标中可用。
- 在两个指标都已处理完毕后,升级到已启用 SELinuxMount 的 Kubernetes 版本。
考虑使用 MutatingAdmissionPolicy、变更准入 webhook,或 Kyverno、Gatekeeper 等策略引擎,将选择退出应用于命名空间中的所有 Pod 或整个集群。
启用 SELinuxMount 后,kubelet 将发出指标 volume_manager_selinux_volume_context_mismatch_errors_total,其中包含因 SELinux 标签与使用同一卷的现有 Pod 冲突而无法启动的 Pod 数量。如果启用了 selinux-warning-controller,具体的 Pod 名称仍应可在 selinux_warning_controller_selinux_volume_conflict 指标中获取。
进一步阅读
- KEP:使用挂载加速 SELinux 卷重新标记
- SELinux 卷重新标记功能门控
- 故事 3:集群升级
- 为 Pod 配置安全上下文——高效的 SELinux 卷重新标记和 selinux-warning-controller
致谢
如果你遇到问题、有反馈,或想要参与贡献,请在 Kubernetes Slack 的 #sig-node 和 #sig-storage 频道找到我们,或参加 SIG Node 或 SIG Storage 会议。
- ← 上一页
- 下一页 →