文卿

https://lwn.net/Articles/726816/

内核文件capabilities当前在用户名字空间(user namespaces)上的使用有很多的缺陷。当前主要的问题集中在可执行文件的capabilities是全局的。当前有内核开发人员提交了一组补丁尝试让用户名字空间能够感知到capabilities,但是这组补丁也引发了关于这一机制工作方式的讨论。讨论的核心问题是文件capabilities如何指定给一组文件。

Linux Capabilities机制允许将一组特权授权一个进程,以便更细粒度的控制特权用户的权限,从而限制传统Unix中root用户的特权带来的安全问题。举个例子,一个非特权程序需要发送信号给另外一个不相关的进程时,只需要具备CAP_KILL权限,而并不需要获得root权限。

传统Unix系统,特权操作通过setuid(1)赋予普通用户。而在带有capabilities的系统上,只需要将可执行文件与对应权限关联即可。而上面提到的文件capabilities则在2.6.24内核上就已经进入到内核主线。

用户名字空间允许一组进程在名字空间中以root用户运行,而其实这个用户在名字空间外的root ID会映射到一个普通ID上来执行操作。为了执行一些特权操作,这个名字空间中root用户将需要执行的一些程序通过setuid设置特权操作。而这些设置了特权操作的程序在名字空间外则不再具备特权。同样的功能目前的文件capabilities则不具备。因为上面提到的全局视角问题。所有用户名字空间对某个可执行文件都具有相同的视角。同时因为名字空间中的进程实际上并不是运行在root名字空间,因此也无法修改文件capabilities。

当前文件capabilities是通过扩展属性(Extended Attribute, EA)来实现的,内部实际上是将这些文件capabilities保存在security.capability属性中。内核会对security.*进行特殊处理。只有特权程序(比如:具备CAP_SYS_ADMIN能力)才被允许修改这些属性。这也就解释了为什么容器内非特权程序是无法添加文件capabilities的原因。同时也可以看到,当前没有方法能够针对特定的用户名字空间来保存不同的EA。

Stefan Berger提交的补丁中通过扩展EA的语法来尝试解决上述问题。该方法会将一个root名字空间下的用户ID映射到特定用户空间上的UID 0。比如一个UID 1000的用户启动一个用户名字空间并作为root用户运行,则他可以访问UID 1000的所有文件。如果该用户在用户名字空间中尝试添加capabilities,则这个信息会被保存成

security.capability@uid=1000

在名字空间外,这个新属性没有任何效果。而在用户名字空间中,这个属性则显示为security.capability,因此容器中的文件可以按照授予的特权来运行。

当前的补丁并不是针对所有EA,而是涉及安全的一部分,比如security.capability以及security.selinux。当然随后security.selinux的特性被移除了,因为SELinux维护者Stephen Smalley指出当前的实现有问题。

Casey Schaufler反对这一补丁,原因是如果两个用户空间使用相同的UID,并且共享目录树,那么这些文件capabilities在两个名字空间中都是可见的。他认为使用UID作为关键字来映射文件capabilities是不正确的。他认为应该找个其他的持久ID与用户名字空间做关联,以解决上面的问题。

James Bottomley反对这一补丁的理由是在动态分配用户ID的容器上无法工作。他建议创建一个@uid前缀来解决动态分配的问题。

总之,虽然原始补丁问题多多,但是在最近几个版本我们就会看到支持用户名字空间的文件capabilities特性了。