​CAPABILITIES(7) Linux 程序员手册​

简介

为了执行权限检查,传统的 UNIX
实现区分了两类进程:
特权进程(其有效用户 ID 为 0,称为
作为超级用户或 root)和非特权进程(其
有效 UID 非零)。特权进程绕过所有
内核权限检查,而非特权进程是
根据进程的完整权限检查
凭据(通常:有效 UID、有效 GID 和
补充组列表)。

从内核 2.2 开始,Linux 划分权限
传统上与超级用户相关联成不同的单元,
称为功能,可以独立启用和
禁用。功能是每个线程的属性。
为了执行权限检查, 区分两类进程:特权进程(其有效用户标识为 0,也就是超级用户 root)和非特权进程(其有效用户标识为非零)。 特权进程绕过所有内核权限检查,而非特权进程则根据进程凭证(通常为有效 UID,有效 GID 和补充组列表)进行完全权限检查。

以常用的 passwd 命令为例,修改用户密码需要具有 权限,而普通用户是没有这个权限的。但是实际上普通用户又可以修改自己的密码,这是怎么回事?在 Linux 的权限控制机制中,有一类比较特殊的权限设置,比如 SUID。因为程序文件 /bin/passwd 被设置了 SUID 标识,所以普通用户在执行 passwd 命令时,进程是以 passwd 的所有者,也就是 root 用户的身份运行,从而修改密码。

SUID 虽然可以解决问题,却带来了安全隐患。当运行设置了 SUID 的命令时,通常只是需要很小一部分的特权,但是 SUID 给了它 root 具有的全部权限。因此一旦 被设置了 SUID 的命令出现漏洞,就很容易被利用。也就是说 SUID 机制在增大了系统的安全攻击面。

Linux 引入了 capabilities 机制对 root 权限进行细粒度的控制,实现按需授权,从而减小系统的安全攻击面。

权限检查的过程就变成了:在执行特权操作时,如果进程的有效身份不是 root,就去检查是否具有该特权操作所对应的 capabilites,并以此决定是否可以进行该特权操作。比如要向进程发送信号(kill()),就得具有 capability <strong>CAP_KILL</strong>;如果设置系统时间,就得具有 capability <strong>CAP_SYS_TIME</strong>。

权限表,见上方的超链接

如何使用 capabilities

getcap​ 命令和 ​setcap​ 命令分别用来查看和设置程序文件的 capabilities 属性 ![image-20220304120508489](C:\Users\keda\AppData\Roaming\Typora\typora-user-images\image-20220304120508489.png)

sudo setcap cap_net_admin,cap_net_raw-p /bin/ping
$ getcap /bin/ping
/bin/ping =

$ ping www.baidu.com
ping: socket: Operation not permitted
$ sudo setcap cap_net_admin,cap_net_raw+p /bin/ping
$ getcap /bin/ping
/bin/ping = cap_net_admin,cap_net_raw+p
$ ping www.baidu.com
PING www.baidu.com.w.kunlungr.com (115.223.14.188) 56(84) bytes of data.

命令中的 ep 分别表示 Effective 和 Permitted 集合(接下来会介绍),+ 号表示把指定的 capabilities 添加到这些集合中,- 号表示从集合中移除(对于 Effective 来说是设置或者清除位)。

程序文件的 capabilities

在上面的示例中我们通过 setcap 命令修改了程序文件 /bin/ping 的 capabilities。在可执行文件的属性中有三个集合来保存三类 capabilities,它们分别是:

  • Permitted
  • Inheritable
  • Effective
在进程执行时,<strong>Permitted 集合</strong>中的 capabilites 自动被加入到进程的 Permitted 集合中。
<strong>Inheritable 集合</strong>中的 capabilites 会与进程的 Inheritable 集合执行与操作,以确定进程在执行 execve 函数后哪些 capabilites 被继承。
<strong>Effective</strong> 只是一个 bit。如果设置为开启,那么在执行 execve 函数后,Permitted 集合中新增的 capabilities 会自动出现在进程的 Effective 集合中。

进程的 capabilities

进程中有五种 capabilities 集合类型,分别是:

  • Permitted
  • Inheritable
  • Effective
  • Bounding
  • Ambient

/proc/[pid]/status 文件中包含了进程的五个 capabilities 集合的信息,我们可以通过下面的命名查看当前进程的 capabilities 信息:

cat /proc/1/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
CapAmb: 0000000000000000

1 是 pid
~]# capsh --decode=0000001fffffffff
0x0000001fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,35,36

总结

Capabilities 为解决这一问题而生,它能提供精细粒度的特权集,从而有效的减小系统的安全攻击面。

为K8s Container 设置权能

apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-4
spec:
containers:
- name: sec-ctx-4
image: gcr.io/google-samples/node-hello:1.0
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_TIME"] # 添加
drop: # 删除
- KILL

说明

Linux 权能常数定义的形式为 ​​CAP_XXX​​​。但是你在 Container 清单中列举权能时, 要将权能名称中的 ​​CAP_​​​ 部分去掉。例如,要添加 ​​CAP_SYS_TIME​​​,可在权能 列表中添加 ​​SYS_TIME​​。

在 Kubernetes 中通过 ​​sercurityContext.capabilities​​​ 进行配置容器的 ​​Capabilities​​​,当然最终还是通过 Docker 的 ​​libcontainer​​​ 去借助 ​​Linux kernel capabilities​​ 实现的权限管理。

Docker Container Capabilities

我们说 Docker 容器本质上就是一个进程,所以理论上容器就会和进程一样会有一些默认的开放权限,默认情况下 Docker 会删除必须的 `capabilities` 之外的所有 `capabilities`,因为在容器中我们经常会以 root 用户来运行,使用 `capabilities` 现在后,容器中的使用的 root 用户权限就比我们平时在宿主机上使用的 root 用户权限要少很多了,这样即使出现了安全漏洞,也很难破坏或者获取宿主机的 root 权限,所以 Docker 支持 `Capabilities` 对于容器的安全性来说是非常有必要的。

不过我们在运行容器的时候可以通过指定 `--privileded` 参数来开启容器的超级权限,这个参数一定要慎用,因为他会获取系统 root 用户所有能力赋值给容器,并且会扫描宿主机的所有设备文件挂载到容器内部,所以是非常危险的操作。

但是如果你确实需要一些特殊的权限,我们可以通过 `--cap-add` 和 `--cap-drop` 这两个参数来动态调整,可以最大限度地保证容器的使用安全。下面表格中列出的 `Capabilities` 是 Docker 默认给容器添加的,我们可以通过 `--cap-drop` 去除其中一个或者多个