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,只需在上下文中增加一个字段即可。

image-center

上图显示的是一个文件服务器中,不同文件的安全级别。安全级别是严格地按层次划分的,并形成一个 BLP 模型,进程只能对同级别的文件进行读取;对更高级别的文件,它只能写入;对更低级别的,它只能读取。即 向下不能写,向上不能读。确保了进程可以向上复制文件,但之后不可从上面读取,除非上到那一级。进程也无法向低级别写入文件,以避免机密信息向下泄漏。

MLS 和 MCS 的特别之处:

  • 可以为进程和客体分配一个 最低级别 - 最高级别 的范围
  • 安全级别可以做的更复杂,即 分级的敏感度 + 不分级的类别,其中类别可省略
  • 允许进程访问客体由应用于安全级别的 “显性” 规则管理
  • 信任的进程可被授予特权,允许它们绕过 BLP 规则做策略允许的任何操作
  • 有些客体不支持单独的读/写功能,因为它们需要在网络等情况下读取/响应

目前,MLS/MCS 服务更多地用于维护应用程序的隔离,如:

  • 虚拟机使用 MCS,可以允许每个虚拟机都在自己的域中运行,让它们彼此隔离
  • 安卓设备使用动态生成的 MCS,这样,代表一个用户运行的应用无法读写另一个用户在同一个应用创建的文件。
MLS/MCS 安全范围的表示方法

敏感度是分级的,类别是不分级的。不连续的类别之间可以用逗号分隔,连续的类别可以用句点分隔。

单个级别的表示

每个级别可以用 敏感度:类别 格式来描述,如 s0:c0。其中 “类别” 是可选的,如果 没有类别,可以单独用 敏感度 来表示某个级别,如 s0

级别范围

级别范围是用一对级别来描述的,如果是 多级,格式为 最低级-最高级,如 s0:c1,c5-s5:c1,c5s0: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 顶层核心要素:

SELinux 顶层核心要素
  • 主体:Subject,对客体实施操作,如读取文件
  • 客体管理器:Object Manager,根据规则 强制实施 对客体的具体操作。他们可以存在于内核空间或用户空间,决定于软件是 Linux 内置的还是第三方的。
  • 安全服务器: Security Server,它基于规则,对主体是否有权对客体操作 做出决策。嵌于内核中。
  • 安全策略:Security Policy,使用策略语言来描述 具体的规则
  • 访问向量缓存:Access Vector Cache,AVC,缓存 所有的安全服务器做出的 决策,以此提升对请求的响应速度。

🍬 SELinux 顶层架构:

image-center

从下往上分析:

  • 安全服务器 内嵌于内核中,策略会从用户空间借助一系列函数被加载。

    客体管理器访问向量缓存 可以存在于:

    • 内核空间:存在于内核空间的客体管理器用于控制内核的服务,如文件、目录、套接字、进程间通信等,是由一些勾子提供的,这些勾子通过 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 决策流程

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 类型 来制定的,因此上下文经常简化成只有类型。

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 级别

级别是 MLSMCS 的属性之一。

该内容为可选,SELinux 支持 MLS 及 MCS,如果使用其中的一种,则在上下文中第四个位置增加 级别级别范围

CentOS 中,targeted 策略强制实施的是 MCS,其中只有一级敏感度 S0,支持 1024 个不同类别(c0.c1023),因此所有类别可以表示为 s0-s0:c0.c1023

13.3.2 域的转换

对于进程来说,SL 类型就是域

域的转换是指:进程在另一个域中启动一个新进程

要想发生域的转换,策略必须满足:

  1. 源域有权限转换到目标域
  2. 程序的二进制文件在源域中有执行权限
  3. 程序的二进制文件有到目标域的入口点

通过执行具有新域的入口点类型的应用程序,一个域中的进程可以转换到另一个域。SELinux 策略中会使用 入口点权限,该权限 可控制哪些程序可用来进入一个域

例如:

  1. 一位用户想修改自己的密码,他运行 passwd 程序,/usr/bin/passwd 可执行文件标签中的类型为 passwd_exec_tpasswd 程序会访问 /etc/shadow,其类型为 shadow_t
  2. 有一条 SELinux 策略规则写道:运行于 passwd_t 域的进程可以读写 shadow_t 类型的文件。而 shadow_t 类型只会应用于密码修改时需要访问的文件上,包括 /etc/gshadow/etc/shadow,及它们的备份文件。
  3. 有一条 SELinux 策略规则写道:passwd_exec_t 域有到 passwd_t 哉的入口点权限
  4. 用户执行 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_tshadow_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 受限进程

几乎所有的 在网络上倾听的服务,如 sshdhttpd是受限的。同时,大部分作为超级用户运行,为用户执行任务的进程,如 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 规则。

范例:非受限进程

  1. 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
    
  2. 暂停 httpd

     ~]# systemctl stop httpd.service
    
  3. 修改 httpd 程序的类型为不会产生域转换的类型

    httpd 的初始类型为 httpd_exec_t,修改为 bin_t

     ~]# chcon -t bin_t /usr/sbin/httpd
    
  4. 确认修改成功:

     ~]# ls -Z /usr/sbin/httpd
     -rwxr-xr-x. root root system_u:object_r:bin_t:s0       /usr/sbin/httpd
    
  5. 启动 httpd 服务

     ~]# systemctl start httpd.service
    
  6. 查看 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
    
  7. 以普通用户身份在家目录下载测试文件:

    在范例二中下载失败的情况,此时消失了,普通用户可以顺利下载。

     ~]$ 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 就会失败。

  8. 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
    
  9. 确认修改成功后,重启 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 susudo
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_tguest_txguest_t 域中的 L 用户只能运行 SUID 程序,如 passwd。这些用户无法运行 susudo,因此无法借助它们变身为超级用户。
  • sysadm_tstaff_tuser_txguest_t 域中的 L 用户可以用 X Window 登陆,也可以从终端登陆
  • 默认情况下,guest_txguest_t 域中的 L 用户无法在家目录或 tmp 目录执行程序,即使他们对目录有写权限。可以避免恶意程序修改用户的文件。
  • 默认情况下,staff_tuser_t 域中的 L 用户可以在其家目录及 /tmp 目录执行程序
  • xguest_t 域中的 L 用户唯一拥有的网络访问权限是用 Firefox 连接到网页

注意,对于系统进程和客体来说,system_u 是一个特殊的用户标识,它永远不可以与 L 用户关联。同样的,unconfined_uroot 是非受限用户

特殊 SL 角色

以下举例说明一些特殊的 SL 角色,这些角色决定了用户能做什么:

  • webadm_r:只能管理与 Apache HTTP Server 相关的 SL 类型
  • dbadm_r:只能管理与 MariaDB 数据库、PostgreSQL 数据库相关的 SL 类型
  • logadm_r:只能管理与 syslogauditlog 相关的 SL 类型
  • secadm_r:只能管理 SELinux
  • auditadm_r:只能管理与 audit 子系统相关的进程

seinfo -r 查看所有角色。

sudo 转换和 SL 角色

某些时候,受限用户可能会需要完成一个需要超级用户权限的管理任务,他必须用 sudo 命令来获得一个受限的管理员角色。当受限用户用 sudo 执行命令时,他们的角色会发生改变,空间如何改变则取决于 /etc/sudoers 中的规则或 /etc/sudoers.d/ 目录中的某个文件。

范例:为现有 SL 用户配置受限的管理员角色
  1. 创建一个新的 SL 用户,并指定其默认角色及附加的受限管理员角色

     ~]# semanage user -a -r s0-s0:c0.c1023 -R "staff_r webadm_r" neo_u
     #                                         默认角色| 附加角色 | 新 SL 用户名
    
  2. 新建用户的上下文直接参照原 SL 用户 staff_u 的上下文,所以直接复制其上下文文件。

     ~]# cp /etc/selinux/targeted/contexts/users/staff_u /etc/selinux/targeted/contexts/users/neo_u
    
  3. 把新建 SL 用户映射给现有 L 用户 neo

     semanage login -a -s neo_u -rs0:c0.c1023 neo
    
  4. /etc/sudoers.d/ 目录中创建一个与 neo 同名的配置文件:

     ~]# echo "linux_user ALL=(ALL) TYPE=webadm_t ROLE=webadm_r /bin/sh " > /etc/sudoers.d/neo
    
  5. rstorecon 修复 L 用户家目录

     ~]# restorecon -FR -v /home/neo
    
  6. 重新登陆 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 程序
  • 运行 susudo 命令

例如,由 SL 用户 user_u 启动的进程运行于 user_t 域中,这类进程可以连网,但无法使用 susudo 命令,这有助于保护系统免于用户的恶意操作(被劫持)带来的危险。

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 命令随时在线切换,但该命令造成的变化,在重启后会自己消失。

SELinux 工作模式
常用命令

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_contextssemanage 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_tdefault_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 squidsmbd_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 窗口系统,并且安装了 setroubleshootsetroubleshoot-server 安装包,而且 dbusauditd 服务都在运行。当发生拒绝时,系统会弹出一个信息框:

image-center

点击 Show 会显示详细信息:

image-center

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 条目表明源进程在 尝试读取目标文件的状态信息,该行为发生于读取文件之前,由于文件的标签错误导致该行为被拒绝。常见的权限包括 getattrreadwrite

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 安装生成的策略包