Skip to content

Files

Latest commit

 

History

History
199 lines (123 loc) · 11.3 KB

77.01、SELinux 基础概念.adoc

File metadata and controls

199 lines (123 loc) · 11.3 KB

SELinux 基础概念

更安全的 Linux

LSM - Linux Security Modules

LSM —— Linux 安全模块,是 Linux 内核的一个子系统(subsystem),SELinux 正是依托于这个子系统而运行的。

内核在处理一个用户空间请求之前,就会调用 LSM 子系统。这些“请求”即为系统调用

在 LSM 的实现上,LSM 本身并没有任何安全功能,它依赖于系统中实际实现了安全功能的内核模块——比如 SELinux、AppArmor 等等。

检查当前正在运行的内核激活的 LSM 模块

cat /sys/kernel/security/lsm

SELinux 是传统 DAC 的扩展

注意,DAC 检查是先于 SELinux 检查的,也就是说,如果一个文件在文件权限检查(user/group/others 或 ACL)上不通过,则不会执行 SELinux 检查。

这种先通过 DAC 检测,再通过 SELinux 检测的流程,实际上来自于 LSM 的实现。

SELinux 的组成部分

SELinux 并非仅仅由 SELinux LSM 模块组成,它包含了多个部分:

  • SELinux 内核子系统,其通过 LSM 在 Linux 内核中实现

  • 库,需要与 SELinux 交互的程序使用它

  • 工具,与 SELinux 交互的管理员使用它

  • 策略(Policy),它确定了访问控制本身

其中,库和工具由 SELinux 用户空间项目(https://github.com/SELinuxProject/selinux)提供。而且,由于 SELinux 的出现,部分初始化系统和部分核心工具也许要更新。

SELinux 上下文(context)

SELinux 是通过上下文(context)来确定是否允许一个特定的操作的。这里的上下文同时包含主体(subject)(也就是操作的发起方)的上下文,和对象(object)(也就是操作的目标)的上下文。这些上下文(或部分上下文)会出现在策略规则中,SELinux 将其作为判断标准。

SELinux 使用进程的上下文来唯一确认(identify)一个进程。SELinux 不关心 Linux 进程的所有权,也不关心进程如何被调用,或者进程的 PID 是什么,或者进程以什么用户运行。SELinux 仅关注进程的上下文,该上下文作为一个标签(label)呈现给管理员和用户。

通常情况下“标签”和“上下文”是可以相互表述的。(从技术上来说,前者是后者的表现方式。)

显示当前用户的上下文
id -Z

返回值可能为 unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

准确来说,上面的命令显示的是 id 命令在运行的过程中,它的进程的上下文。

SELinux 使用上下文,而非实际的进程和文件作为访问控制的判断依据,是基于如下考虑:

Important

虽然说,SELinux 在做某次判断的时候不会考虑文件路径的因素,但并不代表整个 SELinux 不考虑文件路径。因为通过路径访问文件系统上的项目(比如文件、文件夹)的时候,Linux 系统会调用各种安全模块逐级检验路径上每一层文件夹/文件的权限,因而 SELinux 也会逐级检验权限。若给出的路径上有任何一个部分不符合 SELinux 的访问要求,SELinux 都会拒绝这次访问。(因此,current working directory 是非常重要的元素)

  • 文件可以被移动或重挂载,进程也可能使用不同的命名空间访问文件,此时文件的路径会变化。而基于标签的上下文,则不受影响。

  • 上下文可以很好地揭示进程的目的。基于二进制文件启动的方式,相同的二进制文件可以具有不同的上下文。通过上下文的值(比如 id -Z 所显示的)就可以确定每个运行的实例需要的权限;而且从上下文也可以反推出进程启动的情况,以及其目的。

  • 上下文也是对象(object)本身的抽象。上下文除了可以应用在进程和文件上,它也可以作用于抽象资源,比如管道(进程间通信),或数据库对象。

比较下面两种表示:

  • 允许 httpd 进程绑定 TCP 端口 80

  • 允许具有 httpd_t 标签的进程绑定具有 http_port_t 标签的 TCP 端口

第一种表示更简洁,但限制更多。而第二种方法下,规则本身则并不关心提供 HTTP 的实际进程是什么,也不关心 HTTP 实际能绑定的端口有哪些,它只关心 httpd_t 上下文可以绑定 http_port_t 上下文的端口,这样就可以减少规则指定的数量。

让我们看一个进程的上下文:

检查 systemd 进程的上下文
ps -eZ | grep systemd

返回值

system_u:system_r:init_t:s0           1 ?        00:00:03 systemd
...

这个上下文由 4 部分组成

  • SELinux 用户(user) system_u

  • SELinux 角色(role) system_r

  • SELinux 类型(type)(对于正在运行的进程,类型又被称为域 domain) init_t

  • 以及敏感级别(sensitivity level) s0

上下文的四个部分中,第三部分 type,或称 domain最为重要,因为绝大多数 SELinux 策略规则都是关于两个类型之间的交互的(它们并未提及角色、用户或敏感等级)。

SELinux 上下文与 LSM 安全属性对齐,并以标准方式暴露给用户空间。用户可以在 /proc/<PID>/attr/ 下找到数个文件,这些文件要么是空的,要么包含了 SELinux 上下文。若某个文件为空,则表示应用没有为那个特定的用途设置一个上下文,而这个上下文会从规则中推断,或者从父级继承。

  • current:显示进程的当前 SELinux 上下文

  • exec:通过该进程执行的新进程应该具有的 SELinux 上下文。通常为空。

  • fscreate:应用将写入的下一个文件的 SELinux 上下文。通常为空。

  • keycreate:引用在内核中缓存的密钥(kernel keyring 子系统)所赋予的 SELinux 上下文。通常为空。

  • prev:该进程的上一个 SELinux 上下文。通常为父进程的上下文。

  • socketcreate:应用创建的下一个套接字的 SELinux 上下文。通常为空。

若一个进程有多个线程,则每个线程的 SELinux 上下文可以在 /proc/<PID>/task/<taskid>/attr 中查看。

类型确定访问

一个进程的 SELinux 类型是细分该进程的访问控制的基石。

SELinux 是基于标签的(labed-based)访问控制机制,又可以表述为 SELinux 是类型强制(type enforcement)的强制访问控制系统(mandatory access control system, MAC)。

有了类型强制,SELinux 可以基于程序的启动缘由来控制程序的行为:举例来说,systemd 运行的网页服务和用户直接运行的网页服务很有可能并不相同,因此依照两者进程的 SELinux 类型的不同,赋予不同的权利是必然的。

SElinux 类型通常以 _t 为后缀,但并非强制的。

角色确定域访问

SELinux 角色(SELinux 上下文的第二部分)允许 SELinux 支持基于角色的访问控制。虽然 SELinux 中最常见的就是类型强制,但是基于角色的访问控制也是保证系统安全的重要一环,特别是让系统远离刻意的用户尝试。SELinux 角色定义了可以从当前的上下文到哪些类型(域)。SELinux 角色帮助定义了一个用户(用户可以访问一个或多个角色)能做的和不能做的。

通常来说,SELinux 角色使用 _r 后缀。

seinfo --role 可以查看系统上存在的角色。

通过用户限制角色

SELinux 用户(SELinux 上下文的第一部分)与 Linux 用户并不相同。虽然 Linux 用户可以在使用时修改(比如 sudo`su)。即便 Linux 用户发生更改,SELinux 策略也可以(且大多数时候会)强制 SELinux 用户保持固定。这种稳定性让我们可以阻止用户通过获得特权账户而绕过权限限制。

SElinux 用户的一个重要特性是,SELinux 用户确定了 Linux 用户可以获取的 SELinux 角色。也就是说一旦一个 Linux 用户赋予了一个 SELinux 用户,那么它就只能在与该 SELinux 用户关联的 SELinux 角色间切换。

SELinux 通常以 _u 结尾,但并不强制。

通过敏感级别控制信息流动

SELinux 上下文的第四分部为敏感(sensitivity)级别,它并非总是存在。SELinux 的可选的多级安全(multilevel security, MLS)需要这部分标签。该部分标签由两部分组成:一个保密值(confidentiality value)(前缀 s),以及一个类别值(catagory value)(前缀 c)。

当 SELinux 启用 MLS 后,就可以遵循 Bell-LaPadula 模型,也即“不向上读,不向下写”。也就是说具有低保密值的进程不可以读取高保密值的文件的内容,而具有高保密值的进程不可以向低保密值的文件中写入内容。在 SELinux 中,最低保密值为 s0,s 后的数字越高,保密值越高。

类别值则直接规定了主体/对象的类别,仅当主体与对象的类别值相匹配时,才可以通过访问检查。特别的,若一个系统不使用多个保密值,而使用多个类别值,则这种安全形式又称为多类别安全(multi-category security, MCS)

定义与分发策略

SELinux 必须依赖于策略才能运行。SELinux 策略通常以编译后的形式——称为策略模块(policy modules)——分发。之后,这些模块会整合为单一策略库,并加载至内存中。

书写 SELinux 策略

目前有三种书写 SELinux 策略的方法

  • 标准 SELinux 源码格式,人类可读、著名的格式

  • 参考策略风格(reference policy),标准 SELinux 源码格式上扩展了 M4 宏,简化了开发

  • SELinux 通用中间语言(common intermediate language, CIL),机器可读的(部分人类可读的)格式

大多数 SELinux 分发版都是基于“参考策略”(https://github.com/SELinxProject/refpolicy)的

直接书写 SELinux CIL 格式的策略并不太常见,但其本身却大量使用(SELinux 用户空间工具会将所有策略转换为 CIL 格式)。

Example 1. 案例:三种格式的对比

允许具有 http_t 类型的进程绑定具有 http_port_t 类型的 TCP 端口

标准 SELinux 源码格式

allow httpd_t http_port_t : tcp_socket { name_bind }

参考策略风格

corenet_tcp_bind_http_port(httpd_t)

CIL

(allow httpd_t http_port_t (tcp_socket (name_bind)))

一般来说,一个策略的源码文件包含三种文件:

  • .te 文件,Type Enforcement,是规则编写的主文件(类似 C 的 .c 源码文件)

  • .if 文件,InterFace,SELinux 规则接口文件,可以为其它规则文件提供接口的和模板(类似于 C 的头文件)

  • .fc 文件,File Context,为文件赋予特定标签的定义

策略文件的编译流程

  1. .te 文件和 .if 文件通过 checkmodule 生成 .mod 文件

  2. .mod 文件和 .fc 文件通过 semodule_package 生成 .pp 文件

  3. .pp 文件通过 semodule 转换为 .hll 文件

  4. .hll 文件通过 semodule 翻译为 .cil 文件

  5. .cil 文件通过 semodule 构建为 policy.## 文件

显示系统当前使用的策略

一个系统在任何一个时间点上,能且只能激活一个单一策略,这个单一策略被存储在一个 policy store 中。

查询系统上激活的 SELinux policy store
sestatus | grep "Loaded policy name"

修改 /etc/selinux/config 中的 SELINUXTYPE,可以修改下次启动时加载的 policy store。