selinux基础
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 用户空间项目(github.com/SELinuxPr... )提供。而且,由于 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 分发版都是基于“参考策略”(github.com/SELinxPro... )的
直接书写 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,为文件赋予特定标签的定义
策略文件的编译流程
.te
文件和.if
文件通过checkmodule
生成.mod
文件.mod
文件和.fc
文件通过semodule_package
生成.pp
文件.pp
文件通过semodule
转换为.hll
文件.hll
文件通过semodule
翻译为.cil
文件.cil
文件通过semodule
构建为policy.##
文件
显示系统当前使用的策略
一个系统在任何一个时间点上,能且只能激活一个单一策略,这个单一策略被存储在一个 policy store 中。
查询系统上激活的 SELinux policy store
sestatus | grep "Loaded policy name"
修改 /etc/selinux/config 中的 SELINUXTYPE
,可以修改下次启动时加载的 policy store。
最后编辑:SteveChen 更新时间:2025-04-06 14:02