Capabilities 入门教程:基础实战篇
该系列文章总共分为三篇:
上篇文章介绍了 Linux capabilities 的诞生背景和基本原理,本文将会通过具体的示例来展示如何查看和设置文件的 capabilities。
Linux 系统中主要提供了两种工具来管理 capabilities:libcap
和 libcap-ng
。libcap
提供了 getcap
和 setcap
两个命令来分别查看和设置文件的 capabilities,同时还提供了 capsh
来查看当前 shell 进程的 capabilities。libcap-ng
更易于使用,使用同一个命令 filecap
来查看和设置 capabilities。
1. libcap
安装很简单,以 CentOS 为例,可以通过以下命令安装:
$ 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)
解释一下:
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+ip
和cap_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()
系统调用能够赋予新启动的进程其父进程没有的权限,最常见的例子就是通过 setuid
和 setgid
来设置程序进程的 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=true
和 allowPrivilegeEscalation=false
时,就不会开启 no_new_privs
属性。同理,设置了 capAdd=CAP_SYS_ADMIN
和 allowPrivilegeEscalation=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_CHOWN
和 CAP_DAC_OVERRIDE
capabilities 添加到 permitted
和 effective
集合:
$ 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 只会被添加到
permitted
和effective
集合。
查看文件的 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 被添加到
permitted
和effective
集合中”的文件。所以这里没有显示 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 标识。
Capabilities 入门教程:基础实战篇的更多相关文章
- Systemd 入门教程:实战篇
Systemd 入门教程:实战篇 上一篇文章,介绍了 Systemd 的主要命令,这篇文章主要介绍如何使用 Systemd 来管理我们的服务,以及各项的含义: 一.开机启动 对于那些支持 System ...
- Linux 命令详解(八)Systemd 入门教程:实战篇
Systemd 入门教程:实战篇 http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html
- Linux Capabilities 入门教程:概念篇
原文链接:Linux Capabilities 入门教程:概念篇 Linux 是一种安全的操作系统,它把所有的系统权限都赋予了一个单一的 root 用户,只给普通用户保留有限的权限.root 用户拥有 ...
- [Linux] Systemd 入门教程:实战篇
reference : http://www.ruanyifeng.com/blog/2016/03/systemd-tutorial-part-two.html 上一篇文章,我介绍了 Systemd ...
- Systemd入门教程:实战篇(转)
作者: 阮一峰 日期: 2016年3月 8日 上一篇文章,我介绍了 Systemd 的主要命令,今天介绍如何使用它完成一些基本的任务. 一.开机启动 对于那些支持 Systemd 的软件,安装的时候, ...
- 【转载】Systemd 入门教程:实战篇
作者: 阮一峰 日期: 2016年3月 8日 上一篇文章,我介绍了 Systemd 的主要命令,今天介绍如何使用它完成一些基本的任务. 一.开机启动 对于那些支持 Systemd 的软件,安装的时候, ...
- CentOS 7 之 Systemd 入门教程:实战篇
开机启动对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件. 如果你想让该软件开机启动,就执行下面的命令(以httpd.ser ...
- Linux Capabilities 入门教程:基础实战篇
该系列文章总共分为三篇: Linux Capabilities 入门教程:概念篇 Linux Capabilities 入门教程:基础实战篇 待续... 上篇文章介绍了 Linux capabilit ...
- Linux Capabilities 入门教程:进阶实战篇
原文链接:https://fuckcloudnative.io/posts/linux-capabilities-in-practice-2/ 该系列文章总共分为三篇: Linux Capabilit ...
随机推荐
- 采用线性回归方法降低双目测距到平面的误差(sklearn)
继上篇,为了改善标定板的深度信息: remove_idx1 = np.where(Z <= 0) remove_idx2 = np.where(Z > 500)#将Z轴坐标限定在0-500 ...
- SP Flash Tool版本对应MTK处理器型号速查(SP Flash Tool Download)
SP Flash Tool For Windows Download https://monkey8.pipipan.com/dir/19250369-32189369-ac1637/ SP Flas ...
- Acwing-120-防线(二分,前缀和)
链接: https://www.acwing.com/problem/content/122/ 题意: 达达学习数学竞赛的时候受尽了同仁们的鄙视,终于有一天......受尽屈辱的达达黑化成为了黑暗英雄 ...
- 京东POP店铺使用京东物流,如何拦截订单
先进入物流工作台:https://wl.jdwl.com/ 然后操作中心-销售订单查询 然后点击展开按钮 粘贴要查询的订单号 勾选订单,点击取消按钮
- 30 最小n个数
public class test30{ public static void main(String args[]){ int array[]={3,2,5,1,4}; int temp[] = f ...
- Python 操作 MySQL 数据库Ⅲ
数据库查询操作 Python查询Mysql使用 fetchone() 方法获取单条数据, 使用fetchall() 方法获取多条数据. fetchone(): 该方法获取下一个查询结果集.结果集是一个 ...
- 剑指Offer-快速排序
剑指Offer上的快速排序的Partition函数与我在数据结构书上学到的不一样,因此就想要探索下这两种不同的处理方式. 1.基本思想 快速排序的基本思想是基于分治法.在待排序表L[1...n]中任取 ...
- Housewife Wind
Housewife Wind 参考博客:POJ2763 Housewife Wind(树剖+线段树) 差不多是直接套线段树+树剖的板子,但是也有一些需要注意的地方 建树: void build() { ...
- linux 下使用命令查看jvm信息
java程序员除了编写业务代码之外,特别是项目上线之后,更需要关注的是系统的性能表现,这个时候就需要了解一下jvm的性能表现了,可以借助于java虚拟机自带的一些分析工具,主要有三个常用的命令. 1. ...
- TCP定时器 之 保活定时器
在用户进程启用了保活定时器的情况下,如果连接超过空闲时间没有数据交互,则保活定时器超时,向对端发送保活探测包,若(1)收到回复则说明对端工作正常,重置定时器等下下次达到空闲时间:(2) 收到其他回复, ...