Linux Capabilities 入门教程:基础实战篇

该系列文章总共分为三篇:html

上篇文章介绍了 Linux capabilities 的诞生背景和基本原理,本文将会经过具体的示例来展现如何查看和设置文件的 capabilities。github

Linux 系统中主要提供了两种工具来管理 capabilities:libcaplibcap-nglibcap 提供了 getcapsetcap 两个命令来分别查看和设置文件的 capabilities,同时还提供了 capsh 来查看当前 shell 进程的 capabilities。libcap-ng 更易于使用,使用同一个命令 filecap 来查看和设置 capabilities。docker

1. libcap

安装很简单,以 CentOS 为例,能够经过如下命令安装:shell

$ yum install -y libcap

若是想查看当前 shell 进程的 capabilities,能够用 capsh 命令。下面是 CentOS 系统中的 root 用户执行 capsh 的输出:安全

$ capsh --print

Current: = 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,cap_wake_alarm,cap_block_suspend,cap_audit_read+ep
Bounding set =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,cap_wake_alarm,cap_block_suspend,cap_audit_read
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)

解释一下:bash

  • Current : 表示当前 shell 进程的 Effective capabilities 和 Permitted capabilities。能够包含多个分组,每个分组的表示形式为 capability[,capability…]+(e|i|p),其中 e 表示 effective,i 表示 inheritable,p 表示 permitted。不一样的分组之间经过空格隔开,例如:Current: = cap_sys_chroot+ep cap_net_bind_service+eip。再举一个例子,cap_net_bind_service+e cap_net_bind_service+ipcap_net_bind_service+eip 等价。微信

  • Bounding set : 这里仅仅表示 Bounding 集合中的 capabilities,不包括其余集合,因此分组的末尾不用加上 +...

  • Securebits : 我也没搞清楚这是个什么鬼。

这个命令输出的信息比较有限,完整的信息能够查看 /proc 文件系统,好比当前 shell 进程就能够查看 /proc/$$/status。其中一个重要的状态就是 NoNewPrivs,能够经过如下命令查看:

grep NoNewPrivs /proc/$$/status

NoNewPrivs:    0

根据 prctl(2) 中的描述,自从 Linux 4.10 开始,/proc/[pid]/status 中的 NoNewPrivs 值表示了线程的 no_new_privs 属性。至于 no_new_privs到底是干吗的,下面我单独解释一下。

no_new_privs

通常状况下,execve() 系统调用可以赋予新启动的进程其父进程没有的权限,最多见的例子就是经过 setuidsetgid 来设置程序进程的 uid 和 gid 以及文件的访问权限。这就给不怀好意者钻了很多空子,能够直接经过 fork 来提高进程的权限,从而达到不可告人的目的。

为了解决这个问题,Linux 内核从 3.5 版本开始,引入了 no_new_privs 属性(实际上就是一个 bit,能够开启和关闭),提供给进程一种可以在 execve() 调用整个阶段都能持续有效且安全的方法。

  • 开启了 no_new_privs 以后,execve 函数能够确保全部操做都必须调用 execve() 判断并赋予权限后才能被执行。这就确保了线程及子线程都没法得到额外的权限,由于没法执行 setuid 和 setgid,也不能设置文件的权限。

  • 一旦当前线程的 no_new_privs 被置位后,不论经过 fork,clone 或 execve 生成的子线程都没法将该位清零。

Docker 中能够经过参数 --security-opt 来开启 no_new_privs 属性,例如:docker run --security-opt=no_new_privs busybox。下面经过一个例子来体会一下 no_new_privs 属性的做用。

首先撸一段 C 代码,显示当前进程的有效用户 id:

$ cat testnnp.c

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char *argv[])
{
        printf("Effective uid: %d\n", geteuid());
        return 0;
}
$ make testnnp
cc     testnnp.c   -o testnnp

将可执行文件打入 docker 镜像中:

FROM fedora:latest
ADD testnnp /root/testnnp
RUN chmod +s /root/testnnp
ENTRYPOINT /root/testnnp

构建镜像:

$ docker build -t testnnp .
Step 1 : FROM fedora:latest
 ---> 760a896a323f
Step 2 : ADD testnnp /root/testnnp
 ---> 6c700f277948
Removing intermediate container 0981144fe404
Step 3 : RUN chmod +s /root/testnnp
 ---> Running in c1215bfbe825
 ---> f1f07d05a691
Removing intermediate container c1215bfbe825
Step 4 : ENTRYPOINT /root/testnnp
 ---> Running in 5a4d324d54fa
 ---> 44f767c67e30
Removing intermediate container 5a4d324d54fa
Successfully built 44f767c67e30

下面来作两个实验,先在没有开启 no-new-privileges 的状况下启动容器:

$ docker run -it --rm --user=1000  testnnp
Effective uid: 0

从输出结果来看,只要给可执行文件设置了 SUID 标识,即便咱们使用普通用户(UID=1000)来运行容器,进程的有效用户也会变成 root。

接着在开启 no-new-privileges 的前提下启动容器,以防止执行设置了 SUID 标识的可执行文件进行 UID 转换:

$ docker run -it --rm --user=1000 --security-opt=no-new-privileges testnnp
Effective uid: 1000

能够看到,开启了 no_new_privs 属性以后,即便可执行文件设置了 SUID 标识,线程的有效用户 ID 也不会变成 root。这样即便镜像中的代码有安全风险,仍然能够经过防止其提高权限来避免受到攻击。

Kubernetes 也能够开启 no_new_privs,不过逻辑稍微复杂一点。当 Pod 的 SecurityContext 定义下的 allowPrivilegeEscalation 字段值为 false 时(默认就是 false),若是不知足如下任何一个条件,就会开启 no_new_privs 属性:

  • 设置了 privileged=true

  • 增长了 CAP_SYS_ADMIN capabilities,即 capAdd=CAP_SYS_ADMIN

  • 以 root 用户运行,即 UID=0

例如,当设置了 privileged=trueallowPrivilegeEscalation=false 时,就不会开启 no_new_privs 属性。同理,设置了 capAdd=CAP_SYS_ADMINallowPrivilegeEscalation=false 也不会开启 no_new_privs 属性。

管理 capabilities

能够经过 getcap 来查看文件的 capabilities,例如:

$ getcap /bin/ping /usr/sbin/arping

/bin/ping = cap_net_admin,cap_net_raw+p
/usr/sbin/arping = cap_net_raw+p

也可使用 -r 参数来递归查询:

$ getcap -r /usr 2>/dev/null

/usr/bin/ping = cap_net_admin,cap_net_raw+p
/usr/bin/newgidmap = cap_setgid+ep
/usr/bin/newuidmap = cap_setuid+ep
/usr/sbin/arping = cap_net_raw+p
/usr/sbin/clockdiff = cap_net_raw+p

若是想查看某个进程的 capabilities,能够直接使用 getpcaps,后面跟上进程的 PID:

$ getpcaps 1234

若是想查看一组相互关联的线程的 capabilities(好比 nginx),能够这么来看:

$ getpcaps $(pgrep nginx)

这里你会看到只有主线程才有 capabilities,子线程和其余 workers 都没有 capabilities,这是由于只有 master 才须要特殊权限,例如监听网络端口,其余线程只须要响应请求就行了。

设置文件的 capabilities 可使用 setcap,语法以下:

$ setcap CAP+set filename

例如,将 CAP_CHOWNCAP_DAC_OVERRIDE capabilities 添加到 permittedeffective 集合:

$ setcap CAP_CHOWN,CAP_DAC_OVERRIDE+ep file1

若是想移除某个文件的 capabilities,可使用 -r 参数:

$ setcap -r filename

2. libcap-ng

安装也很简单,以 CentOS 为例:

$ yum install libcap-ng-utils

用法

libcap-ng 使用 filecap 命令来管理文件的 capabilities。有几个须要注意的地方:

  • filecap 添加删除或查看 capabilities 时,capabilities 的名字不须要带 CAP_ 前缀(例如,使用 NET_ADMIN 代替 CAP_NET_ADMIN);

  • filecap 不支持相对路径,只支持绝对路径;

  • filecap 不容许指定 capabilities 做用的集合,capabilities 只会被添加到 permittedeffective 集合。

查看文件的 capabilities:

$ filecap /full/path/to/file

递归查看某个目录下全部文件的 capabilities:

$ filecap /full/path/to/dir

例如:

$ filecap /usr/bin

file                 capabilities
/usr/bin/newgidmap     setgid
/usr/bin/newuidmap     setuid

注意: filecap 只会显示“capabilities 被添加到 permittedeffective 集合中”的文件。因此这里没有显示 ping 和 arping。

递归查看整个系统全部文件的 capabilities:

$ filecap /
# or
$ filecap -a

设置文件的 capabilities 语法以下:

$ filecap /full/path/to/file cap_name

例如:

$ filecap /usr/bin/tac dac_override

移除某个文件的 capabilities:

$ filecap /full/path/to/file none

3. 总结

本文经过两种工具演示了如何对可执行文件的 capabilities 进行管理,并以 docker 为例,展示了 no_new_privs 的强大之处。若是条件容许,推荐你们之后尽可能用 capabilities 来替代完整的 root 权限或者设置 SUID 标识。

4. 参考资料

微信公众号

扫一扫下面的二维码关注微信公众号,在公众号中回复◉加群◉便可加入咱们的云原生交流群,和孙宏亮、张馆长、阳明等大佬一块儿探讨云原生技术

原文出处:https://www.cnblogs.com/ryanyangcs/p/11798292.html

相关文章
相关标签/搜索