栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

linux capability详解与容器中的capability

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

linux capability详解与容器中的capability

linux capability详解
  • capability概述
  • 查看当前用户的权限
  • 进程的权限
  • 在进程内部进行用户切换(进程内调用setuid和setgid)
    • 测试内核代码
  • 文件权限
    • 查看某个文件的权限
    • 为某个文件赋权
  • 进程创建子进程的时候的权限

capability概述

在许多文章中都有讲到这部分,本文不做过多解释。自行百度。

capabilities(7) — Linux manual page——官方权威!!!
Linux Capabilities 入门教程:概念篇——米开朗基杨
Linux Capabilities 入门教程:基础实战篇——米开朗基杨
Linux Capabilities 入门教程:进阶实战篇——米开朗基杨
Linux capability详解——弥敦道人-CSDN

在Linux内核2.2之前,为了检查进程权限,将进程区分为两类:特权进程(euid=0)和非特权进程。特权进程(通常为带有suid的程序)可以获取完整的root权限来对系统进行操作。

在linux内核2.2之后引入了capabilities机制,来对root权限进行更加细粒度的划分。如果进程不是特权进程,而且也没有root的有效id,系统就会去检查进程的capabilities,来确认该进程是否有执行特权操作的的权限。

可以通过man capabilities来查看具体的capabilities。

linux一共由5种权限集合。

Permitted ——可以赋予别人的权限。在下文中用大写P简称该权限
Effective ——当前有限的权限(真正实行权限的东西)。在下文中用大写E简称该权限
Inheritable ——可继承的权限。在下文中用大写I简称该权限
Bounding ——边界权限。在下文中用大写B简称该权限
Ambient——环境权限。在下文中用大写A简称该权限

查看当前用户的权限

查看/proc/$$/status文件中的Cap部分

普通用户

[10307750@zte.intra@LIN-29076BB8489 ~]$ cat /proc/$$/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

root用户

[root@LIN-29076BB8489 ~]# cat /proc/$$/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

CapInh对应上文的I
CapPrm对应上文的P
CapEff对应上文的E
CapBnd对应上文的B
CapAmb对应上文的A

进程的权限

下文中的进程权限用pP、pI、pE、pB、pA来分别对应进程的P、I、E、B、A

首先创建一个进程,sleep进程。sleep 100秒。并且在后台运行。(末尾 &表示后台运行)

[10307750@zte.intra@LIN-29076BB8489 ~]$ sleep 100 &
[1] 1968

可以看到该进程的pid为1968,查看该进程的状态,(位置在/proc/"pid"/status)抓取capability部分。
/proc/pid号/status中记录了该pid进程的状态,包括了该进程的权限(capability)

如果不知道进程号,可以使用ps -ef命令来输出所有的进程,然后通过grep命令来搜索想要的信息。
例如本例子中,则可以

[10307750@zte.intra@LIN-29076BB8489 ~]$ ps -ef | head -1; ps -ef | grep sleep
UID        PID  PPID  C STIME TTY          TIME CMD
root      1595  1638  0 10:35 ?        00:00:00 sleep 60
1030775+  1968  1896  0 10:35 pts/1    00:00:00 sleep 100
1030775+  2065 59302  0 10:35 ?        00:00:00 sleep 5
1030775+  2175  1896  0 10:35 pts/1    00:00:00 grep --color=auto sleep

head -1的意思是输出表头,就是UID PID PPID C STIME TTY TIME CMD那一行。

[10307750@zte.intra@LIN-29076BB8489 ~]$ cat /proc/1968/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

可以看到,该进程的只有B有权限,其他所有集合均没有权限。与该用户的权限是一致的。至于为什么,下文会说。(不是简单的全部复制过来哦~)

我们继续看root用户的。

[10307750@zte.intra@LIN-29076BB8489 ~]$ sudo -i
[root@LIN-29076BB8489 ~]# cat /proc/$$/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

可以看到root用户的权限,只有I和A没有,其他权限都有。与root用户本身的权限是一致的。至于为什么,下文会说。(同样不是简单的全部复制过来哦~)

在进程内部进行用户切换(进程内调用setuid和setgid)

当一个进程在执行过程中发生用户切换的时候(在进程的执行代码中,调用了系统调用setuid和setgid)那么进程的capability也会发生相应的变化。
内核代码阅读——一定要收藏啊啊啊!!!

在内核中处理这部分的代码如下:

内核代码位置/security/commoncap.c:1087行

static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old)
{
	kuid_t root_uid = make_kuid(old->user_ns, 0);

	if ((uid_eq(old->uid, root_uid) ||
	     uid_eq(old->euid, root_uid) ||
	     uid_eq(old->suid, root_uid)) 				//这3个,表示进程原来的用户是root用户
	     &&
	    (!uid_eq(new->uid, root_uid) &&
	     !uid_eq(new->euid, root_uid) &&
	     !uid_eq(new->suid, root_uid))) 			//这3个,表示进程限制的用户不是root用户
	     {
		if (!issecure(SECURE_KEEP_CAPS)) {			//如果没有设置KEEP_CAPS标志,则清除P和E权限集合
			cap_clear(new->cap_permitted);
			cap_clear(new->cap_effective);
		}

		
		cap_clear(new->cap_ambient);				//不管是不是root,统统清除A
	}
	if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid))
		cap_clear(new->cap_effective);				//曾经是root,现在切换成非root,则清除E
	if (!uid_eq(old->euid, root_uid) && uid_eq(new->euid, root_uid))
		new->cap_effective = new->cap_permitted;	//曾经是非root,现在切换成root,则E=P
}

上述内核代码主要的功能总结如下:

进程以前是root,切换成非root用户以后。如果没有设置KEEP_CAPS标志,则清除E和P权限集。
如果设置了KEEP_CAPS标志,则保留P权限集。

总而言之,只要发生了从root到普通用户切换,E的权限都会被清除掉,P的权限则视是否设置了KKEP_CAPS标志情况而定。

测试内核代码

本文例子中使用golang编程语言。

代码文件名:main.go

package main

import (
	"fmt"
	"syscall"
	"time"
)

//SetKeepCaps 表示设置保留权限(capability)标志
func SetKeepCaps() error {
	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 1, 0); err != 0 {
		return err
	}

	return nil
}

//ClearKeepCaps 表示设置不保留权限(capability)标志
func ClearKeepCaps() error {
	if _, _, err := syscall.RawSyscall(syscall.SYS_PRCTL, syscall.PR_SET_KEEPCAPS, 0, 0); err != 0 {
		return err
	}

	return nil
}

func main() {

	fmt.Println("Hello world!")
	fmt.Println("before set, the uid is ", syscall.Getuid())
	fmt.Println("before set, the gid is ", syscall.Getgid())

	fmt.Println("|***********************************|")

	if err := SetKeepCaps(); err != nil {			//设置内核代码中的KEEP_CAPS标志
		fmt.Println(err)
		return
	} else {
		fmt.Println("*     secessfully set keep caps     *")
	}
	
	fmt.Println("|***********************************|")

	syscall.Setgid(1000)							//切换组,切换到组id为1000的组上
	syscall.Setuid(10001)							//切换用户,切换到id为10001的用户上
	fmt.Println("after set, the uid is ", syscall.Getuid())	//输出uid
	fmt.Println("after set, the gid is ", syscall.Getgid())	//输出gid
	
	time.Sleep(100 * time.Second)			//程序挂起100s,在这个时间内,可以新建一个命令行(bash)
											//窗口来查看这个进程的权限(capability)
}

上述代码实现的功能:

  1. 首先,设置KEEP_CAPS标志
  2. 在程序内部调用setgid和setuid系统调用,完成子进程的用户切换,从root用户切换到普通用户
  3. 程序休眠100s,在这个时间内,可以用ps命令查找该程序,查看该程序的权限capability

使用方法:

#bash命令
[root@LIN-29076BB8489 ~]# go build main.go

使用go build命令生成可执行文件,文件名为main,没有后缀

然后使用root用户执行main,这个main可执行文件则是从root切换到10001用户上。

[root@LIN-29076BB8489 capability]# ./main
Hello world!
before set, the uid is  0
before set, the gid is  0
|***********************************|
*     secessfully set keep caps     *
|***********************************|
after set, the uid is  10001
after set, the gid is  1000

可以看到程序运行正常。ctrl+C退出程序,重新以后台运行的方式运行程序

[root@LIN-29076BB8489 capability]# ./main &
[1] 54152
[root@LIN-29076BB8489 capability]# Hello world!
before set, the uid is  0
before set, the gid is  0
|***********************************|
*     secessfully set keep caps     *
|***********************************|
after set, the uid is  10001
after set, the gid is  1000
after Clear, the uid is  10001
after Clear, the gid is  1000

运行以后再敲以下回车!

该程序的pid为54152,去/proc/54152/status 文件中查找权限(Cap)。

[root@LIN-29076BB8489 capability]# cat /proc/54152/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

可以看到,该程序从root用户切换到普通用户以后,权限(capability)只有P和B,E被内核清理了。与内核代码一致。

这里的权限(capability)是进程的权限,保留的是pP和pB。

下面要讲的是文件的权限。

文件权限

文件只用E、I、P权限,没有A、B权限!!!
文件只用E、I、P权限,没有A、B权限!!!
文件只用E、I、P权限,没有A、B权限!!!

查看某个文件的权限

下文中使用fI、fP、fE来分别表示文件的I、P、E权限

每个文件同样有权限,这些权限决定了某个用户执行该文件时可以进行哪些敏感操作。一般是看可执行文件的权限。

例如,我们的终端就是一个可执行文件,位置是/bin/bash。可以去查看该文件的权限。

[root@LIN-29076BB8489 ~]# getcap /bin/bash
[root@LIN-29076BB8489 ~]# 

可以看到该文件的权限为空。

查看我们刚刚的main可执行文件的权限:

[root@LIN-29076BB8489 capability]# getcap main
[root@LIN-29076BB8489 capability]# 

可以看到main可执行文件的文件权限fI、fE、fP也为空

getcap看到的文件权限是普通用户的权限!!!
getcap看到的文件权限是普通用户的权限!!!
getcap看到的文件权限是普通用户的权限!!!
重要的事情说3遍!

对于root用户而言,系统默认为root用户设置的权限为所有权限。即fE、fI、fP均为1。这里的1是指后文进行计算时候使用的1,实际拥有哪些权限还是取决于用户root权限B(边界权限)。(cat /proc/$$/status | grep CapBnd)

root用户下的官方解释

   1. If the real or effective user ID of the process is 0 (root),
      then the file inheritable and permitted sets are ignored;
      instead they are notionally considered to be all ones (i.e.,
      all capabilities enabled).  (There is one exception to this
      behavior, described below in Set-user-ID-root programs that
      have file capabilities.)

   2. If the effective user ID of the process is 0 (root) or the
      file effective bit is in fact enabled, then the file effective
      bit is notionally defined to be one (enabled).
为某个文件赋权

以上文的可执行文件main为例。main的普通用户文件权限为空,我们来为main赋予一点权限。

使用命令setcap来进行赋权。

[root@LIN-29076BB8489 capability]# setcap CAP_SYS_ADMIN+eip main
[root@LIN-29076BB8489 capability]# getcap main
main = cap_sys_admin+eip

命令中的+eip(也可以用=eip)表示,在fE集合中添加cap_sys_admin权限,在fI集合中添加cap_sys_admin权限,在fP集合中添加cap_sys_admin权限

可以看到赋权成功,main可执行文件的E、I、P权限集中都有了cap_sys_admin这个权限。

进程创建子进程的时候的权限

当我们在一个进程中创建一个子进程的时候,权限就会发生变化。

例如,我们在

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/277362.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号