13.1 安全访问控制机制
在安全访问中,访问的发起者称为 主体,被访问的资源称为 客体。
😈 为了减少输入时中英文切换的次数,偷懒一下,下文中,用 SL
表示 SELinux,用 L 用户
表示 Linux 用户,用 SL 用户
表示 SELinux 用户。
13.1.1 DAC
DAC,Discretionary Access Control,自主访问控制,Linux 传统的访问控制属于这样的机制。
DAC 控制的主体为用户。
- 自主:访问控制由文件或进程的 所有者决定
- 只使用 UID、GID 来控制
传统文件权限中,帐号分为系统管理员与普通用户,对文件件资源的访问权限决定于 rwx 的设置。根据对象所属的分组来限制对对象的访问。
DAC 最大的缺点是它 无法分离用户和进程,进程能够继承用户的访问控制,授权用户的所有进程会自动获得授权,包括侵入该用户名下的恶意程序。
root 为默认的超级用户,属于它的进程一旦被攻破会极度危险。
13.1.2 MAC
MAC,Mandatory Access Control,强制访问控制,操作系统会限制进程对客体的访问或操作,SELinux 属于这样的控制机制。
主体和客体都有一组安全属性,操作系统会检查这些标签,以决定请求的操作是否允许进行。
MAC 控制的主体为进程,不再是用户。
MAC 的特性
- 系统级别 的访问控制,强制实施
- 用户无法超越策略的限制
- 颗粒级别 的控制
- 最小权限原则:仅授予足够完成具体任务的权限
- 客体为系统资源,如文件、目录、共享内存、套接字、网络主机等
- 任何主体 对 任何客体 实施的 任何操作 都会被规则检查,以确定是否有权进行该操作
- MAC 是近年的主流访问控制方式
- 通过 对比各自身上的标签,决定主体是否有权访问客体。
- 决定访问权限的 不再是主体和客体,而 是访问控制策略。
- SELinux 中 没有默认的超级用户。
对于 SELinux 来说,安全属性即安全上下文。内核中的安全服务器使用安全策略为访问授权。
SELinux 支持两种形式的 MAC:
- 类型强制,Type Enforcement,TE:运行在域中的进程及其对客体的行为受策略的控制。与基于角色的访问控制 RBAC 一起作为一般用途使用。
- 多级安全,Multi-Level Security,MLS:基于 Bell-La Padula,BLP 模型,用于特别需要用多级别来控制敏感信息的场景。把多级别加入上下文以后,可以实现策略中对 “向下不能写” 及 “向上不能读” 的高级别控制。
MLS / MCS
MLS:Multi-Level Security,多级别安全策略。
MCS:Multi-Category Security,多类型安全策略。
SELinux 同时支持 MLS 和 MCS,只需在上下文中增加一个字段即可。
上图显示的是一个文件服务器中,不同文件的安全级别。安全级别是严格地按层次划分的,并形成一个 BLP 模型,进程只能对同级别的文件进行读取;对更高级别的文件,它只能写入;对更低级别的,它只能读取。即 向下不能写,向上不能读。确保了进程可以向上复制文件,但之后不可从上面读取,除非上到那一级。进程也无法向低级别写入文件,以避免机密信息向下泄漏。
MLS 和 MCS 的特别之处:
- 可以为进程和客体分配一个
最低级别 - 最高级别
的范围 - 安全级别可以做的更复杂,即
分级的敏感度 + 不分级的类别
,其中类别可省略 - 允许进程访问客体由应用于安全级别的 “显性” 规则管理
- 信任的进程可被授予特权,允许它们绕过 BLP 规则做策略允许的任何操作
- 有些客体不支持单独的读/写功能,因为它们需要在网络等情况下读取/响应
目前,MLS/MCS 服务更多地用于维护应用程序的隔离,如:
- 虚拟机使用 MCS,可以允许每个虚拟机都在自己的域中运行,让它们彼此隔离
- 安卓设备使用动态生成的 MCS,这样,代表一个用户运行的应用无法读写另一个用户在同一个应用创建的文件。
MLS/MCS 安全范围的表示方法
敏感度是分级的,类别是不分级的。不连续的类别之间可以用逗号分隔,连续的类别可以用句点分隔。
单个级别的表示
每个级别可以用 敏感度:类别
格式来描述,如 s0:c0
。其中 “类别” 是可选的,如果 没有类别,可以单独用 敏感度
来表示某个级别,如 s0
。
级别范围
级别范围是用一对级别来描述的,如果是 多级,格式为 最低级-最高级
,如 s0:c1,c5-s5:c1,c5
或 s0:c1.c100-s5:c1.c100
;如果是 单级,格式为 最低级
,如 s0:c1,c5
。
user:role:type:sensitivity[:category,...] - sensitivity [:category,...]
---------------▼------------------------▼-----▼-------------------------▼
| level | - | level |
| |
|range|
13.2 SELinux 简介
Security Enhanced Linux
SELinux 是内建在 Linux 中的主要的 MAC 机制。
SELinux 是对 进程、文件、用户 的权限进行详细设置的 内核模块。
SELinux 是通过 MAC 方式来管控进程,控制的 主体是进程,客体是文件资源。
SELinux 允许使用不同类型的策略:
- 基于角色 的访问控制,Role-Based Access Control (RBAC)
- 基于类型匹配 的访问控制,类型强制,Type Enforcement (TE)
- 最细致的 多级别 访问控制,Multi-Level Security (MLS)
13.2.1 SELinux 的优点
所有的进程和文件都有类型标签
类型 为进程定义了一个 域,为文件定义了一个 类型。不同的进程分别运行于各自的域中,SELinux 策略规则定义了进程应如何与文件进行交互,进程间如何交互。只有当 SELinux 规则中有允许放行的条目时,访问才能放行。
颗粒化的访问控制
传统的 Linux 权限控制是基于 UID 和 GID,用户自己有裁量权,而 SELinux 访问的决策是基于所有的可用信息,如 SL 用户、角色、类型、级别等。
SELinux 策略是全局强制性的
策略在系统全局强制实施,不是由用户自行决定的。
减少特权升级攻击的漏洞
因为进程运行于域中,因此彼此隔离,因为 SELinux 策略规则定义了进程应如何访问文件及其它进程,如果某个进程被攻破,攻击者仅能访问该进程的普通功能,仅能访问该进程有权访问的文件。例如,如果 Apache HTTP Server 被攻破,攻击者无法用该进程来读取用户家目录的文件,除非加入专门的规则来放行。
SELinux 可用来强化数据机密性和完整性
SELinux 可用来强化数据机密性和完整性,也可以避免进程接受不可信的输入。
虽然有以上优点,SELinux 并非杀毒软件,无法替代密码、防火墙或其它的安全系统,它并非大而全的安全解决方案。
SELinux 被设计 用来强化现有的安全解决方案,而不是替代它们。
13.2.2 SELinux 架构
SELinux 是 Linux 内核中的一个 安全模块,由 可加载的策略规则 驱动。当发生与安全相关的访问时,如进程尝试打开文件,操作会被 SELinux 在内核中截获。如果 SELinux 的策略规则允许该操作,则继续,否则,会返回给系统一个 权限被拒绝(Permission Denied)的错误,然后给审计子系统(audit subsystem)发送一个拒绝消息。这样,管理员可以研究日志中的拒绝消息,以确认安全规则不会被攻破。
SELinux 的 决策,如放行还是拒绝访问,是 会被缓存 的。该缓存即访问向量缓存 AVC,有了这些缓存的决策,就无需频繁地检查 SELinux 策略规则,提升了性能。
SELinux 的规则是在 DAC 规则之后检查的。因此,如果 DAC 规则首先拒绝了访问,则无需再使用 SELinux 策略规则来检查了。
13.2.3 SELinux 核心要素
🍬 SELinux 顶层核心要素:

- 主体:Subject,对客体实施操作,如读取文件
- 客体管理器:Object Manager,根据规则 强制实施 对客体的具体操作。他们可以存在于内核空间或用户空间,决定于软件是 Linux 内置的还是第三方的。
- 安全服务器: Security Server,它基于规则,对主体是否有权对客体操作 做出决策。嵌于内核中。
- 安全策略:Security Policy,使用策略语言来描述 具体的规则。
- 访问向量缓存:Access Vector Cache,AVC,缓存 所有的安全服务器做出的 决策,以此提升对请求的响应速度。
🍬 SELinux 顶层架构:
从下往上分析:
-
安全服务器 内嵌于内核中,策略会从用户空间借助一系列函数被加载。
客体管理器 和 访问向量缓存 可以存在于:
- 内核空间:存在于内核空间的客体管理器用于控制内核的服务,如文件、目录、套接字、进程间通信等,是由一些勾子提供的,这些勾子通过 Linux 安全模块框架挂接到 SELinux 子系统中。内核的 AVC 服务用于缓存安全服务器对基于内核的对象管理器的响应,从而加快未来访问决策的速度。
- 用户空间:存在于用户空间的客体管理器是与需要支持 MAC 的应用程序或服务一起提供的,这些客体管理器使用内建于 SELinux 函数库的 AVC 服务,然而,如果需要的话,它们也可以提供自己的 AVC,甚至可以不使用 AVC。
-
SELinux 安全策略及其配置文件 位于
/etc/selinux
目录,该目录包含主配置文件/etc/selinux/config
,其中指定了要加载的策略名称(SELINUXTYPE 条目)以及加载时策略初始的强制模式(SELINUX 条目)。/etc/selinux/<SELINUXTYPE>
目录包含可激活的策略,以及它们的配置文件。 -
SELinux 支持模块化的策略。模块化的策略 由一个 基本的策略 及零或多个 策略模块 组成,基本的策略中包含客体类别、权限等强制信息,不同的策略模块用于支持不同的程序或服务。这些模块被编译、链接以后,保存在一个 “策略仓库” 中,在这里它们可以被集成进一个 二进制格式 文件,然后被加载到 安全服务器。
一个策略仓库包含策略包和系统管理员所做的修改,二者被合并到一个单独的逻辑实体中。系统中可以同时存在多个策略仓库,每个仓库的策略完全隔离,可以由管理员切换来部署。切换完毕有时需要重启。仓库位于
/etc/selinux
目录中,以各自仓库的名字命名,如系统默认的/etc/selinux/targeted
仓库。 -
要想制定策略就需要 策略的源码。简单的策略可以用内核策略语言编写的源码,而更常用的方法则是使用具有高级别宏的 “参考策略(Reference Policy)” 来定义策略规则。
-
策略源码的编译、链接和加载需要一些工具的帮助。
-
要想允许系统管理员来管理策略,需要 SELinux 环境、标签文件系统、工具软件以及修改过的 GNU / Linux 命令。
-
要想确保安全事件都被记录下来,Linux 使用审计服务来捕获触碰策略的事件。
13.2.4 SELinux 策略
SELinux 安全子系统会通过策略来检查任何的访问企图,它使用 默认拒绝 的设置,因此它寻找针对 SELinux 类型的 显示允许语句。
SELinux 策略定义了 SELinux 子系统的行为,不同的策略会导致不同的行为。策略与 SELinux 子系统是分开的,策略的更新不需要重新编译内核,也不需要更新程序,策略内置于可以动态加载的二进制策略包中,与内核模块可以被加载很类似。
SELinux 支持 模块化 的策略。策略可以分解为具体的规则。
策略 = 一个 基础策略 + 零或若干个 策略模块
基础策略 包含客体类型、权限等 强制信息,而每个 策略模块用于控制特定的程序或服务。这些模块经过编译、链接后,被统一放置在一个 “策略仓库(Policy Store)” 里,他们可以从这里被转换为二进制格式,然后载入安全服务器。
SELinux 默认由 RBAC 和 TE 组成,MLS 为可选项。
SELinux 预置策略:
- Targeted:针对特定的系统进程进行控制
- Minimum: Targeted 的修改版,只保留一少部分
- MLS: 多级别安全保护
CentOS 使用的 默认策略 是 targeted ,专门针对 特定的系统进程 进行控制。targeted 策略在设计时考虑尽量保护最多的主要进程,同时又不会影响用户的体验。
系统内其它一切系统进程、用户程序和所有自制应用程序都在 unconfined 域运行,并且不纳入 SELinux 的保护范围内。
13.2.5 SELinux 决策流程

- 主体发出访问客体的请求
- 内核中的客体管理器查看 AVC
- 如果缓存中有该主体与客体的 AVC 规则,按规则执行,放行或拒绝。
- 如果没有,该请求继续传递到安全服务器
- 安全服务器在策略中查找主体和客体的上下文,找到后在 AVC 中缓存,从而决定是否有权访问。
- 规则允许,放行
- 规则拒绝或不存在,拒绝。拒绝的消息保存在 /var/log/mesages,
13.3 SELinux 上下文
SELinux Contexts,或 Security Context,为陈述方便以下简称上下文。
SELinux 要求每个主体和每个客体都要有关联的上下文,以便安全服务器(security server)
进程和文件 都用 SELinux 上下文打上了 标签,其中包含多个附加信息,如 SL 用户、角色、类型、级别(可选)。在使用 SELinux 期间,所有的这些信息都被 用来进行访问控制的决策。CentOS 提供的是基于角色访问控制、类型强制、多级别安全的组合。
SELinux 上下文在 进程、L 用户和文件 中使用。
ls -Z
可以查看文件和目录的上下文。
同 rwx 一样,文件的 SELinux 上下文保存在文件的 inode 中。
13.3.1 上下文的内容
SELinux User : Role : Type : Level
SELinux 用户:角色:类型:级别
每个字段都用来决定访问控制,但实际上大多数规则是针对上下文中的 SELinux 类型 来制定的,因此上下文经常简化成只有类型。

在命令后加 -Z
参数可以查看上下文:
- 针对文件:
ls -Z
- 针对进程:
ps -Z
- 针对用户:
id -Z
SL 用户
Linux 中的用户通常是与人类用户相关联的,如 Alice,Bob,或是系统功能,如 admin。而 SELinux 的用户通常是一组或一类用户,比如,所有的标准系统用户都可能都会被分配给 SL 用户名 user_u
,而行政人员可能都分配给 staff_u
。
在参考策略(Reference Policy)中,有一个特殊的 SL 用户永远不会与 L 用户关联,因为它的身份对于系统进程和客体来说太特殊了,这个用户就是 system_u
。
按照习惯,SL 用户名后面都会加后缀 _u
,非强制的。
SL 用户标识对于策略来说,它是 被特定的规则集合授权 的,应用于特定的 MLS/MCS 范围。每个 L 用户 都通过 SELinux 策略被 映射到一个 SL 用户,使得 L 用户得以 继承 施加在 SL 用户身上的 限制。被映射的 SL 用户标识被使用于会话中各进程的 SELinux 上下文中,从而定义这些 SL 用户能够进入什么角色和什么级别。
对于进程来说,SL 用户限制了其角色和可访问的级别。
SELinux 角色
为了能对 TE 域能做进一步的控制,SELinux 也会使用 RBAC,它允许 SL 用户关联到一个或多个角色中,每个角色于是被关联到一个或多个域类型中。
SELinux 角色习惯加后缀 _r
,非强制。
SL 用户被授权担任角色,而角色又授权给域,角色做为媒介服务于域和 SL 用户之间。可进入的角色决定了可进入的域,最终,决定了允许访问哪种客体类型。这有助于减少特权升级攻击的漏洞。
SELinux 类型
是 TE 的属性之一。
对于 进程 来说,类型用于定义 域,对于 文件 来说,用于定义 类型。策略的规则中会定义类型之间可以如何互访,是域访问类型(进程访问文件),还是域访问另一个域(进程间沟通)。
只有 特定的显示放行的 SELinux 策略规则存在 时,才会允许访问。
SELinux 使用一种特别风格的 TE 来强制实施访问控制。它意味着所有的主体和客体都有一个与它们相关联的类型标签,可以用来执行策略规定的规则。
SELinux 级别
级别是 MLS 及 MCS 的属性之一。
该内容为可选,SELinux 支持 MLS 及 MCS,如果使用其中的一种,则在上下文中第四个位置增加 级别 或 级别范围。
CentOS 中,targeted 策略强制实施的是 MCS,其中只有一级敏感度 S0
,支持 1024 个不同类别(c0.c1023),因此所有类别可以表示为 s0-s0:c0.c1023
。
13.3.2 域的转换
对于进程来说,SL 类型就是域。
域的转换是指:进程在另一个域中启动一个新进程。
要想发生域的转换,策略必须满足:
- 源域有权限转换到目标域
- 程序的二进制文件在源域中有执行权限
- 程序的二进制文件有到目标域的入口点
通过执行具有新域的入口点类型的应用程序,一个域中的进程可以转换到另一个域。SELinux 策略中会使用 入口点权限,该权限 可控制哪些程序可用来进入一个域。
例如:
- 一位用户想修改自己的密码,他运行
passwd
程序,/usr/bin/passwd
可执行文件标签中的类型为passwd_exec_t
,passwd
程序会访问/etc/shadow
,其类型为shadow_t
- 有一条 SELinux 策略规则写道:运行于
passwd_t
域的进程可以读写shadow_t
类型的文件。而shadow_t
类型只会应用于密码修改时需要访问的文件上,包括/etc/gshadow
,/etc/shadow
,及它们的备份文件。 - 有一条 SELinux 策略规则写道:
passwd_exec_t
域有到passwd_t
哉的入口点权限。 - 用户执行
passwd
时,用户的 shell 进程转换到passwd_t
域,因为有规则允许运行于passwd_t
域的进程访问shadow_t
类型的文件,所以允许passwd
程序访问/etc/shadow
来更新密码。
此例中,类型强制确保了:
passwd_t
域只能通过执行passwd_exec_t
类型的程序来进入,只能从授权的共享库来执行,如lib_t
类型,不能执行任何其它的程序。- 只有授权的域,如
passwd_t
才能写入shadow_t
类型的文件,即便其它进程是用超级用户权限来运行的,那些进程也无法写入shadow_t
类型的文件,因为它们没有在passwd_t
域中运行。 - 只有授权的域才能转换到
passwd_t
域。 - 在
passwd_t
域运行的进程只能读取授权的类型,如含有etc_t
或shadow_t
标签的文件。
13.3.3 进程的上下文
类型对于进程来说就是域,对文件来说才是类型。
13.3.4 用户的上下文
id -Z
可以查看当前 L 用户的上下文
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
CentOS 中,Linux 用户默认是不受限的(unconfined)。
13.4 Targeted 策略
Targeted Policy 是 CentOS 中默认的 SELinux 策略。使用该策略时,被针对的进程会在一个受限的域中运行,未被针对的进程会运行于未受限的域。如,默认情况下,登陆进来的用户运行于非受限域 unconfined_t
,而被 init
启动的系统进程则会运行于 initrc_t
域,所有这些域都是不受限的。
可执行和可写内存检查适用于受限和非受限域。但是,默认情况下,运行在非限制域中的主体不能分配可写内存并执行它。这减少了缓冲区溢出攻击的隐患。这些内存检查可以通过设置布尔值来禁用,这允许在运行时修改 SELinux 策略。
类型强制是 SELinux Targeted 策略中主要使用的权限控制手段。大部分时间,SL 用户和角色都可以被忽略。
13.4.1 受限进程
几乎所有的 在网络上倾听的服务,如 sshd
或 httpd
都 是受限的。同时,大部分作为超级用户运行,为用户执行任务的进程,如 passwd
也都 是受限的。
当一个进程受限时,它会 在自己的域中运行,如 httpd
进程运行于 httpd_t
域。如果某个受限进程被攻破,取决于 SELinux 策略的配置,攻击者对资源的访问及可能造成的损失是有限的。
范例:如何确认 SELinux 状态
~]# sestatus
SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: enforcing
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 28
~]# touch /var/www/html/testfile
# 创建测试文件 testfile
~]# ls -Z /var/www/html/testfile
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/testfile
# SELinux | 用户 | 角色 | 类型 |级别
- 用户:默认情况下,L 用户是非受限的,因此文件
testfile
的用户是unconfined_u
。 - 角色:RBAC 是用于进程的,而非文件,因此 文件的角色没什么意义,
object_r
对文件来说是一个普通的角色(在持续存储和网络文件系统中)。在/proc/
目录中,与进程相关的文件会使用system_r
角色。 - 类型:
httpd_sys_content_t
类型允许httpd
进程访问该文件。
范例:受限的进程
~]# systemctl start httpd # 启动 Apache HTTP Server
~]# wget http://localhost/testfile # 下载刚生成的文件
......
2018-03-21 16:05:47 (0.00 B/s) - ‘testfile’ saved [0/0]
# 可以正常下载
~]# chcon -t samba_share_t /var/www/html/testfile
# 修改测试文件的类型为 ^^^^^^^^^^^^
~]# ls -Z /var/www/html/testfile
-rw-r--r--. root root unconfined_u:object_r:samba_share_t:s0 /var/www/html/testfile
# 确认修改类型成功 ^^^^^^^^^^^^
~]# wget http://localhost/testfile # 再次尝试下载
......
2018-03-21 16:09:44 ERROR 403: Forbidden. # 失败,被拒绝
即便在 DAC 规则允许 httpd
进程访问测试文件时,因为文件的类型标签决定了 httpd
进程无法访问该文件,于是 SELinux 拒绝了访问,即使是超级用户也无权下载文件文件。
如果 auditd
服务在运行,错误会记录到 /var/log/audit/audit.log
日志中:
type=AVC msg=audit(1521620178.016:565): avc: denied { getattr } for pid=6086 comm="httpd" path="/var/www/html/testfile" dev="dm-0" ino=10492580 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
同时也会有错误记录到 /var/log/httpd/error_log
日志中:
[Wed Mar 21 16:16:18.017784 2018] [core:error] [pid 6086] (13)Permission denied: [client ::1:56072] AH00035: access to /testfile denied (filesystem path '/var/www/html/testfile') because search permissions are missing on a component of the path
13.4.2 非受限进程
非受限进程运行于非受限域中,如 init
程序运行于非受限域 initrc_t
中,非受限内核进程运行于 kernel_t
域中,非受限 L 用户运行于 unconfined_t
域中。对于非受限进程来说,其实是应用了 SELinux 的策略的,只不过策略规则说 “允许运行于非受限域的进程访问几乎所有资源”。在效果上,这些非受限进程相当于只使用 DAC 规则。如果非受限进程被攻破,SELinux 无法避免攻击者获取系统资源和数据,不过当然,DAC 规则仍有效。SELinux 毕竟是覆盖在 DAC 规则之上的一个安全强化措施,它无法代替 DAC 规则。
范例:非受限进程
-
chcon
命令会修改文件的标签,然而,在文件系统标签被restorecon
修复时,这种修改就会丢失。修改文件类型为 Samba 使用的类型:
~]# chcon -t samba_share_t /var/www/html/testfile
确认修改成功:
~]$ ls -Z /var/www/html/testfile -rw-r--r-- root root unconfined_u:object_r:samba_share_t:s0 /var/www/html/testfile
-
暂停
httpd
~]# systemctl stop httpd.service
-
修改
httpd
程序的类型为不会产生域转换的类型httpd
的初始类型为httpd_exec_t
,修改为bin_t
~]# chcon -t bin_t /usr/sbin/httpd
-
确认修改成功:
~]# ls -Z /usr/sbin/httpd -rwxr-xr-x. root root system_u:object_r:bin_t:s0 /usr/sbin/httpd
-
启动
httpd
服务~]# systemctl start httpd.service
-
查看
httpd
进程的类型修改之前,
httpd
进程的域为httpd_t
,修改之后变为unconfined_service_t
~]# ps -eZ | grep httpd system_u:system_r:unconfined_service_t:s0 5680 ? 00:00:00 httpd system_u:system_r:unconfined_service_t:s0 5681 ? 00:00:00 httpd system_u:system_r:unconfined_service_t:s0 5682 ? 00:00:00 httpd system_u:system_r:unconfined_service_t:s0 5684 ? 00:00:00 httpd system_u:system_r:unconfined_service_t:s0 5686 ? 00:00:00 httpd system_u:system_r:unconfined_service_t:s0 5687 ? 00:00:00 httpd
-
以普通用户身份在家目录下载测试文件:
在范例二中下载失败的情况,此时消失了,普通用户可以顺利下载。
~]$ wget http://localhost/testfile ...... 2018-03-22 17:56:52 (0.00 B/s) - ‘testfile’ saved [0/0]
虽然
httpd
进程无权访问类型标签为samba_share_t
的文件,但httpd
进程运行于非限制域中,因此又落回了 DAC 的规则,于是wget
命令成功执行。如果httpd
运行于受限域httpd_t
中,wget
就会失败。 -
restorecon
命令会为文件恢复默认的上下文~]# restorecon -v /usr/sbin/httpd restorecon reset /usr/sbin/httpd context system_u:object_r:bin_t:s0->system_u:object_r:httpd_exec_t:s0
-
确认修改成功后,重启
httpd
服务~]# systemctl restart httpd.service ~]# ps -eZ | grep httpd system_u:system_r:httpd_t:s0 6001 ? 00:00:00 httpd system_u:system_r:httpd_t:s0 6002 ? 00:00:00 httpd system_u:system_r:httpd_t:s0 6003 ? 00:00:00 httpd system_u:system_r:httpd_t:s0 6004 ? 00:00:00 httpd system_u:system_r:httpd_t:s0 6005 ? 00:00:00 httpd system_u:system_r:httpd_t:s0 6006 ? 00:00:00 httpd
进程域成功恢复到 httpd_t
。
13.4.3 受限及非受限用户
每个 L 用户都会用 SELinux 策略被映射到一个 SL 用户,这允许 L 用户继承对 SL 用户的限制。
CentOS 中,L 用户默认全部被映射到 unconfined_u
。
用 seinfo -u
可以查看当前 SL 用户列表。
受限和非受限的 L 用户是可执行、可写内存检测的主体,他们也会受到 MLS 或 MCS 的限制。
对新建 L 用户的限制
当 Linux 中新用户第一次登陆时,pam_selinux
这个 PAM 模块会自动把该用户映射到 SELinux unconfined_u
用户上,并设置其上下文,该用户的 shell 就会用该上下文启动。
非受限用户运行程序也能保证安全
当 L 用户运行于 unconfined_t
时,SL 策略是生效的,只不过是策略的规则说,允许运行于 unconfined_t
域的 L 用户几乎可以访问任何资源。如果一个非受限的 L 用户执行一个程序,SELinux 策略说该程序可以从 unconfined_t
域转换到它自己的受限域,则该非受限的 L 用户仍然是该受限域限制的主体。这样做带来的好处是,即使 L 用户是非受限的,而程序依然保持受限,从而,要想利用应用程序的缺陷就会受到策略的限制。
对受限用户运行程序的限制
类似的,我们也可以对受限用户进行这些检查。每个受限的 L 用户被限制在自己的域中,SL 策略也可以定义一个 “从一个受限用户的域到其目标受限域” 的转换,此时,受限的 L 用户是目标受限域所限制的主体。根据其角色的定义,特殊权限是与受限用户相关联的。
SL 用户限制域及能力限制
下表仅举例说明不同的 SL 用户可以如何被限制具体的能力:
用户 | 角色 | 域 | X Window | su 或 sudo | 在家目录及 /tmp 执行(默认) | 网络 |
---|---|---|---|---|---|---|
sysadm_u | sysadm_r | sysadm_t | 是 | su 及 sudo | 是 | 是 |
staff_u | staff_r | staff_t | 是 | 仅 sudo | 是 | 是 |
user_u | user_r | user_t | 是 | 否 | 是 | 是 |
guest_u | guest_r | guest_t | 否 | 否 | 否 | 否 |
xguest_u | xguest_r | xguest_t | 是 | 否 | 否 | 仅 Firefox |
- 如果 SELinux 规则允许,
user_t
、guest_t
、xguest_t
域中的 L 用户只能运行 SUID 程序,如passwd
。这些用户无法运行su
、sudo
,因此无法借助它们变身为超级用户。 sysadm_t
、staff_t
、user_t
、xguest_t
域中的 L 用户可以用 X Window 登陆,也可以从终端登陆- 默认情况下,
guest_t
和xguest_t
域中的 L 用户无法在家目录或tmp
目录执行程序,即使他们对目录有写权限。可以避免恶意程序修改用户的文件。 - 默认情况下,
staff_t
和user_t
域中的 L 用户可以在其家目录及/tmp
目录执行程序 xguest_t
域中的 L 用户唯一拥有的网络访问权限是用 Firefox 连接到网页
注意,对于系统进程和客体来说,system_u
是一个特殊的用户标识,它永远不可以与 L 用户关联。同样的,unconfined_u
和 root
是非受限用户。
特殊 SL 角色
以下举例说明一些特殊的 SL 角色,这些角色决定了用户能做什么:
webadm_r
:只能管理与 Apache HTTP Server 相关的 SL 类型dbadm_r
:只能管理与 MariaDB 数据库、PostgreSQL 数据库相关的 SL 类型logadm_r
:只能管理与syslog
和auditlog
相关的 SL 类型secadm_r
:只能管理 SELinuxauditadm_r
:只能管理与audit
子系统相关的进程
用 seinfo -r
查看所有角色。
sudo
转换和 SL 角色
某些时候,受限用户可能会需要完成一个需要超级用户权限的管理任务,他必须用 sudo
命令来获得一个受限的管理员角色。当受限用户用 sudo
执行命令时,他们的角色会发生改变,空间如何改变则取决于 /etc/sudoers
中的规则或 /etc/sudoers.d/
目录中的某个文件。
范例:为现有 SL 用户配置受限的管理员角色
-
创建一个新的 SL 用户,并指定其默认角色及附加的受限管理员角色
~]# semanage user -a -r s0-s0:c0.c1023 -R "staff_r webadm_r" neo_u # 默认角色| 附加角色 | 新 SL 用户名
-
新建用户的上下文直接参照原 SL 用户
staff_u
的上下文,所以直接复制其上下文文件。~]# cp /etc/selinux/targeted/contexts/users/staff_u /etc/selinux/targeted/contexts/users/neo_u
-
把新建 SL 用户映射给现有 L 用户
neo
semanage login -a -s neo_u -rs0:c0.c1023 neo
-
在
/etc/sudoers.d/
目录中创建一个与neo
同名的配置文件:~]# echo "linux_user ALL=(ALL) TYPE=webadm_t ROLE=webadm_r /bin/sh " > /etc/sudoers.d/neo
-
用
rstorecon
修复 L 用户家目录~]# restorecon -FR -v /home/neo
-
重新登陆
neo
,用户的标签被更新为其默认的 SL 角色,即staff_r
~]# id -Z neo_u:staff_r:staff_t:s0:c0.c1023 # ^^^^^^^
而当该 L 用户运行完
sudo
以后,其 SL 上下文会变成/etc/sudoers.d/neo
中指定的附加角色:~]$ sudo -i ~]# id -Z SELinux_user_u:webadm_r:webadm_t:s0-s0:c0.c1023 # ^^^^^^^^
13.5 对用户的限制
L 用户被默认映射到 unconfined_u
用户,所有由 unconfined_u
启动的进程都处于 unconfined_t
域中,这意味着在 DAC 策略的控制之下,用户可以在系统内任意访问。然而,也存在一些受限的用户,他们就会被限制能力。借助策略,每个 L 用户被映射到一个 SL 用户,实现了 L 用户可以继承 SL 用户的限制。这些限制可能是:
- 运行 X 窗口
- 使用网络
- 运行 SUID 程序
- 运行
su
和sudo
命令
例如,由 SL 用户 user_u
启动的进程运行于 user_t
域中,这类进程可以连网,但无法使用 su
和 sudo
命令,这有助于保护系统免于用户的恶意操作(被劫持)带来的危险。
13.5.1 Linux 和 SL 用户映射
可以使用 semanage login -l
查看 L 用户和 SL 用户的映射:
[root@zion ~]# semanage login -l
Login Name SELinux User MLS/MCS Range Service
__default__ unconfined_u s0-s0:c0.c1023 *
root unconfined_u s0-s0:c0.c1023 *
system_u system_u s0-s0:c0.c1023 *
所有的 L 用户先是被映射到 L 用户 __default__
,然后再统一被映射到 unconfined_u
用户。
13.5.2 限制 Linux 新用户:useradd
当使用 useradd
创建用户时,如果不指定参数,该用户会被映射到 unconfined_u
用户,使用的是如下的默认映射:
__default__ unconfined_u s0-s0:c0.c1023 *
如果用 useradd
创建用户时,用 -Z
参数可以指定该用户要映射要被映射到哪个 SL 用户身上。
useradd -Z user_u user
13.5.3 限制现有 L 用户:semanage login
新建 L 用户默认被映射到 unconfined_u
上,如果你想改变映射,使用 semanage login
命令。
semange login -a -s neo_u neo
13.5.4 修改默认映射
默认 L 用户都会先被映射给 __default__
用户,然后再把该用户映射到 unconfined_u
,如果希望修改这个默认映射,如希望把 L 用户默认映射给 user_u
,可以这样做:
semanage login -m -S targeted -s "user_u" -r s0 __default__
13.5.5 xguest
:kiosk 模式
kiosk,报刊厅,意指公共场所,信息不安全地带。kiosk user account,访客用户。随来随用,用完就走。
在安装过 xguest 包以后就会产生 xguest
这个访客用户,它的权限非常有限:登陆、用 firefox 浏览网页。
所有的访客用户都分配给 xguest_u
,该用户登陆后发生的所有改变在他退出后都会丢失,如登陆期间创建的文件,或其它设置的修改等。
为了照顾各类使用人群,该用户不用密码保护,因此,它只能由 SELinux 来强制保护。而且该用户只能用 GDM 登陆系统。
13.5.6 用户执行程序的布尔值
应用程序会继承用户的权限,因此,在家目录及 /tmp
等用户有写权限的目录中,禁止 L 用户执行程序可以避免有缺陷或恶意程序修改用户文件。
使用 setsebool
命令来配置布尔值,可以改变用户的行为,setsebool -P
做出的改变会持续生效,因此如果不希望在重启后还有效,就不要使用 -P
参数。
setsebool -P guest_exec_content off
禁止 guest_t
域中的 L 用户在其家目录及 /tmp
运行程序
13.6 使用 SELinux
13.6.1 日志
CentOS 中,dbus 和 audit 包是默认安装的,setroubleshoot-server 需要手动安装。
在 audit 服务运行的情况下,SELinux 拒绝消息会被写入 /var/log/audit/audit.log
,消息内容类似下面:
type=AVC msg=audit(1223024155.684:49): avc: denied { getattr } for pid=2000 comm="httpd" path="/var/www/html/file1" dev=dm-0 ino=399185 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=system_u:object_r:samba_share_t:s0 tclass=file
另外还会有一条消息被写入 /var/log/message
:
May 7 18:55:56 localhost setroubleshoot: SELinux is preventing httpd (httpd_t) "getattr" to /var/www/html/file1 (samba_share_t). For complete SELinux messages. run sealert -l de7e30d6-5488-466d-a606-92c9f40d316d
setroubleshootd
过去经常被用于分析 AVC 消息,但现在它不再以服务的形式运行了。有两个新的程序可以在需要的时候间接启动 setroubleshoot
:
sedispatch
作为audit
子系统的一分部运行,当返回一条 AVC 拒绝消息时,sedispatch
会使用dbus
发送一条消息,如果setroubleshootd
此时在运行,消息会直接发送给它,如果没有运行,sedispatch
会自动启动它。seapplet
运行于系统工具栏,在setroubleshootd
中等待dbus
传来的消息,它会开启通知气泡,把 AVC 消息传递给用户查看。
13.6.2 配置文件
/etc/selinux/config
是 SELinux 的主要配置文件,它控制着 SELinux 启动还是禁用,以及当前使用哪个 SELinux 模式、哪个策略。
13.6.3 状态与模式
SELinux 状态
SELinux 的状态要么是 启用(Enabled),要么是 禁用(Disabled),这两种状态 不能在线热切换,切换之后 必须重启 系统。
通过编辑 /etc/selinux/config
配置文件,来切换 SELinux 状态或模式。
SELINUX=enforcing/permissive/disabled
SELinux 工作模式
在开启状态下,SELinux 有两种工作模式:
-
强制模式:enforcing,默认值。会在系统上强制实施,SELinux 根据安全策略来拒绝访问。
-
宽容模式:permissive。不强制实施,不会拒绝访问,但会把强制模式下会生成的拒绝访问的信息记录到日志。
这两种工作模式可以使用 setenforce
命令随时在线切换,但该命令造成的变化,在重启后会自己消失。

常用命令
getenforce
查看 SELinux 当前工作模式
setenforce 0
切换到宽容模式
setenforce 1
切换到强制模式
当系统运行于宽容模式时,允许用户给文件打错误的标签。当 SELinux 禁用时创建的文件是不会被打标签的,如果此时切换到强制模式就会造成问题,因为文件标签是错的,或是根本没标签。为了避免错误标签及无标签文件造成问题,从禁用状态切换到启用状态时,文件系统会 自动重新打标签。
启动时修改 SELinux 模式
系统启动时,可以设置几个内核参数来改变 SELinux 的运行方式:
enforcing=0
该参数会让系统 以宽容模式启动,便于排查问题。
如果文件系统被严重干扰,使用该模式可能是唯一的排查办法了。而且,该模式下,系统会继续正确地创建标签。在该模式下产生的 AVC 消息会与强制模式略有不同。在宽容模式中,只有第一次拒绝会被报告,然而在强制模式中,每当读取一次目录或程序暂停时都会收到一次拒绝。在宽容模式中,你收到的是相同的 AVC 消息,但实际上当程序继续读取目录中的各个文件时,你会额外收到不同的拒绝。
selinux=0
该参数会使内核在启动时不加载 SELinux 架构的任何部分,即 禁用 SELinux。当 init
脚本发现系统是携带 selinux=0
参数启动时,它会 创建 /.autorelabel
文件,在下次系统启动并启用 SELinux 时,这会促使系统 自动重新打标签。
不推荐使用该模式来排查问题。
autorelabel=1
该参数会促使系统先创建 /.autorelabel
文件,然后重启。即相当于连续运行以下命令:
~]# touch /.autorelabel
~]# reboot
13.6.4 布尔值
为了在运行时(runtime)能够灵活地启用、禁用某些 SELinux 策略,系统创建了布尔值这个概念。
在实际使用中,有时会发现一些活动被拒绝了,然而明明在逻辑上有充分的理由放行的。当这些理由信赖于特定的因素或选择时,SELinux 策略的编写者就会被鼓励 将这些策略变为可选。在 SELinux 世界里,可选就意味着可以 通过 SELinux 布尔值来开、关特定的访问控制。
查看布尔值列表
semanage boolean -l
查看布尔值名称、描述、开关状态
getsebool -a
查看布尔值名称、开关状态
getsebool boolean-name1 boolean-name2
查看指定名称的布尔值状态
修改布尔值
通过设置布尔值来 开启或关闭某项规则。
setsebool [-P] boolean_name on/off
-P
持续生效,重启后不会丢失
on/off
开关状态,可以用 1/0 或 true/false 来替换
如果不加 -P 参数,只在当前有效,系统重启后失效。
~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off # 当前是为关闭状态
~]# setsebool -P httpd_enable_homedirs 1 # 打开
~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on
13.6.5 给文件打标签
在使用 SELinux 的系统中,所有的进程和文件都会被打上标签,以此来标示安全相关的信息,即 SELinux 上下文,以此来制定决策。在 DAC 系统中,访问控制是基于 L 的 UID 与 GID。SELinux 的策略规则检查是在 DAC 规则检查之后进行的,如果 DAC 规则已经拒绝了访问,则 SELinux 策略规则就无需使用。
默认情况下,新创建的文件和目录会 继承父目录的 SL 类型。
临时修改标签
chcon
命令用于 临时修改 文件的上下文,但它造成的变化会在 文件系统重打标签时被覆盖,也会 被 restorecon
命令的执行给覆盖。
SELinux 策略决定了是否允许用户修改给定文件的上下文。经常发生的事情是,类型标签打错了,导致拒绝访问。
chcon
手动修改文件的上下文
chcon [-R] [-t type] [-u user] [-r role] 文件
chcon [-R] --reference=参考文件
-v
显示对每个文件的操作过程,必须放在 所有参数之前 🌟
-R
遍历子目录及其文件
-t
修改类型
-u
修改用户
-r
修改角色
--reference=
参考指定文件
【 范例 】修改指定文件的类型
先查看 /etc/hosts
的类型,将之用于 /etc/cron.d/checktime
文件上。
~]# ll -Z /etc/hosts
-rw-r--r--. root root system_u:object_r:net_conf_t:s0 /etc/hosts
# 参考文件的类型 ^^^^^^^^^^
~]# chcon -v -t net_conf_t /etc/cron.d/checktime
# ^^^^^^^^^^
changing security context of ‘/etc/cron.d/checktime’
~]# ll -Z /etc/cron.d/checktime
-rw-r--r--. root root unconfined_u:object_r:net_conf_t:s0 /etc/cron.d/checktime
# ^^^^^^^^^^ 修改成功
【 范例 】指定参考文件,自动修改文件标签
参考文件 /etc/shadow
,自动修改文件 /etc/cron.d/checktime
的上下文,修改之后该文件的所有标签与参考文件完全一致。
用 --reference
参数来 指定参考文件。
~]# chcon -v --reference=/etc/shadow /etc/cron.d/checktime
~]# ll -Z /etc/shadow /etc/cron.d/checktime
-rw-r--r--. root root system_u:object_r:shadow_t:s0 /etc/cron.d/checktime
----------. root root system_u:object_r:shadow_t:s0 /etc/shadow
持久修改
semanage fcontext
命令用于 持久修改文件的上下文,不会受文件系统重打标签(Relabel)的影响,即文件系统被重打标签之后,之前用 semanage fcontext
修改的标签依然会保持原标签不变。
Relabel 即指文件系统重新打标签的过程。
如何免于 RELABEL
要想让 SL 上下文的修改能够避免于 relabel,需要在使用 semanage
修改完标签之后,再用 restorecon
来实际应用该修改才行。
注意:semanage fcontext
后面必须接 文件的绝对路径,否则不会生效。
~]# semanage fcontext -a options file-name|directory-name
~]# restorecon -v file-name|directory-name
😈 为什么在 semanage fcontext
之后还要再多做一步 restorecon
呢?
事情是这样婶儿的:
在使用 targeted 策略时,semanage fcontext
所做的修改会保存到 /etc/selinux/targeted/contexts/files/
目录中的两个文件中:
file_contexts
:semanage fcontext
所做的修改首先会检查该文件,如果在该文件中找到了要修改上下文的文件或目录的名称,则在此文件更新。file_contexts.local
:如果上一步中,没有找到目标文件或目录的名称,则把修改记录添加到此文件中。
这两个文件生成、更新之后,会有两个工具来读它们:
setfiles
:当文件系统要重打标签(relabel)时,会用该工具来执行打标签的任务。它打的时候会先读取上面的文件,以将其排除在外,因此,这样一来,semanage fcontext
所做的修改才能免于重打标签的毒手。restorecon
:它可以随时拿来修复默认上下文
修复上下文
restorecon
命令会根据当前所在目录查找对应的规则,以确定其默认的上下文。如果当前文件的上下文与所在目录默认上下文不匹配,则将其纠正。有了这个命令,便无需用 chcon
手动修改了。
文件被移动后,往往会携带原目录的上下文,因此在存取中会造成上下文不匹配,用该命令来予以纠正。
restorecon [-Rv] 文件或目录
-R
遍历子目录
-v
显示过程
restorecon -Rv /etc/cron.d
修复指定目录中所有文件的 SL 类型
13.6.6 file_t
及 default_t
类型
当使用支持 扩展属性 的文件系统时,如果一个文件尚未分配扩展属性,其默认 SL 类型为 file_t
,该类型仅仅是做这个用的,并不会存在于正确打标签的文件系统中,因为在使用 SELinux 的系统中,所有文件都应该有正确的上下文,而 fiel_t
类型是永远不会在上下文的配置中使用的。
在文件上下文配置过程中,那些不匹配任何特征的文件,会被分配 default_t
类型,以便能与没有标签的文件区别开来,通常这些文件在受限域中是拒绝访问的。
13.6.7 挂载文件系统
默认情况下,当一个 支持扩展属性的文件系统 被挂载时,每个文件的上下文是 从文件的扩展属性 security.selinux
获得 的。使用 mount -o context
命令可以覆盖现有的扩展属性。
而在 不支持扩展属性的文件系统 中,会按照文件系统的类型,依照策略为文件分配 单一的、默认的上下文。使用 mount -o context
命令可以指定一个不同的默认上下文。
mount -o context
的使用场景:
- 不信任某个文件系统能提供正确的属性,如在多个系统中使用的可移动磁盘,通常用
context="system_u:object_r:removable_t"
- 在不支持扩展属性的文件系统中支持文件打标签,如 FAT 或 NFS 驱动器。
用 context
参数指定的上下文不会被写到磁盘中,原始的上下文还是被保留的,当你不用 context
挂载磁盘时就会看到了,前提是支持扩展属性的文件系统。
挂载文件系统时使用上下文参数
在挂载文件系统时,通过特定的 挂载参数,实现特定的访问控制。
要想以指定的上下文挂载文件系统,覆盖现有的上下文 的话,或是为不支持扩展属性的文件系统 指定默认上下文:
mount -o context=SELinux_user:role:type:level
挂载驱动器时对文件系统上下文的改变 不会被写入磁盘。
范例:用挂载参数控制服务对 NFS 的访问
NFS 驱动器在客户端挂载时,使用的是针对 NFS 的策略定义的默认标签。在一般的策略中,该默认上下文使用的是 nfs_t
类型。
- 希望 禁止 其它服务访问挂载的 NFS:挂载时 不附带额外参数
- 希望 允许 网页服务访问挂载的 NFS,可以为其 指定网页服务专用的类型:
~]# mount server:/export /local/mount/point -o \
context="system_u:object_r:httpd_sys_content_t:s0"
这样,在挂载的文件系统中新创建的文件和目录 看上去 就有了 -o context
指定的上下文了,因为这些改变不会保存到磁盘中,所以为了维持这个上下文,每次挂载 该文件系统时必须使用 相同的上下文参数 才行。
类型强制是 SELinux Targeted 策略中主要的权限控制手段。大部分时间,SL 用户和角色都可以被忽略。因此,当使用 -o context
覆盖原上下文时,会使用 system_u
用户和 object_r
角色,再配合类型使用。如果没有使用 MLS 或 MCS,可使用 s0
级别。
当文件系统 挂载时使用了 context
参数 时,系统会 禁止用户和进程修改上下文,chcon
会返回 Operation not supported
错误。
修改默认上下文
在支持扩展属性的文件系统中,磁盘中缺少上下文的文件会被看作它有策略指定的默认的上下文。在普通的策略中,这个默认的上下文使用 file_t
类型。如果想使用不同的默认上下文,挂载文件系统时可以使用 defcontext
参数。
下面范例为在 /test/
目录上挂载一个新建的文件系统 /dev/sda2
,假设在 /etc/selinux/targeted/contexts/files/
目录中不存在为 /test/
定义的上下文:
~]# mount /dev/sda2 /test/ -o defcontext="system_u:object_r:samba_share_t:s0"
本例中:
defcontext
参数为未打标签的文件指定了默认上下文- 挂载时,文件系统的根目录
/test/
被视为有defcontext
指定的上下文(标签不会保存在磁盘上),这会影响为/test/
目录中创建的文件打标签:新文件会继承samba_share_t
类型,这些标签是会保存在磁盘上的。 - 在
/test/
目录中创建的文件会保存其标签
同一 NFS 的多次挂载
NFS 的导出目录的同一子目录,虽然支持多次被挂载,但会导致叠加挂载,文件在两个不同的上下文中都能被访问,会造成混乱。
而同一 NFS 导出目录的不同子目录,可以同时用不同的上下文分别挂载,使用的是 -o nosharecache,context
参数:
~]# mount server:/export/web /local/web \
-o nosharecache,context="system_u:object_r:httpd_sys_content_t:s0"
# ^^^^^^^^^^^^^^^^^^^
~]# mount server:/export/database /local/database \
-o nosharecache,context="system_u:object_r:mysqld_db_t:s0"
# ^^^^^^^^^^^
/etc/fstab
中指定挂载参数
server:/export /local/mount/ nfs context="system_u:object_r:httpd_sys_content_t:s0" 0 0
13.6.8 标签的维护
当复制、移动、归档文件和目录时,上下文发生了什么?如何在复制和归档时保留上下文?
复制文件和目录
复制文件和目录时,如果不指定参数,不会保留原上下文。产生的新文件和目录的上下文是基于默认标签规则的,而不是原文件和目录的上下文。
- 如果向另一目录复制文件,不加参数复制,则目标文件的上下文会根据目标目录的默认上下文创建
- 如果目标文件已存在,不加参数复制,覆盖后,已存在文件的上下文会保留
- 使用
--preserve=context
参数来保留原上下文 - 使用
cp --context=system_u:object_r:samba_share_t:s0
来指定目标文件的上下文
根据以上特性,建议尽量用复制替代移动,这样能更好地保证标签的正确性。
移动文件和目录
文件和目录被 移动时会保留原始的上下文,但在许多情况下,目标目录的上下文往往与其不同。
检查上下文
matchpathcon
可以用来 检查文件和目录的上下文是否正确,所谓正确的标准就是系统策略中该路径的默认上下文。
matchpathcon -V /var/www/html/*
检查该目录中所有文件的上下文是否正确
检查出来的错误可以用 restorecon
来修正。
用 tar
归档文件
tar
默认不会保留扩展属性,而因为 SL 上下文是保存在扩展属性中的,所以归档时文件上下文会丢失。
可以使用 tar --selinux
参数来保留上下文。
- 打包时使用该参数,包里的文件上下文保留原始标签
- 解包时使用该参数,解开的文件会保留原始上下文
- 解包时不使用该参数,则解开的文件会继承当前目录的上下文
13.6.9 信息分析
avcstat
查看 AVC 统计
该命令会显示 本次开机以来 AVC 的简要统计 信息,可以指定实时显示时刷新的频率。
~]# avcstat
lookups hits misses allocs reclaims frees
47517410 47504630 12780 12780 12176 12275
sestatus
查询系统状态
sestatus
可 查询 SELinux 当前状态信息:
- SELinux 当前状态,启动或关闭
- SELinux 关键目录的位置,/etc/selinux
- 当前已载入的策略名称
- SELinux 的挂载点
- 当前工作模式
- 配置文件中指定的工作模式
- MLS 状态
- 对于未定义操作,是否默认采取拒绝
sestatus [-vb]
-v
查看 /etc/sestatus.conf 中记录的文件与进程的安全上下文
-b
查看布尔值列表,左侧为布尔值,右侧为开关状态
seinfo
查询策略中要素
seinfo
用于 查询 SELinux 策略中的各要素,身份、角色、类型、规则。它使用 policy.conf
、二进制策略文件、策略包的模块化列表或一个策略列表文件做为输入。其输出也会根据不同的输入而有所区别。
seinfo [-trub]
--all
查看所有要素:SELinux 状态、规则、身份、角色、类型
-u
查看所有身份
-r
查看所有角色
-t
查看所有类型
-b
查看所有规则
sesearch
查找特定的规则
sesearch
用于在策略中 查找特定的规则。可以在策略源文件中查找,也可以在二进制文件中查找。
sesearch [-A] [-s 主体类型] [-t 客体类型] [-b 规则名称]
-A
仅查看放行的规则
-C
显示条件表达式及状态
sesearch -C
会在返回结果的开头加上两个字母,第一个字母表示 本条规则当前状态,第二个字母表示 布尔值的哪个状态会启用本条规则。 E D T F 分别为 Enabled, Disabled, True, False 的首字母。
- ET:本规则当前启用,布尔值打开时会启用本规则
- EF:本规则当前启用,布尔值关闭时会启用本规则
- DT:本规则当前禁用,布尔值打开时会启用本规则
- DF:本规则当前禁用,布尔值关闭时会启用本规则
-s
指定主体类型,域
-t
指定客体类型,文件类型
-b
指定规则名称
sesearch
返回的格式是这样的:
allow crond_t setfiles_exec_t : file { read getattr execute open } ;
# 控制 | 域 | 类型 | 客体 | 操作
13.6.10 禁用 ptrace()
ptrace()
系统调用允许一个进程观察并控制另一个进程的执行,可以修改它的内存和寄存器。该调用主要用于策略开发人员做调试。日常使用中应该禁用该调用,以提升系统安全。可以通过启用 deny_ptrace
布尔值来完成:
setsebool -P deny_ptrace on
13.6.11 缩略图保护
缩略图可能会允许攻击者使用移动介质攻破一个锁定的机器,如 U 盘或光盘。当系统检测到移动介质时,Nautilus 文件管理器会执行缩略图驱动程序代码,在文件资源管理器中显示缩略图,不管主机是否被锁定。这个行为非常危险,因为如果执行缩略图的程序有漏洞的话,很有可能被攻击者利用来绕过锁屏而无需输入密码。
因此,有一个新的策略用来防止这些攻击的发生,该策略能保证锁屏时也同时锁定所有的缩略图驱动程序。
13.7 SELinux 排错
13.7.1 拒绝访问时都发生了什么?
诸如拒绝访问、允许访问这样的 SELinux 的决策会被缓存,即 AVC。发生拒绝访问时,拒绝消息会记录到日志,这些消息也称为 AVC 拒绝。根据当前运行的日志服务,可以把日志保存到以下位置:
服务 | 日志位置 |
---|---|
auditd 开启 | /var/log/audit/audit.log |
auditd 关闭,rsyslogd 开启 | /var/log/messages |
setroubleshootd, rsyslogd, auditd 开启 | /var/log/audit/audit.log ,更易读的拒绝也被另存到 /var/log/messages |
~]# grep "SELinux is preventing" /var/log/messages
~]# grep "denied" /var/log/audit/audit.log
13.7.2 常见问题
标签问题
使用 SELinux 的系统中,所有的进程和文件都有上下文,标签要是错了,就可能造成访问被拒绝。如果程序的标签错了,就会造成其进程标签也是错的,也会造成拒绝访问,进程又会生成标签错误的文件。
一个常见的打错标签的原因是,为服务使用非标准目录。例如,本应该为网站使用 /var/www/html/
目录,结果管理员使用了 /srv/myweb/
。在 CentOS 中,/srv
目录的类型为 var_t
,在该目录中创建的文件和目录也会继承该标签。新建的一级目录(如 myserver/
)会默认使用 default_t
标签,SELinux 会阻止 httpd
访问这两种标签。要想允许访问,SELinux 必须知道 /srv/myweb/
中的文件是可以被 httpd
访问的。
semanage fcontext -a -t httpd_sys_content_t "/srv/myweb(/.*)?"
为 /srv/myweb/
指定新的上下文
restorecon -R -v /srv/myweb
应用上面的改变
如果用 matchpathcon -V /var/www/html/*
检查出错误的上下文,可以用 restorecon -R -v /var/www/html/
来修复。
受限服务如何运行
要想控制服务之间的互访,可以通过布尔值的开关来实现。例如,要允许 Apache HTTP Server 与 MariaDB 通信,可以启用布尔值 httpd_can_netwrok_connect_db
。
要想查询某个服务相关的访问,可以用 getsebool -a | grep ftp
实现。
端口号
根据策略的配置,可以限制服务只能运行于特定的端口号,如果尝试在策略之外修改服务运行的端口号,会导致服务启动失败。
semanage port -l | grep http
查询 http 相关的端口号
http_port_t
端口类型定义了 Apache 可以监听的端口。
要想增加 httpd
可以监听的端口,必须使用 semanage port -a http_port_t -p tcp 9876
。
规则的进化与崩溃的程序
程序有可能会崩溃,造成 SELinux 的拒绝访问,同时 SELinux 的规则也在进化,SELinux 有可能在某种程度上没有检测到程序的运行,造成了拒绝访问,即便程序是正常运行的。例如一个新版 的PostgreSQL 发布后,它的一些操作可能当前的策略从未见过,导致拒绝访问,可这些访问应该被放行。
这种情况下,可以使用 audit2allow
来创建自定义策略模块,以放行访问。
13.7.3 解决问题
Linux 权限
拒绝访问发生时,应该检查标准的 L 权限,毕竟 SL 的策略是在 DAC 规则之后使用的,如果 DAC 规则已经拒绝了,就不会再使用 SL 策略了。
如果访问拒绝,而日志中没有发现拒绝,应该查看 L 权限是否导致拒绝的原因。如需要可用 chown
命令修改文件的拥有者和属组。
不记录拒绝的可能原因
在某些情况下,AVC 拒绝消息可能不会被记录,程序和系统库函数经常会过多的检测,有些检测对于任务的完成是不必要的。为了在维持最少特权时,还能尽量减小日志中对无害程序的检测产生的 AVC 拒绝的数量,通过使用 dontaudit
规则,策略无需开放权限就可以减少 AVC 拒绝。这些规则在标准策略中是很常见的。使用 dontaudit
的缺点是,虽然 SELinux 拒绝了访问,日志中却没有拒绝消息,给排错增加了难度。
semodule -DB
可以临时禁用 dontaudit
规则,以允许日志记录所有拒绝
其中的参数 -D
会导致禁用 dontaudit
规则,-B
会重建策略。运行该命令以后,再尝试运行之前产生问题的程序,根据日志来排查问题。
semodule -B
重新启用 dontaudit
并重建策略
sesearch --dontaudit
查看所有 dontaudit
规则
sesearch --dontaudit -s smbd_t | grep squid
在 smbd_t
域中筛选 squid 相关的规则
服务的说明文档
服务的说明文档(man page)包含很多有价值的信息,比如在给定的情况下应该使用什么文件类型,影响服务对客体访问的布尔值有哪些等等。这些信息有的在标准的说明文档中,有的可以从 SELinux 策略中,针对每个服务域,使用 sepolicy manpage
来自动生成,这类的说明文档的名称为 service-name-selinux
格式,通常安装了 selinux-policy-doc
包就会有。
例如,httpd_selinux(8)
的说明文档中包含:在不同情况下应使用哪种文件类型,在家目录要想运行脚本、共享文件、访问目录的话需要哪些布尔值,等等。
宽容域
当 SELinux 运行于宽容模式时,不会拒绝访问,但对违反规则的访问仍然会记录日志。
宽容域允许 某个进程单独以宽容模式运行,主要用处:
- 排错
- 管理员为新程序创建策略
启用宽容域
semanage permissive -a domain
把一个域切换到宽容模式
semodule -l | grep permissive
查询当前所有宽容域
semanage permissive -d domain
取消宽容域
禁用宽容域
semodule -d permissivedomains
禁用所有的宽容域
一旦使用了该命令,策略模块就不会出现在 semodule -l
的结果中,要想查看包含被禁用模块在内的所有策略,可以用:
semodule --list-modules=full
宽容域的拒绝
在日志中,强制模式下的 AVC 拒绝所产生的 SYSCALL
消息中 success=no
,因为访问被拒绝;而在宽容模式下的 AVC 拒绝所产生的 SYSCALL
消息中 success=yes
,因为访问被放行。
而 AVC 消息在两种模式下均为 avc: denied
。
查找、查看拒绝
假设系统已经安装了 setroubleshoot、setroubleshoot-server、dbus、audit,并正在运行。
ausearch
audit
安装包提供了 ausearch
工具,基于不同的搜索规范,可用来 在 audit
服务的日志中查询事件,它会访问 /var/log/audit/audit.log
日志,因此必须用超级用户运行。
ausearch -m avc,user_avc,selinux_err,user_selinux_err
查找所有拒绝
ausearch -m avc -ts today
查找今天发生的拒绝
ausearch -m avc -ts recent
查找 10 分钟内发生的拒绝
ausearch -m avc -c httpd
查找 httpd 服务产生的拒绝
aureport
audit
安装包提供了 aureport
工具,它可以 生成审计系统日志的总结报告,它会访问 /var/log/audit/audit/log
日志,因此必须用超级用户运行。
aureport -a
查看 AVC 拒绝列表
~]# aureport -a
AVC Report
========================================================
# date time comm subj syscall class permission obj event
========================================================
1. 05/01/2009 21:41:39 httpd unconfined_u:system_r:httpd_t:s0 195 file getattr system_u:object_r:samba_share_t:s0 denied 2
2. 05/03/2009 22:00:25 vsftpd unconfined_u:system_r:ftpd_t:s0 5 file read unconfined_u:object_r:cifs_t:s0 denied 4
sealert
setroubleshoot-server
安装包提供了 sealert
工具,它可以读取由 setroubleshoot-server
翻译过的拒绝消息。每一个拒绝都被分配一个 ID,具体的拒绝保存在 /var/log/messages
,如:
setroubleshoot: SELinux is preventing /usr/sbin/httpd from name_bind access on the tcp_socket. For complete SELinux messages. run sealert -l 8c123656-5dda-4e5d-8791-9e3bd03786b7
# ^^^^^^^^ 拒绝消息的 ID ^^^^^^^^^^^
sealert -l 8c123656-5dda-4e5d-8791-9e3bd03786b7
查看该拒绝的详细分析,为什么拒绝,如何解决
如果你使用了 X 窗口系统,并且安装了 setroubleshoot
及 setroubleshoot-server
安装包,而且 dbus
和 auditd
服务都在运行。当发生拒绝时,系统会弹出一个信息框:
点击 Show 会显示详细信息:
audit 原始消息
原始的 audit 消息记录在 /var/log/audit/audit.log
日志中,以下是 AVC 拒绝消息及相关的 SYSCALL 的例子:
type=AVC msg=audit(1226874073.147:96): avc: denied { getattr } for pid=2465 comm="httpd" path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13 a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48 gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd" exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
该条消息记录的是 Apache HTTP Server 尝试访问 /var/www/html/files
时产生的拒绝消息。下面拆开来看看:
AVC 消息
{ getattr }
花括号中的项目表明了被拒绝的权限。getattr
条目表明源进程在 尝试读取目标文件的状态信息,该行为发生于读取文件之前,由于文件的标签错误导致该行为被拒绝。常见的权限包括 getattr
、read
、write
。
comm="httpd"
启动该进程的可执行文件,其完整路径可在 SYSCALL 消息中 exe=
部分找到,本例为 exec="/usr/sbin/httpd"
path="/var/www/html/file1"
进程尝试访问的 客体的路径
scontext="unconfined_u:system_r:httpd_t:s0"
主体(进程)的上下文,source context
tcontext="unconfined_u:object_r:samba_share_t:s0"
客体的上下文,target context
SYSCALL 消息
success=no
表明 AVC 为强制模式,SYSCALL 不成功,被拒绝了。反之如果为 success=yes
为宽容模式,成功,被放行。
exe="/usr/sbin/httpd"
启动该进程的可执行文件的完整路径
错误的文件类型是最常见的导致拒绝的原因,要想排错,先比较源上下文 scontext 与目标上下文 tcontext,进程应该允许访问客体吗?
sealert 消息
/var/log/messages
中的拒绝被分配了 ID,运行 sealert -l ID
即可查看完整消息。
这是 /var/log/messages
中的消息:
hostname setroubleshoot: SELinux is preventing httpd (httpd_t) "getattr" to /var/www/html/file1 (samba_share_t). For complete SELinux messages. run sealert -l 32eee32b-21ca-4846-a22f-0ba050206786
查看完整消息:
~]$ sealert -l 32eee32b-21ca-4846-a22f-0ba050206786
SELinux is preventing httpd from getattr access on the file /var/www/html/file1.
***** Plugin restorecon (92.2 confidence) suggests ************************
If you want to fix the label.
/var/www/html/file1 default label should be httpd_sys_content_t.
Then you can run restorecon.
Do
# /sbin/restorecon -v /var/www/html/file1
***** Plugin public_content (7.83 confidence) suggests ********************
If you want to treat file1 as public content
Then you need to change the label on file1 to public_content_t or public_content_rw_t.
Do
# semanage fcontext -a -t public_content_t '/var/www/html/file1'
# restorecon -v '/var/www/html/file1'
***** Plugin catchall (1.41 confidence) suggests **************************
If you believe that httpd should be allowed getattr access on the file1 file by default.
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
allow this access for now by executing:
# ausearch -c 'httpd' --raw | audit2allow -M my-httpd
# semodule -i my-httpd.pp
Additional Information:
Source Context system_u:system_r:httpd_t:s0
Target Context unconfined_u:object_r:samba_share_t:s0
Target Objects /var/www/html/file1 [ file ]
Source httpd
Source Path httpd
Port <Unknown>
Host hostname.redhat.com
Source RPM Packages
Target RPM Packages
Policy RPM selinux-policy-3.13.1-166.el7.noarch
Selinux Enabled True
Policy Type targeted
Enforcing Mode Enforcing
Host Name hostname.redhat.com
Platform Linux hostname.redhat.com
3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57
EDT 2017 x86_64 x86_64
Alert Count 2
First Seen 2017-07-20 02:52:11 EDT
Last Seen 2017-07-20 02:52:11 EDT
Local ID 32eee32b-21ca-4846-a22f-0ba050206786
Raw Audit Messages
type=AVC msg=audit(1500533531.140:295): avc: denied { getattr } for pid=24934 comm="httpd" path="/var/www/html/file1" dev="vda1" ino=31457414 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
Hash: httpd,httpd_t,samba_share_t,file,getattr
audit2allow
允许访问
audit2allow
工具会从日志中搜集拒绝的信息,然后 生成 SL 策略的放行规则。
在分析完拒绝消息之后,如果修改标签和布尔值都不能实现放行,可以使用 audit2allow
来生成一个本地策略模块,它可以生成类型强制规则,用来放行之前的拒绝。
audit2allow -w -a
会读取 /var/log/audit/audit.log
,然后把所有的拒绝消息生成人类易读的描述:
~]# audit2allow -w -a
type=AVC msg=audit(1226270358.848:238): avc: denied { write } for pid=13349 comm="certwatch" name="cache" dev=dm-0 ino=218171 scontext=system_u:system_r:certwatch_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=dir
Was caused by:
Missing type enforcement (TE) allow rule.
You can use audit2allow to generate a loadable module to allow this access.
......
-a
参数用来读取所有的审核日志,-w
参数用来生成人类易读的描述。
audit2allow -a
查看放行的类型强制规则:
~]# audit2allow -a
#============= gssproxy_t ==============
#!!!! This avc is allowed in the current policy
allow gssproxy_t fs_t:filesystem getattr;
#============= httpd_t ==============
#!!!! The file '/var/www/html/testfile' is mislabeled on your system.
#!!!! Fix with $ restorecon -R -v /var/www/html/testfile
allow httpd_t samba_share_t:file getattr;
......
要想使用上面命令所显示的规则,可以运行 audit2allow -a -M TEfilename
创建自定义模块。-M
参数用来在当前工作目录创建类型强制文件 *.te
,后面跟的就是文件名。
用 grep 把拒绝消息中需要的部分筛选出来,确认没有问题,再用管道交给 audit2allow
来处理。
~]# grep certwatch /var/log/audit/audit.log | audit2allow -R -M mycertwatch2
******************** IMPORTANT ***********************
To make this policy package active, execute:
semodule -i mycertwatch2.pp
运行完毕之后,会生成两个文件:*.te
和 *.pp
,前者为类型强制文件,后者为前者被编译后生成的策略包文件(模块)。
semodule -i mycertwatch.pp
安装生成的策略包