Skip to content

feat: selinux openshift support - #85

Open
coldzerofear wants to merge 3 commits into
mainfrom
feat/selinux-openshift-support
Open

feat: selinux openshift support#85
coldzerofear wants to merge 3 commits into
mainfrom
feat/selinux-openshift-support

Conversation

@coldzerofear

Copy link
Copy Markdown
Owner

No description provided.

coldzerofear and others added 3 commits July 31, 2026 17:09
对应 Project-HAMi/HAMi#961 / #2226 / #2227 那一类问题: 容器内的 LD_PRELOAD 库
需要读写宿主挂进来的目录,而 OpenShift 的任意 UID 与 SELinux 的标签是两条正交
的拦截路径。UID 那一轴本项目此前已经解决(EnsureDir 用 0777 且在 MkdirAll 之后
显式 Chmod,umask 削不掉),本次补齐标签这一轴,并把 OpenShift 的 SCC 补上。

## A. SELinux 重打标签

新增 pkg/util/selinux: 用 opencontainers/selinux 的 SetFileLabel 把路径打成
container_file_t。三点设计取舍:

- 级别用裸 s0 而非带 MCS 分类。容器会被分配随机的 s0:cN,cM,只能访问自己支配
  的级别; 这些目录要给我们既不知道也不控制其分类的 Pod 使用(DRA partition 场景
  还要给多个 Pod 同时用),s0 是唯一都够得着的写法。
- 失败全部静默(V(5) 日志)。SELinux 可能没开(此时整包是 no-op)、部署时可能没给
  特权、文件系统可能不支持 xattr —— 都不该让分配失败。库侧本来就是降级而非崩溃,
  所以打标签失败最坏也只是回到"本来就没有这些特性"的状态。
- 提供 VGPU_SELINUX_FILE_LABEL 环境变量作为逃生舱,置空即完全关闭。

覆盖方式刻意选在**唯一收敛点** util.EnsureDir 上,而不是逐个调用点补 ——
全部 9 处动态创建的挂载目录一次覆盖,以后新增挂载目录也不会漏。另外三处不走
EnsureDir 的显式补齐:

- PrepareDeviceUtilFile: watcher 目录与采样文件(os.MkdirAll 创建)
- registry server: registry 目录,以及 net.ListenUnix 建出来的 socket ——
  socket 在启动期递归打标签**之后**才创建,不显式补会漏掉,直接导致
  DevicePluginClientMode 在 SELinux 节点上连不上
- device-plugin / kubelet-plugin 启动时对 manager root 递归打标签,覆盖 init
  容器装进去的 libvgpu-control.so 与 ld.so.preload

递归遍历跳过 .host_proc(宿主 /proc 的容器内挂载点),且不跟随符号链接,避免把
标签打到宿主系统路径上。

## C. OpenShift SCC

两个 chart 各加 openshift.enabled(默认 false),开启后为节点级 DaemonSet 渲染
SecurityContextConstraints 并通过 RBAC 绑到其 ServiceAccount。不开时完全不渲染,
vanilla Kubernetes 行为不变。

刻意不覆盖的三类,因为它们不需要:
- scheduler extender 与 webhook: 不申请任何特权,restricted-v2 原样放行
- device-monitor sidecar: 与 DaemonSet 共用 ServiceAccount,已被覆盖
- **GPU 工作负载**: 这是与 HAMi#2226 诉求的关键差别 —— 挂给工作负载的目录既是
  0777(任意 UID 可写)又已重打标签(受限容器可达),所以工作负载不需要自己的 SCC,
  也不需要按命名空间逐个绑定

注: 环境无 Go 工具链与 helm,改动未经编译与渲染验证。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
穷尽核对全部 29 个挂载点(pluginapi.Mount 与 cdispec.Mount 的每一个 HostPath)
之后补三处遗漏,均为"目录已打标签但文件本身没有"的情况:

1. registry socket。目录由启动期递归覆盖,但 socket 是 net.ListenUnix 在那之后
   创建的,会漏。漏了的直接后果是 DevicePluginClientMode 在 SELinux 节点上连不上
   socket。同时把 registry 目录也显式补上——它走的是 os.MkdirAll 不是 EnsureDir。

2. vgpu.config(WriteResourceDataToDisk)与 devices.json(writeJSONFile)。这两个
   文件是容器直接 mmap/读取的。新建文件会从已打标签的父目录继承类型,但两处写入
   都是 O_TRUNC 语义——升级前就存在的旧文件只被截断、不重建,会沿用旧 inode 的
   旧标签。不依赖继承,在写入的收敛点显式补一次。

3. 顺带修两处缺失的 import(pkg/config/vgpu、pkg/deviceplugin/vgpu),否则编译不过。

核对结论(完整表见 commit 讨论):
- 目录: 全部经 util.EnsureDir 这一个收敛点,共 9 处动态创建点; 另有 watcher 与
  registry 两处走 os.MkdirAll,已显式补齐
- 文件: devices.json / vgpu.config / watcher 采样文件 / registry socket 均显式;
  pids.config 走继承(其父目录 EnsureDir 已打标签)
- 节点级产物(libvgpu-control.so、ld.so.preload)由启动期递归覆盖
- DRA 的 CDI 与 NRI 两条注入路径共用 ensurePartitionDirectories,同被覆盖
- 刻意不打标签: /proc、/dev/nvidia*、/dev/null、/sys/fs/cgroup、以及
  nvidia-cdi-hook(在宿主侧执行,不进工作负载容器)

另: device-monitor 无需任何额外处理。SCC 按 Pod 准入,sidecar 与主容器同 Pod 同
ServiceAccount 已被覆盖; 它也不创建任何宿主路径,纯读者。已在 vgpu-manager chart
的 values 注释里补上这条说明,与 DRA chart 对齐。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant