一个Pod缺少capabilities带来的小问题

前段时间,有团队的同学在边缘服务器上遇到问题,最终定位和Linux Capabilities有关,定位过程并不复杂,回顾过往算是第二次遇到类似问题,所以还是打算记录下来。

问题背景

业务有部署在客户侧的边缘服务器,我们会将服务通过云端控制下发到边缘服务器上,这些下沉的业务服务均以容器化的方式进行部署和管理。

在一个业务场景中,会在客户侧的边缘服务器插入一个硬件外设,业务服务初始化时调用厂商脚本安装外设驱动,但是在测试的过程中发现驱动安装一直失败,提示如下异常:

由于厂商脚本底层SDK未提供源码,无法通过源码进行问题定位,所以研发同学在这个问题上花了不少时间。

问题分析

从报错信息来看,问题点很好判断,脚本内会进行insmod/rmmod操作失败,提示的异常是Operation not permitted

看到这里,可能首先想到的就是权限不够,但是初步排查即使以root用户身份执行该脚本,也提示相同的异常信息,所以我们当时就想到应该是容器本身不具备某些资源或者权限。

但是这就让研发同学比较抓瞎,没有源码也不好判断具体缺少哪个行为或者权限。

这就到了掐指一算的环节了。

因为涉及到权限、资源层面的使用,大部分都需要通过系统调用进行操作,抛出的Operation not permitted大概率也是系统调用本身返回了无权限,因此只要看哪一个系统调用返回异常就可以了。

那就是时候掏出strace了。

最终发现是delete_module返回了Operation not permitted,追踪一下源码,很快可以找到问题根因。

执行delete_module需要判断是否具备CAP_SYS_MODULE能力,如果不具备则返回-EPERM,提示Operation not permitted

那么要解决这个问题,我们需要了解两个东西:

(1)CAP_SYS_MODULE是什么? --- Linux Capabilities (2)如何让容器具备这个能力? --- Pod Security Context

Linux Capabilities

为了能够进行权限检查,传统的UNIX将进程分为特权进程和非特权进程,其中特权进程可以绕过所有内核权限检查直接执行对应的操作,而非特权进程则需要根据进程认证和权限信息进行判断。

从Linux 2.2开始将超级用户的特权拆分成不同的单元,这些权能单元就是Linux Capabilities

官方文档
capabilities(7) - Linux manual page

CAP_SYS_MODULE就是Linux Capabilities中的一种,包含模块加载和卸载相关的权限能力。

Capabilities分配

既然将特权拆分成不同的Capabilities模块,那么就可以按需进行Capabilities分配,配置方式包含线程权能集(Thread Capabilities Sets)和文件权能集(File Capabilities),而两种权能集各有其分配计算方式、继承规则,最终共同作用决定是否具备权限进行操作。

其中,文件权能集从Linux 2.6.24开始支持。

  • Thread Capabilities Sets

每个进程都以下的几种权能集合,每个集合包含0到多个不同的权能。

类型 说明
Permitted 进程可获取的权限
Inheritable 从父进程继承传递给子进程的权限
Effective 进程的有效权限,内核基于该集合进行权限检查 (可以理解为最终真正生效的权限集)
Bounding 进程允许拥有的最大权限集 (Since Linux 2.6.25,在此之前是一个系统级属性限制所有线程)
Ambient 当前生效的权限,应用于非特权程序的当前进程或子进程

其中,通过fork()创建的子进程继承父进程的权能集。

  • File Capabilities

从Linux 2.6.24后,内核支持为一个可执行文件设置权能集,保存在security.capability中。

类型 说明
Permitted 当文件执行时,自动添加给进程的权能集
Inheritable 该集合与线程Inheritable集做与&操作,以确定进程哪些权能可被继承
Effective 非集合,仅是一个bit位。为1时进程Permitted集才可添加到进程Effective集中
  • 权能计算方式

有了进程和文件权能集,那么实际执行一个新的程序时,也有其对应的权限计算方式。

Pod Security Context

安全上下文(Security Context)定义 Pod 或 Container 的特权与访问控制设置,其中就包含Linux权能,能为进程赋予 root 用户的部分特权而非全部特权。

Kubernetes 为 Pod 或容器配置安全上下文

可针对Pod或容器层面进行配置,容器层面的官方配置示例如下:

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo-4
spec:
  containers:
  - name: sec-ctx-4
    image: xxxx:1.0
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_TIME"]

因此,结合我们的场景,只需要给容器增加对应的权能即可。

解决方案

要解决这个问题,只需要给对应的容器增加SYS_MODULE权能即可。

yaml 复制代码
....
spec:
  containers:
  - name: xxxx
    image: xxxx:1.0
    securityContext:
      capabilities:
        add: ["SYS_MODULE"]
....

这里注意,源码中的权能名是CAP_SYS_MODULE,但是实际在securityContext的时候对应权能名位SYS_MODULE

问题总结

综上所述,实际为容器不具备对应的权能,导致硬件外设驱动安装失败,只要加上SYS_MODULE权能即可。

但是实际上,驱动安装这个行为也并不适合在容器中完成,应该在服务器初始化阶段进行安装,将基础环境初始化与服务部署两个行为解耦开。

相关推荐
明月与玄武10 分钟前
快速掌握Django框架设计思想(图解版)
后端·python·django
陪我一起学编程10 分钟前
关于ORM增删改查的总结——跨表
数据库·后端·python·django·restful
南囝coding17 分钟前
这个 361K Star 的项目,一定要收藏!
前端·后端·github
虎鲸不是鱼37 分钟前
Spring Boot3流式访问Dify聊天助手接口
java·spring boot·后端·大模型·llm
onlooker666638 分钟前
Go语言底层(五): 深入浅出Go语言的ants协程池
开发语言·后端·golang
武子康1 小时前
Java-46 深入浅出 Tomcat 核心架构 Catalina 容器全解析 启动流程 线程机制
java·开发语言·spring boot·后端·spring·架构·tomcat
寻月隐君1 小时前
Solana 开发实战:Rust 客户端调用链上程序全流程
后端·rust·web3
丘山子2 小时前
别再滥用 None 了!这才是 Python 处理缺失值的好方法
后端·python·面试
error_cn2 小时前
postgresql视图与触发器
后端
知秋丶2 小时前
Spring-rabbit重试消费源码分析
java·后端·spring