linux入门---操作体统的概念

Posted 叶超凡

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux入门---操作体统的概念相关的知识,希望对你有一定的参考价值。

什么是操作系统

操作系统是一个对软硬件资源进行管理的软件。计算机由一堆硬件组成,这些硬件遵循着冯诺依曼体系结构

在这个硬件的基础上还有一个软件叫做操作系统

操作系统的任务是对硬件进行管理,既然是管理的话操作系统得访问到底层的硬件,比如说这是一个什么类型的硬件,这个硬件如果是磁盘的话,这个磁盘的大小是多少?目前已经用来多大的空间还剩多少空间,这个磁盘是否处于运行的状态等等,所以操作系统为了管理好这里的硬件就将自己分为四大模块分别进程管理,文件系统,内存管理,驱动管理,其中文件系统和内存管理是整个操作系统中最重要的两个部分因为在整个机器的运行过程中要到他们的次数最多,所以以后的学习中我们将重点放到这两个方面上

在操作系统之上还有一层就是用户:

我们作为用户通过操作系统来使用电脑,比如说用windows操作系统使用电脑玩英雄联盟,csgo,使命召唤等等,那么这里就有一个问题操作系统为什么要对软硬件资源进行进行管理?原因很简单操作系统的服务对象是人,我们每个人使用电脑的时候都希望自己使用的系统能够给我们提供更好的服务,比如说打游戏不卡,使用电脑不死机等等,而操作系统之所以要管理软硬件就是想通过合理的管理软硬件资源,来位用户提供一个稳定的,高效的,安全的执行环境,其中对软硬进行管理是操作系统的手段,给用户一个良好的环境是操作系统的目的。好看到这里大家已经知道了什么是操作系统(操作系统是一个对软硬件资源进行管理的软件),以及为什么会有操作系统(操作系统的存在可以给用户提供良好的执行环境),那么接下来我们就要了解操作系统是如何管理的软硬件来达到这样的目的。

什么是管理

在了解操作系统是如何管理软硬件之前我们先来谈谈管理这个词,看这篇文章的大部分应该都是大学生,那么这里我就问大家一个问题:你有见过校长吗?你有见过校长亲自来到你的宿舍查寝询问你的生活日常以及平时成绩吗?很明显这种情况是几乎不可能出现的,校长在学校里面属于是管理者,学生在学校里面属于是被管理者,校长虽然连学生的一次面都没有见过但是校长却能够知道学生的生活日常,学生的课表是什么?这个专业得期末成绩这个专业的某个班的专业成绩以及学校中具体的某个人的成绩校长都可以知道,那管理者是如何做到的呢?我们大学毕业进入公司工作,但是从进入工作的第一天开始到离职我们连ceo的面都没有见过,他没有问过我这个月的效绩怎么样这个月的工作是否顺利,没有问过我家庭是否幸福对工作是否充满热情,那这个ceo又是如何来管理我对我进行升值或者炒鱿鱼呢?

看了上面的两个例子大家对于管理者应该能够明白一件事:管理者不需要和被管理者直接交互,但是依旧能够把被管理着管理起来。那管理着是如何做到的呢?在了解这个问题之前我们先来想一个问题什么样的人是管理者?有小伙伴可能就会这么想,我的妈妈天天在家里管着我所以我的妈妈是管理者,我们班的班主任天天管着我的学习和生活所以班主任是管理者,如果大家是这么理解的话就大错特错了,你的妈妈和班主任虽然天天管着你但是他们不是管理者他们只是在管着你,我们判断一个人是不是管理着主要取决于这个人能不能做出重大决定,比如说一个学校的校长可以决定是否举办一个全校的师生大会来公布去年取得的成绩 ,一个学校的ceo能够决定今年的年终奖什么时候发发多少等等,我们把这样的人称之为管理者,而一个管理者每次做出一个重大的决定往往都是要有依据的,一个公司的ceo能够随便分发年终奖吗?比如说给一个普通的员工几个亿让他实现财富自由,对吧很明显是不可能的,比如说学校组织年纪大会时间选定在晚上的凌晨3点可能吗?很明显是不可能的,所以管理者每次做出一个重大的决定都是得有依据的,我们作为一个学生我的学号是多少?哪里人?高中在哪读?手机号是多少?以往期末考试成绩怎么样等等这些都是我们作为一个学生的数据,当我们作为一个员工时我每天的基础工作时间,加班时间,工资多少,为公司解决了多少问题,年终奖为多少,为公司所作的贡献等等这些都是我们作为员工的数据,公司的ceo或者学校的校长虽然不和我们直接打交道但是我们的所有数据早以被管理者所熟知,并且这些数据还在不停的更新,所以管理者做重大决定的依据就是被管理者的不停更新的数据,所以管理者对被管理者的管理实际上就是管理者对数据的管理,通过这些数据来做出重大的决定这样就算校长没有见过我,就算ceo不会亲自给我发工资但是他们依然能够很好的管理我们,管理的本质就是对数据的管理,当校长看到某个班级的语文成绩降低了校长就会提醒语文老师加强这个班的语文教育,当ceo看到某个部门的效绩很低那么ceo就会通知这个部门的领导加强对这个部门的监督那么这就是管理,管理的本质就是对数据的管理。我们说管理者是根据被管理者的数据来做出相应的重大决策,可是这里就存在一个问题,管理者从来不和被管理者接触那他是如何来获取不断更新的数据的呢?并且管理者下达的决策还得传达给底层的人,那这个决策又是如何传达的呢?所以在管理者和被管理者之间就会出现一个新的角色叫做执行者,执行者帮管理者收集被管理者的数据,然后管理者下达的决策就会由执行者传递给被管理者,这里的执行者就相当于大学中的辅导员各种领导,一旦校长决定开会或者举办运动会之后,这些辅导员和领导就会发消息给同学通知他们最好对应的准备,一旦这些学生考完试这些辅导员和领导就会把这些同学的成绩进行汇总上报给校长,那么在计算机中操作系统就相当于这里的管理者他负责分析数据根据这些数据下达决策,计算机中的各种硬件就相当于学校中的学生,他们负责执行对应的指令并反馈自己此时的状态,而计算机中的各种驱动就相当于学校中的辅导员他们负责传达校长执行的决策,并将学生的各种状态不停的收集起来交给校长

我们上面说管理的本质是对数据进行管理,校长管理一个学校就是管理这个学校的各种数据,而数据是由辅导员和各种领导采集到一起的,可是这里还是会存在一个问题,如果学校的人非常少的话产生的基础数据也会非常的少,比如说一个学校只有100号人,那么校长要想管理这100个人的话通过几张A4纸都能管理的过来,但是一个大学往往都会有几千好人,这么多人产生的数据是非常多的,这时如果还是一个人一个人的查看数据的话就会让管理的效率变得非常的慢,所以为了提高效率人们会采用这样的方法:校长要管理的人都是学生,学生虽然是不同的但是他们的数据类型却都是一样的,比如说这个学生的学号,年龄,学号,手机号等等,每个学生都有这些性质,所以校长就可以创建一个结构体来描述这些学生:

这样校长就把对学生的管理转换成对学生的数据进行管理,将对学生的数据进行管理转换成对数据组成的结构体变量进行管理,而在计算机中同一个硬件返回的数据的类型都是一样的,所以我们就可以使用结构体对这些数据进行描述,然后再通过数据结构中的方法来对这些结构体变量实现增删查改,那么这就是操作系统管理的数据方法:先描述,再组织。先使用结构体对数据进行描述,再通过数据结构来管理一个又一个的结构体。其次大家要注意的一点就是操作系统能够对底层的硬件进行管理,那么同样的道理操作系统在内部也能对各种软件进行管理,管理的方式也是先描述再组织。在现实生活中银行为什么要搞个窗户将用户和员工隔开,因为银行无法判断你是否是一个好人,因为银行自己很容易受到伤害,所以银行不相信任何一个顾客,所以他在顾客和员工之间添加了一个窗户,那么操作系统也是同样的道理,操作系统也不相信任何人,但是他又必须给上层的用户提供各种各样的服务,所以就有了操作系统接口,这些接口是操作系统提供的而操作系统是c语言写的,所以这些接口本质上就是c式接口,如果你想要访问计算机中的一些内容就必须得通过操作系统提供的接口来进行访问,其他的非法都会被操作系统直接拒绝

操作系统提供的接口在调用的时候十分的麻烦,所以就有了c/c++库和shell,我们写的代码用的printf,cout就是像硬件写入对应的信息,但是这个信息并不是printf函数或者cout函数向硬件中写入的数据,而是在这些库函数的内部调用了操作系统的接口,在通过操作系统的接口往硬件中写入了数据,我们的库写的非常的好,所以每次只用调用几个函数就能达到我们的目的,所以在操作系统调用接口的上一层还存在一层东西来帮助我们使用计算机,这个东西就是c/c++库,shell,界面等等,其中c/c++库是为了方便我们变成,而shell和界面是方便我们执行指令操作:

这就好比我们生活中的银行,银行拥有很多的基本设备比如说仓库,座椅,电脑,纸张等等,这些基本设备就相当于电脑的外设:键盘鼠标网卡硬盘等等。

银行的基本设备一般都会有人来管理,比如说仓库由保安来管理,桌椅由后勤来管理,电脑由程序员管理,纸张由其他人进行管理,管理物品的这些人就相当于电脑里面的各种驱动:

这些人都是基本员工他们属于被管理的人,而管理他们的人就是行长,行长就相当于操作系统,操作系统不仅可以通过驱动管理硬件,他还可以直接管理软件,就相当于行长不仅可以管理保安后勤程序员,还可以管理业务人员部门经理一样

银行不是直接开着保险库与客户进行交流而是通过一个个的小型窗口对客户进行服务,那么这里的窗口就相当于操作系统提供的系统调用接口,通过这个接口来获取对应的服务

银行服务的人各种各样,有社会上的精英也有刚步入社会的学生也有连字都不认识的老年人,所以为了服务这些老年人银行往往就会在大厅里面安排一些大厅经理来专门为不懂如何操作得老年人服务,那么这里得大厅经理就相当于科学家给我们提供得shell,c/c++库,图形化界面:

那么以上就是本篇文章得全部内容希望大家能够理解。

Linux Capabilities 入门教程:概念篇

原文链接:Linux Capabilities 入门教程:概念篇

Linux 是一种安全的操作系统,它把所有的系统权限都赋予了一个单一的 root 用户,只给普通用户保留有限的权限。root 用户拥有超级管理员权限,可以安装软件、允许某些服务、管理用户等。

作为普通用户,如果想执行某些只有管理员才有权限的操作,以前只有两种办法:一是通过 sudo 提升权限,如果用户很多,配置管理和权限控制会很麻烦;二是通过 SUID(Set User ID on execution)来实现,它可以让普通用户允许一个 owner 为 root 的可执行文件时具有 root 的权限。

SUID 的概念比较晦涩难懂,举个例子就明白了,以常用的 passwd 命令为例,修改用户密码是需要 root 权限的,但普通用户却可以通过这个命令来修改密码,这就是因为 /bin/passwd 被设置了 SUID 标识,所以普通用户执行 passwd 命令时,进程的 owner 就是 passwd 的所有者,也就是 root 用户。

SUID 虽然可以解决问题,但却带来了安全隐患。当运行设置了 SUID 的命令时,通常只是需要很小一部分的特权,但是 SUID 给了它 root 具有的全部权限。这些可执行文件是黑客的主要目标,如果他们发现了其中的漏洞,就很容易利用它来进行安全攻击。简而言之,SUID 机制增大了系统的安全攻击面。

为了对 root 权限进行更细粒度的控制,实现按需授权,Linux 引入了另一种机制叫 capabilities

1. Linux capabilities 是什么?

Capabilities 机制是在 Linux 内核 2.2 之后引入的,原理很简单,就是将之前与超级用户 root(UID=0)关联的特权细分为不同的功能组,Capabilites 作为线程(Linux 并不真正区分进程和线程)的属性存在,每个功能组都可以独立启用和禁用。其本质上就是将内核调用分门别类,具有相似功能的内核调用被分到同一组中。

这样一来,权限检查的过程就变成了:在执行特权操作时,如果线程的有效身份不是 root,就去检查其是否具有该特权操作所对应的 capabilities,并以此为依据,决定是否可以执行特权操作。

Capabilities 可以在进程执行时赋予,也可以直接从父进程继承。所以理论上如果给 nginx 可执行文件赋予了 CAP_NET_BIND_SERVICE capabilities,那么它就能以普通用户运行并监听在 80 端口上。

capability 名称 描述
CAP_AUDIT_CONTROL 启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则
CAP_AUDIT_READ 允许通过 multicast netlink 套接字读取审计日志
CAP_AUDIT_WRITE 将记录写入内核审计日志
CAP_BLOCK_SUSPEND 使用可以阻止系统挂起的特性
CAP_CHOWN 修改文件所有者的权限
CAP_DAC_OVERRIDE 忽略文件的 DAC 访问限制
CAP_DAC_READ_SEARCH 忽略文件读及目录搜索的 DAC 访问限制
CAP_FOWNER 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制
CAP_FSETID 允许设置文件的 setuid 位
CAP_IPC_LOCK 允许锁定共享内存片段
CAP_IPC_OWNER 忽略 IPC 所有权检查
CAP_KILL 允许对不属于自己的进程发送信号
CAP_LEASE 允许修改文件锁的 FL_LEASE 标志
CAP_LINUX_IMMUTABLE 允许修改文件的 IMMUTABLE 和 APPEND 属性标志
CAP_MAC_ADMIN 允许 MAC 配置或状态更改
CAP_MAC_OVERRIDE 忽略文件的 DAC 访问限制
CAP_MKNOD 允许使用 mknod() 系统调用
CAP_NET_ADMIN 允许执行网络管理任务
CAP_NET_BIND_SERVICE 允许绑定到小于 1024 的端口
CAP_NET_BROADCAST 允许网络广播和多播访问
CAP_NET_RAW 允许使用原始套接字
CAP_SETGID 允许改变进程的 GID
CAP_SETFCAP 允许为文件设置任意的 capabilities
CAP_SETPCAP 参考 capabilities man page
CAP_SETUID 允许改变进程的 UID
CAP_SYS_ADMIN 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等
CAP_SYS_BOOT 允许重新启动系统
CAP_SYS_CHROOT 允许使用 chroot() 系统调用
CAP_SYS_MODULE 允许插入和删除内核模块
CAP_SYS_NICE 允许提升优先级及设置其他进程的优先级
CAP_SYS_PACCT 允许执行进程的 BSD 式审计
CAP_SYS_PTRACE 允许跟踪任何进程
CAP_SYS_RAWIO 允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备
CAP_SYS_RESOURCE 忽略资源限制
CAP_SYS_TIME 允许改变系统时钟
CAP_SYS_TTY_CONFIG 允许配置 TTY 设备
CAP_SYSLOG 允许使用 syslog() 系统调用
CAP_WAKE_ALARM 允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器)

2. capabilities 的赋予和继承

Linux capabilities 分为进程 capabilities 和文件 capabilities。对于进程来说,capabilities 是细分到线程的,即每个线程可以有自己的capabilities。对于文件来说,capabilities 保存在文件的扩展属性中。

下面分别介绍线程(进程)的 capabilities 和文件的 capabilities。

线程的 capabilities

每一个线程,具有 5 个 capabilities 集合,每一个集合使用 64 位掩码来表示,显示为 16 进制格式。这 5 个 capabilities 集合分别是:

  • Permitted
  • Effective
  • Inheritable
  • Bounding
  • Ambient

每个集合中都包含零个或多个 capabilities。这5个集合的具体含义如下:

Permitted

定义了线程能够使用的 capabilities 的上限。它并不使能线程的 capabilities,而是作为一个规定。也就是说,线程可以通过系统调用 capset() 来从 EffectiveInheritable 集合中添加或删除 capability,前提是添加或删除的 capability 必须包含在 Permitted 集合中(其中 Bounding 集合也会有影响,具体参考下文)。 如果某个线程想向 Inheritable 集合中添加或删除 capability,首先它的 Effective 集合中得包含 CAP_SETPCAP 这个 capabiliy。

Effective

内核检查线程是否可以进行特权操作时,检查的对象便是 Effective 集合。如之前所说,Permitted 集合定义了上限,线程可以删除 Effective 集合中的某 capability,随后在需要时,再从 Permitted 集合中恢复该 capability,以此达到临时禁用 capability 的功能。

Inheritable

当执行exec() 系统调用时,能够被新的可执行文件继承的 capabilities,被包含在 Inheritable 集合中。这里需要说明一下,包含在该集合中的 capabilities 并不会自动继承给新的可执行文件,即不会添加到新线程的 Effective 集合中,它只会影响新线程的 Permitted 集合。

Bounding

Bounding 集合是 Inheritable 集合的超集,如果某个 capability 不在 Bounding 集合中,即使它在 Permitted 集合中,该线程也不能将该 capability 添加到它的 Inheritable 集合中。

Bounding 集合的 capabilities 在执行 fork() 系统调用时会传递给子进程的 Bounding 集合,并且在执行 execve 系统调用后保持不变。

  • 当线程运行时,不能向 Bounding 集合中添加 capabilities。
  • 一旦某个 capability 被从 Bounding 集合中删除,便不能再添加回来。
  • 将某个 capability 从 Bounding 集合中删除后,如果之前 Inherited 集合包含该 capability,将继续保留。但如果后续从 Inheritable 集合中删除了该 capability,便不能再添加回来。

Ambient

Linux 4.3 内核新增了一个 capabilities 集合叫 Ambient ,用来弥补 Inheritable 的不足。Ambient 具有如下特性:

  • PermittedInheritable 未设置的 capabilities,Ambient 也不能设置。
  • PermittedInheritable 关闭某权限(比如 CAP_SYS_BOOT)后,Ambient 也随之关闭对应权限。这样就确保了降低权限后子进程也会降低权限。
  • 非特权用户如果在 Permitted 集合中有一个 capability,那么可以添加到 Ambient 集合中,这样它的子进程便可以在 AmbientPermittedEffective 集合中获取这个 capability。现在不知道为什么也没关系,后面会通过具体的公式来告诉你。

Ambient 的好处显而易见,举个例子,如果你将 CAP_NET_ADMIN 添加到当前进程的 Ambient 集合中,它便可以通过 fork()execve() 调用 shell 脚本来执行网络管理任务,因为 CAP_NET_ADMIN 会自动继承下去。

文件的 capabilities

文件的 capabilities 被保存在文件的扩展属性中。如果想修改这些属性,需要具有 CAP_SETFCAP 的 capability。文件与线程的 capabilities 共同决定了通过 execve() 运行该文件后的线程的 capabilities。

文件的 capabilities 功能,需要文件系统的支持。如果文件系统使用了 nouuid 选项进行挂载,那么文件的 capabilities 将会被忽略。

类似于线程的 capabilities,文件的 capabilities 包含了 3 个集合:

  • Permitted
  • Inheritable
  • Effective

这3个集合的具体含义如下:

Permitted

这个集合中包含的 capabilities,在文件被执行时,会与线程的 Bounding 集合计算交集,然后添加到线程的 Permitted 集合中。

Inheritable

这个集合与线程的 Inheritable 集合的交集,会被添加到执行完 execve() 后的线程的 Permitted 集合中。

Effective

这不是一个集合,仅仅是一个标志位。如果设置开启,那么在执行完 execve() 后,线程 Permitted 集合中的 capabilities 会自动添加到它的 Effective 集合中。对于一些旧的可执行文件,由于其不会调用 capabilities 相关函数设置自身的 Effective 集合,所以可以将可执行文件的 Effective bit 开启,从而可以将 Permitted 集合中的 capabilities 自动添加到 Effective 集合中。

详情请参考 Linux capabilities 的 man page

3. 运行 execve() 后 capabilities 的变化

上面介绍了线程和文件的 capabilities,你们可能会觉得有些抽象难懂。下面通过具体的计算公式,来说明执行 execve() 后 capabilities 是如何被确定的。

我们用 P 代表执行 execve() 前线程的 capabilities,P\' 代表执行 execve() 后线程的 capabilities,F 代表可执行文件的 capabilities。那么:

P\'(ambient) = (file is privileged) ? 0 : P(ambient)

P\'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & P(bounding))) | P\'(ambient)

P\'(effective)   = F(effective) ? P\'(permitted) : P\'(ambient)

P\'(inheritable) = P(inheritable) [i.e., unchanged]

P\'(bounding) = P(bounding) [i.e., unchanged]

我们一条一条来解释:

  • 如果用户是 root 用户,那么执行 execve() 后线程的 Ambient 集合是空集;如果是普通用户,那么执行 execve() 后线程的 Ambient 集合将会继承执行 execve() 前线程的 Ambient 集合。

  • 执行 execve() 前线程的 Inheritable 集合与可执行文件的 Inheritable 集合取交集,会被添加到执行 execve() 后线程的 Permitted 集合;可执行文件的 capability bounding 集合与可执行文件的 Permitted 集合取交集,也会被添加到执行 execve() 后线程的 Permitted 集合;同时执行 execve() 后线程的 Ambient 集合中的 capabilities 会被自动添加到该线程的 Permitted 集合中。

  • 如果可执行文件开启了 Effective 标志位,那么在执行完 execve() 后,线程 Permitted 集合中的 capabilities 会自动添加到它的 Effective 集合中。

  • 执行 execve() 前线程的 Inheritable 集合会继承给执行 execve() 后线程的 Inheritable 集合。

这里有几点需要着重强调:

  1. 上面的公式是针对系统调用 execve() 的,如果是 fork(),那么子线程的 capabilities 信息完全复制父进程的 capabilities 信息。

  2. 可执行文件的 Inheritable 集合与线程的 Inheritable 集合并没有什么关系,可执行文件 Inheritable 集合中的 capabilities 不会被添加到执行 execve() 后线程的 Inheritable 集合中。如果想让新线程的 Inheritable 集合包含某个 capability,只能通过 capset() 将该 capability 添加到当前线程的 Inheritable 集合中(因为 P\'(inheritable) = P(inheritable))。

  3. 如果想让当前线程 Inheritable 集合中的 capabilities 传递给新的可执行文件,该文件的 Inheritable 集合中也必须包含这些 capabilities(因为 P\'(permitted)   = (P(inheritable) & F(inheritable))|...)。

  4. 将当前线程的 capabilities 传递给新的可执行文件时,仅仅只是传递给新线程的 Permitted 集合。如果想让其生效,新线程必须通过 capset() 将 capabilities 添加到 Effective 集合中。或者开启新的可执行文件的 Effective 标志位(因为 P\'(effective)   = F(effective) ? P\'(permitted) : P\'(ambient))。

  5. 在没有 Ambient 集合之前,如果某个脚本不能调用 capset(),但想让脚本中的线程都能获得该脚本的 Permitted 集合中的 capabilities,只能将 Permitted 集合中的 capabilities 添加到 Inheritable 集合中(P\'(permitted)  = P(inheritable) & F(inheritable)|...),同时开启 Effective 标志位(P\'(effective)   = F(effective) ? P\'(permitted) : P\'(ambient))。有 有 Ambient 集合之后,事情就变得简单多了,后续的文章会详细解释。

  6. 如果某个 UID 非零(普通用户)的线程执行了 execve(),那么 PermittedEffective 集合中的 capabilities 都会被清空。

  7. 从 root 用户切换到普通用户,那么 PermittedEffective 集合中的 capabilities 都会被清空,除非设置了 SECBIT_KEEP_CAPS 或者更宽泛的 SECBIT_NO_SETUID_FIXUP。

关于上述计算公式的逻辑流程图如下所示(不包括 Ambient 集合):

4. 简单示例


下面我们用一个例子来演示上述公式的计算逻辑,以 ping 文件为例。如果我们将 CAP_NET_RAW capability添加到 ping 文件的 Permitted 集合中(F(Permitted)),它就会添加到执行后的线程的 Permitted 集合中(P\'(Permitted))。由于 ping 文件具有 capabilities 意识,即能够调用 capset()capget() ,它在运行时会调用 capset()CAP_NET_RAW capability 添加到线程的 Effective 集合中。

换句话说,如果可执行文件不具有 capabilities 意识,我们就必须要开启 Effective 标志位(F(Effective)),这样就会将该 capability 自动添加到线程的 Effective 集合中。具有capabilities 意识的可执行文件更安全,因为它会限制线程使用该 capability 的时间。

我们也可以将 capabilities 添加到文件的 Inheritable 集合中,文件的 Inheritable 集合会与当前线程的 Inheritable 集合取交集,然后添加到新线程的 Permitted 集合中。这样就可以控制可执行文件的运行环境。

看起来很有道理,但有一个问题:如果可执行文件的有效用户是普通用户,且没有 Inheritable 集合,即 F(inheritable) = 0,那么 P(inheritable) 将会被忽略(P(inheritable) & F(inheritable))。由于绝大多数可执行文件都是这种情况,因此 Inheritable 集合的可用性受到了限制。我们无法让脚本中的线程自动继承该脚本文件中的 capabilities,除非让脚本具有 capabilities 意识

要想改变这种状况,可以使用 Ambient 集合。Ambient 集合会自动从父线程中继承,同时会自动添加到当前线程的 Permitted 集合中。举个例子,在一个 Bash 环境中(例如某个正在执行的脚本),该环境所在的线程的 Ambient 集合中包含 CAP_NET_RAW capability,那么在该环境中执行 ping 文件可以正常工作,即使该文件是普通文件(没有任何 capabilities,也没有设置 SUID)。

5. 终极案例

最后拿 docker 举例,如果你使用普通用户来启动官方的 nginx 容器,会出现以下错误:

bind() to 0.0.0.0:80 failed (13: Permission denied)

因为 nginx 进程的 Effective 集合中不包含 CAP_NET_BIND_SERVICE capability,且不具有 capabilities 意识(普通用户),所以启动失败。要想启动成功,至少需要将该 capability 添加到 nginx 文件的 Inheritable 集合中,同时开启 Effective 标志位,并且在 Kubernetes Pod 的部署清单中的 securityContext --> capabilities 字段下面添加 NET_BIND_SERVICE(这个 capability 会被添加到 nginx 进程的 Bounding 集合中),最后还要将 capability 添加到 nginx 文件的 Permitted 集合中。如此一来就大功告成了,参考公式:P\'(permitted) = ...|(F(permitted) & P(bounding)))|...P\'(effective) = F(effective) ? P\'(permitted) : P\'(ambient)

如果容器开启了 securityContext/allowPrivilegeEscalation,上述设置仍然可以生效。如果 nginx 文件具有 capabilities 意识,那么只需要将 CAP_NET_BIND_SERVICE capability 添加到它的 Inheritable 集合中就可以正常工作了。

当然了,除了上述使用文件扩展属性的方法外,还可以使用 Ambient 集合来让非 root 容器进程正常工作,但 Kubernetes 目前还不支持这个属性,具体参考 Kubernetes 项目的 issue

虽然 Kubernetes 官方不支持,但我们可以自己来实现,具体实现方式可以关注我后续的文章。

6. 参考资料

微信公众号

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

以上是关于linux入门---操作体统的概念的主要内容,如果未能解决你的问题,请参考以下文章

Linux运维从入门到进阶

Linux运维入门到高级全套常用要点

Linux运维常用命令有哪些?Linux基础入门

Linux运维实战CentOS7.6操作系统从入门到精通

Linux运维容器篇 docker入门简介和部署

Linux运维容器篇 docker入门简介和部署