一个与 Capabilities 相关的问题

·

2 min read

问题描述

在使用 k8s 部署容器中遇到了一些权限问题。

我想让当前容器可以生成 core dump 文件以便后续 debug,因此我需要设置允许的 core 文件的大小,我使用 ulimit -c 该命令来修改系统限制。

在执行以下命令时遇到了一些问题。

root$ ulimit -c 10000
bash: ulimit: core file size: cannot modify limit: Operation not permitted

看起来似乎是权限不够。但是我使用的是 root 用户,问题看起来有些棘手。

问题解决

实际上我是在 ssh 环境下执行的 ulimit -c 命令,需要考虑是否是 ssh 导致的该问题。该 sshd 进程是在容器的启动命令中以 root 身份,执行的如下命令。

sudo /usr/sbin/sshd

在上网搜索一番后,没有找到解决方法,虽然了解到一些 pam 相关的权限控制相关的东西,但是对我的问题没有帮助。

ld 说之前碰到过这个问题,并且在之前的系统中解决过,只是没有记录下解决流程,一个简单的解决方法是,启动 sshd 时去掉 sudo,因为本身就以 root 用户权限执行。不过在原来的系统中由于不是 root 用户,所以并不是通过该方法解决的,具体解决方法忘记了。

实际操作了一下,在去掉sudo后,再执行ulimit命令不再出现权限问题。

问题是不是出在sudo上呢,在容器中执行的sudo和直接在容器中以root用户执行命令的区别在哪?

经过搜索引擎的搜索以及对之前系统和现在系统申请容器时参数的比较,我发现新老系统在容器模板中的securityContext参数存在差异。在原来的系统中存在如下参数:

securityContext:
      capabilities:
        add:
        - CAP_SYS_RESOURCE

在增加了该参数到新系统后,ulimit -c 能够正常运行了!

Capabilities

那么问题来了,为什么会出现这样的现象呢?

这是由于 docker 容器中的 root 用户其实并不能完全拥有宿主机 root 用户的全部权限。如果让容器中的用户拥有全部权限,会对系统的安全埋下重要隐患,而且容器中的进程运行并不一定需要系统的全部权限。因此,docker 使用了 capabilities 这一 linux 系统原生的进程权限控制机制,对容器的权限进行控制。

Capabilities 机制

linux 中有两种 capabilities,分别是进程的 capabilities 和文件的 capabilities。

进程的 capabilities

每个进程有五个 capabilities 集合,每个集合用 64 位掩码表示,以 16 进制格式显示,分别为:

  • Permitted
  • Effective
  • Inheritable
  • Bounding
  • Ambient

以下分别介绍每个集合的作用。

Permitted

EffectiveInheritable 集合的有限超集。进程可以通过 capset() 这一系统调用来在 Effective 或者 Inheritable 集合中添加和删除 capability。

Effective

内核用来检查权限的集合,是实际上的使能集合。

Inheritable

在执行 execve() 这一系统调用运行文件时,Inheritable 集合将会保留。同时如果可执行文件设置了 inheritable 位,Inheritable 集合中的 capabilities 会被添加到 Permitted 集合中。但是需要注意的是,如果是非 root 用户调用 execve(),一般不会保留 Inheritable 集合,因此主程序如果需要运行一些帮助程序,需要使用 Ambient 集合来提权。

Bounding

用来限制 execve() 过程可获取的 capabilities。

Ambient

execve() 过程执行非特权程序时仍然保留的集合。保证其中的 capabilites 必须同时在 PermittedInheritable 集合中。

如果执行了 SUID 或 SGID 程序(会改变 UID,GID),或者设置了 file capabilities 的程序,会直接清空该集合。在 execve() 过程中,Ambient 集合的元素会被加到 PermittedEffective 集合中。

在执行 fork() 系统调用时,会复制父进程的这五个集合。

使用 capset() 系统调用,可以修改进程自己的 capabilities 集合。

文件的 capabilities

通过 setcap() 可以将 capability 集合与可执行文件关联到一起。这些集合被存在文件的扩展属性中。

对这一扩展属性进行操作需要进程具有 CAP_SETFCAP 的 capability。

这一属性决定了用户 execve() 之后进程的 capabilities。

一共包含三个集合:

Permitted

自动添加到进程的 Permitted 集合中。

Inheritable

与进程的 Inheritable 集合做与运算,将其加入到进程的 Permitted 集合中。

Effective

不是一个集合,而是单个单独的位。设置后,execve() 系统调用中所有新加入到进程 Permitted 集合的 capabilities 也会被加入到 Effective 集合,否则则不会加入。

通过使能该位,可以将通过另外两个集合的特性在 execve() 中添加到 Permitted 集合的 capabilities 也加到 Effective 集合中。

问题深究

在学习了以上机制后,猜测是 root 下直接执行和 sudo 执行时,sshd 进程的 capabilities 存在差异。因此需要对两种不同情况下的 capabilities 进行检查,以验证猜想。

验证 1

在 k8s 中启动两个容器,使用不同的启动命令,设置不同的 securityContext,输出在 root 和 sudo 环境下分别的 capabilities 集合。

使用命令

cat /proc/self/status

其中包含当前进程的 capabilities 集合信息 根据 securityContext 中是否包含 CAP_SYS_RESOURCE,以及使用 root 和直接用 sudo 共四种情况

root & no CAP_SYS_RESOURCE

CapInh:    00000000a80425fb
CapPrm:    00000000a80425fb
CapEff:    00000000a80425fb
CapBnd:    00000000a80425fb
CapAmb:    0000000000000000

root & CAP_SYS_RESOURCE

CapInh:    00000000a90425fb
CapPrm:    00000000a90425fb
CapEff:    00000000a90425fb
CapBnd:    00000000a90425fb
CapAmb:    0000000000000000

sudo & CAP_SYS_RESOURCE

CapInh:    00000000a80425fb
CapPrm:    00000000a80425fb
CapEff:    00000000a80425fb
CapBnd:    00000000a80425fb
CapAmb:    0000000000000000

sudo & CAP_SYS_RESOURCE

CapInh:    00000000a90425fb
CapPrm:    00000000a90425fb
CapEff:    00000000a90425fb
CapBnd:    00000000a90425fb
CapAmb:    0000000000000000

根据以上结果来看,似乎没有差异,因此必定与sshd本身的行为相关。

验证 2

查看在不开启 securityContext ,两种运行 sshd 的方式下,通过 ssh 连接到容器后的终端进程的 capabilities。

root

CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000

sudo

CapInh: 00000000a80425fb
CapPrm: 00000000a80425fb
CapEff: 00000000a80425fb
CapBnd: 00000000a80425fb
CapAmb: 0000000000000000

似乎还是没有差异。。。

那应该是别的原因,在 securityContext 中增加 CAP_SYS_RESOURCE 应该只是一种解决方式。根本原因还是 sudo 和 root 下执行的环境有差异,继续研究吧。。

验证3

/usr/sbin/sshd 入手 首先

ls -l /usr/sbin/sshd
-rwsr-sr-x 1 root root 790952 Aug 12  2021 /usr/sbin/sshd

可以看到这是一个 SUID 的程序,即运行时会以 superuser 的身份执行所有的操作。 麻了,最终也没找到问题在于什么地方。。。

后记

最终,我并没有找个这个问题产生的根本原因,如果你有什么想法,请联系我,谢谢🙏🏻。

Reference