Linux 权限管理与访问控制详解(2)——MAC 和 SELinux

本系列 前一篇 主要介绍了 Linux 权限管理和访问控制的相关概念与 DAC 相关的 UGO+RWX、ACL,本篇续写 MAC 相关概念与知识,主要详细介绍 SELinux。

1. 强制访问控制(SELinux)

前半部分讲解了Linux系统中实现的DAC(Discretionary Access Control,自主访问控制)机制,主要包括传统的UGO+RWX机制和 ACL 机制;下半部分开始讲解MAC(Mandatory Access Control,强制访问控制),该部分主要讲 SELinux,因为 Linux 系统中的MAC机制,主要由 SELinux 来实现。

2. SELinux简介


2.1 起源

NSA(美国国家安全局)一直非常关注计算机操作系统的安全领域,他们发现大部分操作系统的安全机制,包括Window和大部分*nix系统,都是以DAC机制为安全认证基础的。由于DAC机制的设计很不利于系统安全,NSA便一直致力于开发一套更安全的MAC操作系统安全认证机制。

DAC与MAC两种安全机制的原理与区别,在前文已叙述过,DAC的控制方式够灵活,比较松,但不严格,有一定的安全隐患。在DAC的控制机制中,传统Linux由于 root 权限的“权力”过大而造成巨大的安全威胁:一旦黑客入侵Linux操作系统并获得root权限,整个操作系统将暴露于恶意攻击的威胁之下。

SELinux 正是为解决这类为题而设计,它控制了无限的root权限,并不采用大众所知道的传统安全机制。在SELinux下,root账号采用强制访问控制机制,同时限制用户和程序(主体)使用最低权限做足以完成任务的工作。因此,即使系统不幸遭受黑客攻击,由此引起的危害也随之降到最低限度,所以极大地提升了Linux系统的安全性。

2.2 SELinux 工作机制概述

在SELinux中,每个对象(程序、文件和进程等,包括前文所述的“主体”与“客体”)都有一个 安全上下文(Security Context),它依附于每个对象身上,记载着该对象具有的权限(SELinux定义的权限)。管理员可以通过定制 安全策略(Security Policy)来定义这些安全上下文,从而定义哪种对象具有什么权限。当一个对象需要执行某个操作时,系统会按照该对象以及该对象要操作的对象的安全上下文所定制的安全策略来检查相对应的权限,去过全部权限都符合,系统就会允许该操作,否则将阻断这个操作。这些过程不会影响到其他正常运行的对象,系统会保证它们的安全系统结构以及稳定运行。

SELinux 从Linux Kernel 2.6开始,就已经是内核的一部分了;传统的 UGO+RWX 机制也是运行在内核中;ACL 是一个POSIX标准。

在启用了SELinux的Linux操作系统中,某个对象需要执行某个操作时,系统权限管理不仅根据安全上下文所规定的内容来检查,同时还要根据传统DAC机制来检测,并且是先通过DAC机制的检测,再由SELinux定制的安全策略来检测。只有通过DAC和SELinux的双重检测之后,才能执行操作。

SELinux 的另一个重要概念是 TE(Type Enforcement,类型强制),其原理是将权限与程序的访问结合在一起,而不是结合用户。本文讨论的所有SELinux策略特性,都是处理主体(运行中的进程)对客体(文件、目录或套接字等)的访问权的,主要集中于程序访问控制决策,这也是SELinux的主要功能。它允许SELinux安全策略编写者基于程序的功能和安全属性,加上用户要完成任务所需的访问权作出访问决策,可以将程序限制到功能合适、权限最小化的程度。因此,即使它出现了故障或被攻击,但整个系统的安全并不会受到威胁。例如,一个Web服务器的策略阻止修改它显示的文件,那么即使Web服务器被攻破,TE策略也能阻止被攻破的服务器修改那些文件。这样就消除了通过Web服务器的漏洞攻击造成对整个服务器的威胁,而只有被攻破的应用程序受到影响,并且它会被我们的安全策略限制访问权限。

具体的系统运行中,在 SELinux 系统启动时,会加载一个叫做policy.*的安全策略配置文件,这个文件中就定义了SELinux设定的各种权限。如果用户在文件中设定了SELinux不能在开机后转回 permissive模式的话,那么系统的root用户则可能无法修改当前的设定,也就是说root用户在SELinux中已经不具有默认的所有权限。因此,即便黑客盗取了root用户密码并成功入侵到用户的计算机,也只能在他入侵的这个自治域内进行破坏,并不会像以前那样扩散到整个Linux系统。因此,在启用了SELinux的Linux操作系统中,root用户也被限制进行某些操作。

2.3 SELinux 的优势

总的来说,SELinux有以下几点优势:

  • 所有的进程与文件都用一个类型(Type)来标记。一个类型定义了一个进程的域(Domain)和一个文件的域。不同的进程只在自己所属的域内运行,SELinux的策略则定义了不同进程与文件、进程与进程间通信的方式。只有SELinux的策略允许,一个访问操作才可能被执行;
  • 细粒度访问控制。优于传统的基于用户和组的Linux自主访问控制机制,SELinux基于一切可用信息,比如SELinux定义的用户角色类型和一个可选的安全等级
  • SELinux策略是以管理方式定义的、全系统范围内有效的,不是用户自主可控的;
  • 降低提权攻击的风险(上面所讲述的SELinux的主要贡献就是这个);
  • SELinux 可以保证数据的机密性和完整性,并能防止部分外部恶意数据输入。

但是,需要注意以下几点:

  • SELinux 不是防病毒软件,也不能替代防病毒软件;
  • SELinux 不能替代基础密码、防火墙或者IDS/IPS或者其他安全防护系统;
  • SELinux 不是一体化的防护系统。

3. SELinux 基本工作原理


3.1 SELinux 中的上下文(Context)

SELinux 系统中的进程文件都标记了 SELinux 的上下文,这个上下文包含了许多有用的信息,包括SELinux用户(不同于Linux系统的用户)、角色(Role)、类型(Type)和级别(Security Level)等等。在运行 SELinux 的时候,这些上下文信息被用来辅助进行访问控制,它们可以看做是 SELinux 策略的“维度”。最新的 SELinux 综合提供了 RBACTE(类型增强)和 MLS(Multi-Security Level,多级别安全)三种访问控制机制。

下面是一个 SELinux 上下文的例子。SELinux 上下文广泛使用在 进程Linux用户文件中,使用 ls -Z [file|dir] 命令可以查看文件目录的 SELinux 上下文:

1
2
➜ ls -Z
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 test.txt

SELinux 上下文的组成为:

1
SELinux user:role:type:level


SELinux user

SELinux user 标志一群被授权的角色或者一个特定的 MLS 范围。每一个 Linux 系统用户都通过 SELinux 机制被映射为一个 SELinux user,这使得Linux用户可以继承 SELinux 用户的访问权限。这个标志主要用于限制 Linux 用户可以进入的角色和级别范围,相当于 SELinux 对 Linux 的用户结构进行了一层封装。

Linux 下用 root 用户权限运行 semanage login -l 命令可以查看 SELinux 用户与 Linux 用户之间的映射关系:

1
2
3
4
5
6
7
➜ semanage login -l
登录名 SELinux 用户 MLS/MCS 范围 服务
__default__ unconfined_u s0-s0:c0.c1023 *
root unconfined_u s0-s0:c0.c1023 *
system_u system_u s0-s0:c0.c1023 *

  • 例子中第一列为 Linux 系统用户
  • 第二列的 SELinux 用户列出了第一列系统用户对应的 SELinux 用户。对于进程来说,这些 SELinux 用户直接限制了什么角色和级别可以为该用户访问;
  • 第三列的 MLS/MCS 范围 则给出了 MLSMCS(Multi-Category Security,多种类安全)机制所采用的 安全级别(Level)。关于 MCSLevel,下文详解;
  • 最后一列的服务,定义了对应的Linux系统用户登录后对应的 SELinux 上下文,默认值 *代表任意服务。

角色(Role)

SELinux 中有一部分采用前文介绍的基于角色的访问控制(RBAC)机制。而 role 是 RBAC 机制中的一个属性,也是 RBAC 机制在 SELinux 中的运用。在 SELinux 的设计中,Linux 系统用户被映射为 SELinux 用户,而 SELinux用户被授权为角色(Role),继而角色被授权为对应的可访问的(Domain)。所以,角色作为域和 SELinux 用户之间联系的媒介。通过角色可以决定 SELinux 用户可以访问哪些域,而最终决定了SELinux 用户可以访问哪些对象类型。通过这种机制可以降低权限提升的风险。


类型(Type)

类型是类型强制(Type Enforcement)机制的一个属性,也是 TE 机制在 SELinux 中的实现。类型进程定义了,为文件定义了类型。SELinux 机制策略明确定义了类型间相互访问域访问类型域间相互访问的规则和许可。只有存在某条 SELinux 机制规则允许的情况下,才允许上述的访问发生。


级别(Level)

级别是上述 MLS 和 MCS 机制的另一个重要属性。一个 MLS 范围是一个 级别对,采用区间标志,比如 (最低级别, 最高级别) 或者 (S0, s5) 。每个级别都是一个种类(Category) 敏感的数对,然而种类是可选的。如果存在种类,则可以表示为 sensitivity: category-set 的形式,如果没有种类,则只用 sesitivity 表示即可。

如果种类集(category-set)是连续的,则在表示时可以简写,比如 c0.c2c0,c1,c2 表示同样的含义。举例说明:在最新的 CentOS7 操作系统中,目标机制(Targeted Policy)对MCS进行了增强,因此它只有一个敏感级别 s0 。MCS 支持 1024 个不同的种类,从 c0 一直到 c1023 ,所以,s0-s0:c0.c1023 所有的种类都被授权。

另外,与级别相关的 /etc/selinux/targeted/setrans.conf 配置文件非常重要,切记用 Vi(m)/Gedit/Nano/Emacs 等编辑器对其直接进行编辑,可以使用 semanage 命令进行修改,这样才能保证修改的正确性。


类型强制(Type Enforcement)

SELinux 策略大部分都是一套声明和规则一起定义的类型强制(TE)策略,一个定义良好、严格的 TE 策略可能包括上千条 TE 规则,TE 规则数量的巨大是正常现象,因为它们表达了由内核暴露出的允许对资源的访问权,这就意味着每个进程对每个资源的访问尝试都必须至少要得到一条 TE 规则的允许,考虑一下现代Linux操作系统中进程和资源的数量,就会明白为什么有这么多 TE 规则了。
TE 规则数量众多,但规则本身并不复杂,分类也较少,所有的规则基本上都属于两类范畴:访问向量(AV,Access Vector)和类型规则。AV规则允许或审核两个类型之间的访问权,而某些情况下使用类型规则控制默认的标记。

由于 TE 规则数量较多,全部加载比较耗费资源,每次检索也会比较费性能,所以,在 SELinux 运行的过程中,系统实现了一个 访问向量缓存(Access Vector Cache),用来存放已经查询过的规则,提高性能。

正如类型强制的名字所示,TE 规则通过安全上下文与所有资源结合起来对类型起作用,策略语言包括了另外的允许我们定义类型及其策略组件的语句。SELinux 不会管Linux系统用户,可以给同一个程序指定多个域类型(因此有不同的特权集),这样就允许引入角色的概念。因此,访问控制的标准仍然是基于程序的域类型而不是基于用户的域类型。

3.2 域转换(Domain Transitions)

前文介绍过,SELinux 一大特点就是将进程和用户的执行权限限定在一个(Domain)内。因此,即使 root 用户也不可能具有太大权限,从而保证了系统整体的安全性。SELinux 中定义的 会限定进程的执行权限或范围,但进程执行的时候是可以从一个域转换到另外一个域的,以获得另外一个域内限定的权限。进程从一个域转换到另一个域需要执行一个具有新域的入口点(Entrypoint)权限的应用程序来实现。这个“入口点”许可在 SELinux 机制中使用,它用来控制某些应用程序可以用来进入一个。为了清楚说明这个问题,下面举实例说明。

一个用户想要修改自己的密码。为了修改密码,应该运行 passwd 程序,/usr/bin/passwd 可执行命令的标记 passwd_exec_t 类型,如下:

1
2
➜ ls -Z /usr/bin/passwd
-rwsr-xr-x. root root system_u:object_r:passwd_exec_t:s0 /usr/bin/passwd

在实际执行过程中,该命令访问了 /etc/shadow 文件,该文件的类型为 shodow_t,如下:

1
2
➜ ls -Z /etc/shadow
----------. root root system_u:object_r:shadow_t:s0 /etc/shadow

SELinux 机制的相关规则规定:运行在 passwd_t (,是 TE 机制为进程定义的权限范围;passwd_t域,即是/usr/bin/passwd程序运行时的进程所处的)的进程对标记为 shadow_t 类型类型,是 TE 机制为文件定义的权限范围)的文件具有读和写的权限。并且, shadow_t 类型仅仅只赋予和密码修改相关的那些文件,这些文件包括 /etc/gshadow/etc/shadow 以及它们的备份文件。根据这个规则,用户可以知道:passwd_t 域的进程具有 passwd_exec_t 类型的 入口点 权限。因此,当用户运行 /usr/bin/passwd 程序修改密码时,该程序启动的进程运行在 passwd_t 域;由于 passwd_t 域的进程可以对 shadow_t 类型的文件进行读写操作,所以 passwd 命令的进程可以操作 /etc/shadow 文件。

当然,在 SELinux 机制中,如果默认情况下(没有相应规则),该进程是无法访问相应文件的。可以进一步解释这个例子:未使用 SELinux 的 Linux 操作系统中,/usr/binpasswd 程序是可信的,因而可以修改经过加密的密码文件 /etc/shadow。所以,passwd 程序执行它自己内部的安全策略,允许普通用户修改自己的密码,同时也允许 root 修改所有的密码。为了执行这个密码修改操作,passwd程序需要有移动和重新创建shadow文件的能力。在标准 Linux 系统中,它具有这个特权,因为 /usr/bin/passwd 在执行时被加上了 setuid 位,它作为 root 用户被允许对密码进行修改操作。然而,许多程序都可以作为 root 允许(实际上,所有程序都有可能作为 root 允许)。这就意味着任何程序(当以 root 身份运行时)都有可能修改 /etc/shadow 文件。因此,类型强制使用户能做的事情是确保只有 /usr/bin/passwd 程序(或类似受信程序)可以操作 /etc/shadow 文件,而不论运行程序的用户是谁。

在上述例子中,除 SELinux 的相关规则约定外,TE 机制在很大程度上保证下面几条前提条件:

  • 只有标记为 passwd_exec_t 类型的应用程序执行才能进入 passwd_t 域,其他的命令执行时都不允许进入该域;
  • 只有一些授权的域,比如 passwd_t 才能对 shadow_t 类型的文件具有读写权限。及时其他的进程具有超级用户的权限,也不允许对 shadow_t 类型的文件具有写权限,因为它们并不运行在 passwd_t 域中;
  • 只有一些授权的域才能转换到 passwd_t 域,比如 sendmail 进程运行在 sendmail_t 域中,在该域中它们没有合理的理由和权限执行 passwd 命令,因此就不授予转换到 passwd_t 域的入口点
  • 运行在 passwd_t 域中的进程只对授权的类型具有读写权限,例如标记为 shadow_tetc_t 类型的文件。这就有效阻止了 passwd 命令对其他文件的任意读写权限,从而保证了最小权限运行,最终保证了系统安全。

3.3 SELinux 中进程上下文

前文所述的上下文是以文件上下文来说的,其实,SELinux机制的实现中,还有进程上下文用户上下文,下面分别介绍一下。

前文讲过用命令 ls -Z [file|dir]可以查看文件上下文,类似地,用命令 ps -eZ 就可以查看进程上下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜ ps -eZ | more
LABEL PID TTY TIME CMD
system_u:system_r:init_t:s0 1 ? 00:00:22 systemd
system_u:system_r:kernel_t:s0 2 ? 00:00:00 kthreadd
system_u:system_r:kernel_t:s0 3 ? 00:00:00 ksoftirqd/0
system_u:system_r:kernel_t:s0 5 ? 00:00:00 kworker/0:0H
system_u:system_r:kernel_t:s0 7 ? 00:00:00 migration/0
system_u:system_r:kernel_t:s0 8 ? 00:00:00 rcu_bh
system_u:system_r:kernel_t:s0 9 ? 00:00:00 rcuob/0
system_u:system_r:kernel_t:s0 10 ? 00:00:02 rcu_sched
system_u:system_r:kernel_t:s0 11 ? 00:00:05 rcuos/0
system_u:system_r:kernel_t:s0 12 ? 00:00:01 watchdog/0
system_u:system_r:kernel_t:s0 13 ? 00:00:00 khelper
system_u:system_r:kernel_t:s0 14 ? 00:00:00 kdevtmpfs
system_u:system_r:kernel_t:s0 15 ? 00:00:00 netns
system_u:system_r:kernel_t:s0 16 ? 00:00:00 writeback
system_u:system_r:kernel_t:s0 17 ? 00:00:00 kintegrityd
system_u:system_r:kernel_t:s0 18 ? 00:00:00 bioset
system_u:system_r:kernel_t:s0 19 ? 00:00:00 kblockd
system_u:system_r:kernel_t:s0 20 ? 00:00:00 khubd
--More--

3.4 SELinux 中用户上下文

类似地,id -Z 命令可以查看Linux系统用户相关的SELinux上下文信息:

1
2
➜ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

4. 使用 SELinux 前的准备工作


4.1 SELinux 相关的工具

前文已经使用了 semanage 命令来查看 Linux 系统用户与 SELinux User 之间的映射关系,其实,与 SELinux 相关的工具包与命令工具有好多。安装系统时默认也安装了一些基础的命令工具,但CentOS7 系统如果以最小化的文字模式安装,policycoreutils-python 包和 policycoreutils-gui 包是不会被安装的,里面附带的一些工具也不可用,或者下面列表中其他默认不被安装的工具包,都需要用 yum install [package_name] 来手动安装。下面是常用的工具包及其里面的工具简介:

  • policycoreutils:提供与 SELinux 相关的命令,比如 restorecon, secon, setfiles, semodule , load_policysetsebool 来操作和管理 SELinux。
  • policycoreutils-gui:提供图形化软件 system-config-selinux 来管理 SELinux。
  • policycoreutils-python:提供命令比如 semanage, audit2allow, audit2why, 和
    chcat 来管理和操作 SELinux。
  • selinux-policy:提供 SELinux 引用策略,该引用策略包括了所有的 SELinux 策略,并被用作其他策略(如 Targeted Policy)的基础使用。
  • selinux-policy-targeted:提供 SELinux 的 targeted 策略。
  • selinux-policy-mls:提供 SELinux 的 MLS 策略。
  • setroubleshoot-server:翻译 SELinux 的拒绝操作体质信息,为 sealert 工具提供可以查看的、可读性好的信息。
  • setools/setools-console/setools-gui:这些安装包提供了与 SELinux 相关的策略分析、检索、日志审计与监控、文件上下文管理管理的相关工具。setools元工具setools-gui提供了 apol, seaudit 工具;setool-console 则提供了 sechecker,
    sediff, seinfo, sesearch, 和 findcon 等命令行工具。
  • mcstrans:提供对 SELinux 上文中的级别(比如 s0-s0:c0.c1023)翻译的工具。
  • libselinux:为 SELinux 的应用提供 API 支持。
  • libselinux-python:为 SELinux 应用提供 Python 绑定接口。
  • libselinux-utils:提供 avcstat, getenforce, getsebool, matchpathcon,
    selinuxconlist, selinuxdefcon, selinuxenabled, 和 setenforce 工具。

上述工具的使用请自行查阅相关资料。

4.2 SELinux 日志

SELinux 有不止一种日志文件可记录在运行过程中对操作的拒绝日志,以便管理员后续审计与分析。默认情况下,CentOS7 安装了 dbusaudit 服务,另外一个有用的 SELinux 日志相关的工具包 setroubleshoot-server 可用命令 yum install setroubleshoot 来安装。

如果 audit 守护进程在运行,SELinux 的拒绝操作日志便会记录在 /var/log/audit/audiut.log 中,内容如下所示:

1
2
3
type=AVC msg=audit(1419246735.232:99): avc: denied { getattr } for pid=3524 comm="httpd" path="/var/www/html/info.php" dev="dm-1" ino=429843 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
type=AVC msg=audit(1419246735.232:100): avc: denied { getattr } for pid=3524 comm="httpd" path="/var/www/html/info.php" dev="dm-1" ino=429843 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
type=AVC msg=audit(1419246736.440:101): avc: denied { unlink } for pid=11537 comm="setroubleshootd" name="setroubleshoot_server" dev="tmpfs" ino=30142 scontext=system_u:system_r:setroubleshootd_t:s0-s0:c0.c1023 tcontext=unconfined_u:object_r:var_run_t:s0 tclass=sock_file

如果 setroubleshooted 进程在运行的话,上面的记录将会被翻译成可读性好的形式保存到 /var/log/messages 中:

1
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

当然,不同形式的拒绝操作信息被保存到不同的文件中,是根据不同的守护进程耳钉的,下表列出了对应不同的守护进程的日志文件路径:

守护进程 日志文件
audit [on] /var/log/audit/audit.log
audit [off]/rsyslogd [on] /var/log/messages
setroubleshooted/rsyslogd/audit [on] /var/log/audit/audit.log 翻译后存入 /var/log/messages

至于上述 3 个进程的启动、随系统启动自动启动的设置,请查看 systemd 相关的 systemctl 命令用法。

5. SELinux 目标策略


前文讲了 SELinux 基本的工作原理,主要是对 Linux 系统用户、文件以及进程“标记”上 SELinux 定义的上下文,然后根据 SELinux 策略对不同的上下文元素进行分域、分等级、分角色和分类控制。

接下来的内容是 SELinux 的第二部分核心内容:策略 。SELinux 的策略以配置文件形式存在,并且有不同的策略用于不同的功能。这一部分就以最常用的目标策略(Targeted Policy)为例开始讲解 SELinux 中关于 策略 的方方面面。

5.1 Targeted Policy 原理

Targeted Policy 是从 strict 示例策略衍生而来的,它们结构与组织几乎相同。不同的是 strict 策略更趋向于最大化使用 SELinux 所有特性,为大部分程序提供强大的安全保护;而 Targeted Policy 的目标是隔离高风险程序。Targeted Policy 的好处是,一方面可以向 Linux 系统添加大量的安全保护,同时又尽量少影响现有的用户程序。Targeted Policy 的策略主要集中于面向网络的服务(即那些暴露在外容易遭受黑客攻击的组件与服务),Targeted Policy 是 RHEL/Fedora/CentOS 系统上的标准策略,它在增强安全性和减少对现有应用程序影响之间达到了一个很好地平衡。

CentOS7 系统中,Targeted Policy 源文件可以在 /etc/selinux/targeted/src/policy/ 目录下找到。

Targeted Policy 中使用了无限制的域类型 unconfined_t,并移除了 strict 策略中的 sysadmin_tuser_t 域类型,这也意味着基本的角色结构被移除,所有的用户都以角色 system_r 来运行,几乎所有用户运行的程序都以 unconfined_t 域类型执行。不过,无限制域和限制域都需要接受可执行和可写的内存检查。默认情况下,运行在无限制域下的主体不能分配可写和可执行的内存,这个机制降低了系统遭受缓冲区溢出攻击的风险。这些内存检查可以通过设置SELinux Boolean 变量来关掉,下文详述。

5.2 限制进程

几乎所有的网络服务进程都在限制下运行;多数以 root 身份运行的系统进程(比如 passwd 进程)都是受限的。当进程受限时,它只能在自己被限制的内运行,只能操作该内的资源(文件、服务等),对其他的资源则无权操作。比如 Web 服务进程 httpd 只能运行在 httpd_t 域内。如果一个受限制的进程被黑客攻击并控制,根据 SELinux 策略的配置,该黑客仅仅能访问这个受限制的域,因此攻击所带来的危害也比传统的 Linux 小很多。

以下通过一个具体的例子来演示 SELinux 如何将进程限制在自己的域内运行,本例以Apache服务器的进程 httpd 为例,介绍 SELinux 如何阻止 httpd 进程去访问由其他域管理的文件类型的。

  1. 运行 sestatus 命令来确认 CentOS7 中 SELinux 试运行的,它运行在 enforcing 模式下(SELinux 有3种运行模式,下文详述),且采用了 Targeted Policy:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ➜ 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
  2. root 权限在 httpd 的工作目录下创建一个新的文件 info.php(显示 PHPINFO 的内容):

    1
    2
    3
    4
    ➜ cat /var/www/html/info.php
    <?php
    echo phpinfo();
    ?>
  3. 查看该文件的 SELinux 上下文信息:

    1
    2
    ➜ ls -Z /var/www/html/info.php
    -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/info.php

NOTE:
至此,可以看出,默认情况下,Linux用户是非限制的,因此刚创建的 info.php 文件的 SELinux 上下文中的用户标记为 unconfined_u 。然而RBAC访问控制机制是作用于进程的,不是用于文件,并且,角色对文件来说也没什么太大意义,因此上述结果中的object_u的角色也仅仅是一个用与文件的通用角色。在 /proc/ 目录下,与进程相关的文件可以采用 system_r 角色。另外,结果中的 httpd_sys_context_t 类型允许运行在 httpd_t 中的 httpd 进程访问该文件。

  1. 访问 info.php 文件,如下图所示:
    Alt text

可知该文件默认是可以被访问到的。

  1. 使用 chcon 命令对该文件的类型重新标识。下面,以 root 用户身份运行如下命令将上述 info.php 文件的类型改为可由运行在 samba_share_t 内的 Samba 进程访问的文件:
    1
    2
    3
    ➜ chcon -t samba_share_t info.php
    ➜ ls -Z info.php
    -rw-r--r--. root root unconfined_u:object_r:samba_share_t:s0 info.php

可以看到该文件的类型已被改为 samba_share_t,需要注意的是,这种方法的修改是临时的,若要永久修改文件的类型,需要用 semanage 命令,下文详述。

  1. 再次访问该 info.php 文件,就会发现被禁止访问了:
    Alt text

通过上述几个步骤的演示可知,虽然 Linux 系统基本的 DAC 机制允许 httpd 进程访问 info.php 文件,然而 SELinux 实现的 MAC机制却进制该访问操作。同时, SELinux 还对这些操作详情记录到日志中,以方便系统管理员事后审计与分析。SELinux 审计日志可查看 /var/log/audit/audit.log 文件:

1
2
3
➜ cat /var/log/audit/audit.log | grep info.php
type=AVC msg=audit(1419231479.505:13570): avc: denied { getattr } for pid=18874 comm="httpd" path="/var/www/html/info.php" dev="dm-1" ino=429843 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
type=AVC msg=audit(1419231479.505:13571): avc: denied { getattr } for pid=18874 comm="httpd" path="/var/www/html/info.php" dev="dm-1" ino=429843 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file

当然,由于该操作涉及 httpd 服务进程,Apache 也有自己的日志文件,因此,也可以查看 Apache 服务器的错误日志文件 /var/log/httpd/error_log :

1
2
➜ cat /var/log/httpd/error_log | grep denied
[Mon Dec 22 14:57:59.507103 2014] [core:error] [pid 18874] (13)Permission denied: [client 140.254.254.254:57804] AH00035: access to /info.php denied (filesystem path '/var/www/html/info.php') because search permissions are missing on a component of the path

5.3 非限制进程

非限制进程运行在非限制域中。比如,init 进程运行在非限制的 initrc_t 中,非限制的 Kernel 进程运行在 kernel_t 域中,非限制的用户运行在 unconfined_t 域中。对于非限制的进程, SELinux 的策略规则仍然适用,然而有关允许进程运行在非限制域的规则几乎允许所有的访问。此时,相当于 SELinux 不起作用,SELinux 架起来的防护只相当于“退化”到传统的 DAC机制。如果一个非限制进程被黑客控制,那么 SELinux 将无法阻止黑客进一步活动(当然 DAC 规则仍然适用, SELinux 只是在 DAC 层次上附加了一层更高强度的防护,而不是替代 DAC 机制)。
下面将给出一个具体的例子来演示 Apache HTTP 服务器的 httpd 进程在非限制条件下运行时,是如何访问本应由 Samba 服务器访问的数据的。(下面的例子需要安装 SELinux 相关的 setroubleshoot-serveraudit等工具包,同上,SELinux 也需要运行在 enforcing 模式下)。

  1. 通过 sestatus 命令检查 SELinux 的运行状态:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ➜ 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
  2. 确保上述的 info.php 文件 SELinux 上下文中的类型仍然是 samba_share_t

    1
    2
    ➜ ls -Z
    -rw-r--r--. root root unconfined_u:object_r:samba_share_t:s0 info.php
  3. httpd 进程原来运行在受限制httpd_t 内,其进程启动文件 /usr/sbin/httpd 的文件类型为 httpd_exec_t :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ➜ ls -Z /usr/sbin/httpd
    -rwxr-xr-x. root root system_u:object_r:httpd_exec_t:s0 /usr/sbin/httpd
    ➜ ps -eZ | grep httpd
    system_u:unconfined_r:httpd_t:s0 23284 ? 00:00:00 httpd
    system_u:unconfined_r:httpd_t:s0 23286 ? 00:00:00 httpd
    system_u:unconfined_r:httpd_t:s0 23287 ? 00:00:00 httpd
    system_u:unconfined_r:httpd_t:s0 23288 ? 00:00:00 httpd
    system_u:unconfined_r:httpd_t:s0 23289 ? 00:00:00 httpd
    system_u:unconfined_r:httpd_t:s0 23290 ? 00:00:00 httpd
  4. 在将 httpd 进程从 限制 改为 非限制 之前,需要先终止该进程:

    1
    ➜ systemctl stop httpd.service
  5. root 用户身份运行以下命令来改变 httpd 进程启动命令文件的 类型,以确保其启动之后不运行在原来的 httpd_t 内(将 /usr/sbin/httpd类型 改为 unconfined_exec_t ,启动后进程将运行在 unconfind_t 非限制域 内):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ➜ chcon -t unconfined_exec_t /usr/sbin/httpd
    ➜ ls -Z /usr/sbin/httpd
    -rwxr-xr-x. root root system_u:object_r:unconfined_exec_t:s0 /usr/sbin/httpd
    ➜ systemctl start httpd.service
    ➜ ps -eZ | grep httpd
    system_u:unconfined_r:unconfined_t:s0 23284 ? 00:00:00 httpd
    system_u:unconfined_r:unconfined_t:s0 23286 ? 00:00:00 httpd
    system_u:unconfined_r:unconfined_t:s0 23287 ? 00:00:00 httpd
    system_u:unconfined_r:unconfined_t:s0 23288 ? 00:00:00 httpd
    system_u:unconfined_r:unconfined_t:s0 23289 ? 00:00:00 httpd
    system_u:unconfined_r:unconfined_t:s0 23290 ? 00:00:00 httpd
  6. 此时,用运行在 非限制域 内的 httpd 进程访问本来只能由 Samba 进程访问的、类型为 samba_share_tinfo.php文件:
    Alt text
    该结果显示,虽然 httpd 进程原来没有访问标记为 samba_share_t 类型的文件的权限,然而由于 httpd 现在运行在非限制域 unconfined_t 中,所以 SELinux 的防护策略没起作用,相当于只有 Linux 系统的 DAC 机制起作用。

  7. 测试结束后,可以用 restorecon 命令来恢复文件的默认类型,下面依次恢复上述测试中的两个文件的默认类型,并重新启动相关进程:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    ➜ restorecon -v /var/www/html/info.php
    restorecon reset /var/www/html/info.php context unconfined_u:object_r:samba_share_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
    ➜ ls -Z /var/www/html/info.php
    -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/info.php
    ➜ systemctl stop httpd.service
    ➜ restorecon -v /usr/sbin/httpd
    restorecon reset /usr/sbin/httpd context system_u:object_r:unconfined_exec_t:s0->system_u:object_r:httpd_exec_t:s0
    ➜ systemctl start httpd.service
    ➜ ps -eZ | grep httpd
    system_u:system_r:httpd_t:s0 24479 ? 00:00:00 httpd
    system_u:system_r:httpd_t:s0 24481 ? 00:00:00 httpd
    system_u:system_r:httpd_t:s0 24482 ? 00:00:00 httpd
    system_u:system_r:httpd_t:s0 24483 ? 00:00:00 httpd
    system_u:system_r:httpd_t:s0 24484 ? 00:00:00 httpd
    system_u:system_r:httpd_t:s0 24485 ? 00:00:00 httpd

上述步骤演示了 SELinux 如果通过限制进程的运行来保证 Linux 系统安全的,也演示了如果将重要进程改为非限制进程的安全隐患,因此,将一个进程的运行状态由限制改为非限制的时候,应当十分慎重。

3.4.4 限制和非限制用户

前文介绍过,每个 Linux 系统用户在 SELinux 中都被映射为一个 SELinux User,这使得 Linux 用户能够继承在 SELinux 实现的 SELinux User 访问控制。用 semanage login -l 查看两种用户间的映射情况:

1
2
3
4
5
6
7
➜ semanage login -l
登录名 SELinux 用户 MLS/MCS 范围 服务
__default__ unconfined_u s0-s0:c0.c1023 *
root unconfined_u s0-s0:c0.c1023 *
system_u system_u s0-s0:c0.c1023 *

CentOS7 中,Linux 用户被默认映射到 SELinux 的 __default__ login 中,从而映射为 unconfined_u 类型。下面通过一个具体的在 CentOS7 中添加新用户的例子,来掩饰 SELinux 是如何映射 Linux 系统用户的。

  1. root 用户身份,运行 useradd 命令新增用户 jiayu ,并用passwd 命令给新用户设置密码:

    1
    2
    ➜ useradd jiayu
    ➜ passwd jiayu
  2. su jiayu 命令切换到用户 jiayu 登录 Shell,并用 id -Z 命令查看该用户的 SELinux 上下文:

    1
    2
    3
    ➜ su jiayu
    [jiayu@localhost ~]$ id -Z
    unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

由上面步骤可以看到,当 Linux 添加一个新用户时, SELinux 默认将该用户映射为 unconfined_u 类型,角色unconfined_runconfined_t

限制和非限制的用户都需要接受可执行和可写的内存检查,并接受 MCS(Multi-Category Security) 与 MLS(Multi-Level Security)机制的约束。如果一个非限制用户执行了一个从 unconfined_t域向一个允许的域转变的应用程序,非限制用户仍要接受那个转变到的域的限制。这个机制就保证了即使一个用户是非限制的,这个应用也是受限的,因此,软件的漏洞引起的风险仍能得到一定程度的控制。

3.4.5 策略目录介绍

SELinux 系统上安装的每个策略在 /etc/selinux/ 目录下都有他们自己的目录,子目录的名字对应于策略的名字(如本小节介绍的 Targeted Policy),在 SELinux 配置文件中就要使用这些子目录名字,高速内核在启动时载入什么策略。看下 CentOS7 上 /etc/selinux/ 目录中内容:

1
2
3
4
➜ ls -lZ /etc/selinux
-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 config
-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 semanage.conf
drwxr-xr-x. root root system_u:object_r:selinux_config_t:s0 targeted

上面结果显示,CentOS7 系统默认只安装了一个策略:targeted 。注意目录和策略子目录的类型都是 selinux_config_t

semodulesemanage 命令管理策略的诸多方面,前者管理可载入策略模块的安装、更新和删除,它对可载入策略包起作用,包括一个可载入策略模块和文件上下文消息;后者管理添加、修改和删除用户、角色文件上下文、MLS/MCS转换、端口标记和接口标记。

每个策略子目录包括的文件,以及文件如何标记必须遵守一个规范,这个规范被许多系统实用程序使用,帮助管理策略。通常,任何设计优良的策略源树都将正确安装策略文件,下面是 targeted 策略目录的列表输出,它就是一个典型:

1
2
3
4
5
6
7
8
➜ ls -lZ /etc/selinux/targeted
-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 booleans.subs_dist
drwxr-xr-x. root root system_u:object_r:default_context_t:s0 contexts
drwxr-xr-x. root root system_u:object_r:selinux_login_config_t:s0 logins
drwxr-xr-x. root root system_u:object_r:selinux_config_t:s0 modules
drwxr-xr-x. root root system_u:object_r:semanage_store_t:s0 policy
-rw-r--r--. root root system_u:object_r:selinux_config_t:s0 setrans.conf
-rw-r--r--. root root unconfined_u:object_r:selinux_config_t:s0 seusers

6. SELinux 配置文件及启用/禁用

6.1 配置文件

SELinux 的主配置文件是 /etc/selinux/config 文件,它控制系统下一次启动过程中载入哪个策略。以及 SELinux 以什么样的模式运行。下面显示了一个 config 文件的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜ cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of these two values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted

该配置文件主要控制两个方面的设置:SELinux 的 运行模式活动策略

  • SELinux 模式(由上面 SELINUX=enforcing 一行确定):可以被设置为 enforcing/permissive/disabled 共3种。

    • enforcing 模式,在该模式下,SELinux 策略背完政治性,这是 SELinux 主要工作模式要使 SELinux 发挥增强系统安全的作用,必须开启此模式;
    • permissive模式,该模式下,SELinux 的策略规则不会被强制执行,相反,只是审核并记录遭受拒绝的消息,除此之外,SELinux 对系统的安全性没有影响,此模式在调试 SELinux 策略时很有帮助;
    • disabled模式,内核中的 SELinux 机制完全关闭,只有系统启动时策略载入前系统才会处于 disabled 模式,该模式和 permissive 模式有所不同,permissive 模式有 SELinux 内合特征操作,但不会拒绝任何访问,只是进行审核、记录;在 disabled 模式下, SELinux 将不会有任何动作,只有在不希望使用 SELinux 时才使用该模式。

      NOTE:
      enforcing 模式和 permissive 模式或者 disabled 模式之间切换要小心,当返回 enforcing 模式时,通常会导致文件标记不一致。SELinux 配置文件中的模式设置由 init 进程使用,在它载入初始策略前配置 SELinux 使用。

  • SELinux 活动策略:SELinux 配置文件中的 SELINUXTYPE 配置项告诉 init 进程在系统启动过程中载入哪个 SELinux 策略,这里设置的字符串必须匹配用来存储二进制策略版本的目录名,例如,我们使用 targeted 策略为例,因此设置 SELINUXTYPE=targeted

6.2 启用/禁用 SELinux

启用/禁用 SELinux步骤很简单,只需修改其配置文件,然后重启系统即可。

启用 SELinux

  1. 编辑配置文件 /etc/selinux/config 。根据前面对配置文件的介绍,编辑确定 SELinux 的运行模式(enforcing)和活动策略(targeted):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # This file controls the state of SELinux on the system.
    # SELINUX= can take one of these three values:
    # enforcing - SELinux security policy is enforced.
    # permissive - SELinux prints warnings instead of enforcing.
    # disabled - No SELinux policy is loaded.
    SELINUX=enforcing
    # SELINUXTYPE= can take one of these two values:
    # targeted - Targeted processes are protected,
    # minimum - Modification of targeted policy. Only selected processes are protected.
    # mls - Multi Level Security protection.
    SELINUXTYPE=targeted
  2. 重启系统:

    1
    shutdown -r now

禁用 SELinux

  1. 编辑配置文件 /etc/selinux/config 。根据前面对配置文件的介绍,编辑 SELinux 的运行模式(disabled):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # This file controls the state of SELinux on the system.
    # SELINUX= can take one of these three values:
    # enforcing - SELinux security policy is enforced.
    # permissive - SELinux prints warnings instead of enforcing.
    # disabled - No SELinux policy is loaded.
    SELINUX=disabled
    # SELINUXTYPE= can take one of these two values:
    # targeted - Targeted processes are protected,
    # minimum - Modification of targeted policy. Only selected processes are protected.
    # mls - Multi Level Security protection.
    SELINUXTYPE=targeted
  2. 重启系统:

    1
    shutdown -r now

7. SELinux 布尔值(Booleans)


SELinux 中的布尔值允许 SELinux 在运行状态下动态调整部分策略,而无需重新加载 SELinux 或 重新编译 SELinux 策略,比如 允许某些服务访问 NFS 卷。

7.1 查看 SELinux 布尔值

root 用户身份,运行 semanage boolean -l 命令,即可查看包括 关闭(off)打开(on) 状态的所有布尔变量,下面是部分布尔值列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
➜ semanage boolean -l | wc -l
282
➜ semanage boolean -l | more
SELinux 布尔值 状态 默认 描述
ftp_home_dir (关,关) Allow ftp to home dir
smartmon_3ware (关,关) Allow smartmon to 3ware
mpd_enable_homedirs (关,关) Allow mpd to enable homedirs
xdm_sysadm_login (关,关) Allow xdm to sysadm login
xen_use_nfs (关,关) Allow xen to use nfs
mozilla_read_content (关,关) Allow mozilla to read content
ssh_chroot_rw_homedirs (关,关) Allow ssh to chroot rw homedirs
mount_anyfile (开,开) Allow mount to anyfile
icecast_use_any_tcp_ports (关,关) Allow icecast to use any tcp ports
openvpn_can_network_connect (关,关) Allow openvpn to can network connect
zoneminder_anon_write (关,关) Allow zoneminder to anon write
telepathy_connect_all_ports (关,关) Allow telepathy to connect all ports
spamassassin_can_network (关,关) Allow spamassassin to can network
gluster_anon_write (关,关) Allow gluster to anon write
deny_ptrace (关,关) Allow deny to ptrace
selinuxuser_execmod (开,开) Allow selinuxuser to execmod
httpd_can_network_relay (关,关) Allow httpd to can network relay
openvpn_enable_homedirs (开,开) Allow openvpn to enable homedirs
gpg_agent_env_file (关,关) Allow gpg to agent env file

上面三列内容的意义:

  • SELinux 布尔值 列出了 SELinux 布尔值的名称;
  • 状态 默认 一对数据列出了对应布尔值当前的开关状态与默认状态;
  • 描述 列出了布尔值的用途。

比如第一行的 ftp_home_dir 当前状态和默认状态都是 关(off) ,这条策略组织 FTP 服务的守护进程(比如 vsftpd)读/写 用户主目录里的文件。

getsebool -a 命令会简要列出 SELinux 布尔值及其 当前状态(默认安字典序) ,而无其他信息,下面是部分结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
➜ getsebool -a
abrt_anon_write --> off
abrt_handle_event --> off
abrt_upload_watch_anon_write --> on
antivirus_can_scan_system --> off
antivirus_use_jit --> off
auditadm_exec_content --> on
authlogin_nsswitch_use_ldap --> off
authlogin_radius --> off
authlogin_yubikey --> off
awstats_purge_apache_log_files --> off
boinc_execmem --> on
cdrecord_read_content --> off
cluster_can_network_connect --> off
cluster_manage_all_files --> off
cluster_use_execmem --> off
cobbler_anon_write --> off
cobbler_can_network_connect --> off
cobbler_use_cifs --> off
cobbler_use_nfs --> off

而运行 getsebool [boolean-name] 命令则会列出指定布尔值的当前状态:

1
2
➜ getsebool cvs_read_shadow
cvs_read_shadow --> off

上面例子中,还可以用 空格 隔开多个布尔值名称,用以一次查看多个布尔值变量的状态:

1
2
3
4
➜ getsebool cvs_read_shadow daemons_dump_core ftp_home_dir
cvs_read_shadow --> off
daemons_dump_core --> off
ftp_home_dir --> off

7.2 配置布尔值

root 用户身份运行 setsebool [boolean_name] [on|off] 命令就可以将指定的布尔值状态修改成 关闭打开。下面用一个实例演示如何用此命令调整布尔值 httpd_can_network_connect_db 的状态。

  1. 布尔值 httpd_can_network_connect_db 用以 允许/阻止 HTTP 服务器访问数据库服务,默认状态为 off

    1
    2
    ➜ getsebool httpd_can_network_connect_db
    httpd_can_network_connect_db --> off
  2. 下面的命令可以 临时允许 HTTP 服务器中的脚本或模块访问数据库服务器:

    1
    2
    3
    4
    ➜ setsebool httpd_can_network_connect_db on
    ➜ getsebool httpd_can_network_connect_db
    httpd_can_network_connect_db --> on

即可发现状态修改成功。

  1. 需要注意的是上述修改知识 临时 的,重启系统即会失效,若要 永久 修改某个布尔值的状态,需要对 setsebool 命令加 -P 参数:
    1
    ➜ setsebool -P httpd_can_network_connect_db on

8. SELinux 上下文修改与恢复

在前文『3.4.2 限制进程』与『3.4.3 非限制进程』两个小节的例子中,曾经使用过 chcon 命令来修改文件的类型,用 restorecon 命令来恢复文件的类型,本小节再多介绍一下其用法。

8.1 临时修改上下文

chcon 命令可以修改文件的 SELinux 上下文,但其功能不止能修改前文例子中的类型类型只是 SELinux 上下文的 4 个主要元素(user:role:type:level)之一,而该命令可以修改任何一个元素。先看其 Usage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
➜ chcon --help
用法: chcon [选项]... 环境 文件...
 或: chcon [选项]... [-u 用户] [-r 角色] [-l 范围] [-t 类型] 文件...
 或: chcon [选项]... --reference=参考文件 文件...
Change the SELinux security context of each FILE to CONTEXT.
With --reference, change the security context of each FILE to that of RFILE.
Mandatory arguments to long options are mandatory for short options too.
--dereference affect the referent of each symbolic link (this is
the default), rather than the symbolic link itself
-h, --no-dereference affect symbolic links instead of any referenced file
-u, --user=USER set user USER in the target security context
-r, --role=ROLE set role ROLE in the target security context
-t, --type=TYPE set type TYPE in the target security context
-l, --range=RANGE set range RANGE in the target security context
--no-preserve-root do not treat '/' specially (the default)
--preserve-root fail to operate recursively on '/'
--reference=RFILE use RFILE's security context rather than specifying
a CONTEXT value
-R, --recursive operate on files and directories recursively
-v, --verbose output a diagnostic for every file processed
The following options modify how a hierarchy is traversed when the -R
option is also specified. If more than one is specified, only the final
one takes effect.
-H if a command line argument is a symbolic link
to a directory, traverse it
-L traverse every symbolic link to a directory
encountered
-P do not traverse any symbolic links (default)
--help 显示此帮助信息并退出
--version 显示版本信息并退出
GNU coreutils online help: <http://www.gnu.org/software/coreutils/>
请向<http://translationproject.org/team/zh_CN.html> 报告chcon 的翻译错误
要获取完整文档,请运行:info coreutils 'chcon invocation'

有两个要点需要注意:

  • 根据chcon 命令的 Usage ,前文只用 -t 参数修改了指定文件的 类型,而还可以用 -u 参数修改文件的 SELinux user,用 -r 参数修改指定文件的 角色,用 -l 参数修改文件的 level。用法雷同,此处不详述;
  • 除了能修改 文件 的上下文,该命令还能修改 目录 的上下文信息,参数 -R 可以递归地修改目录下所有文件、子目录及子目录里的文件的上下文信息;
  • chcon 命令对 文件/目录 上下文的修改是 临时 性的,重启系统即会失效,若要 永久 修改上下文信息,需要用 semanage 命令,下文详述。

restorecon 命令用法单一,功能也单一,仅仅是恢复 文件/目录 的默认上下文信息。

8.2 永久修改上下文

如前文所述,semanage 命令可以 永久性 修改 文件/目录 的上下文信息。当 SELinux 使用 Targeted Policy 时,该命令修改的文件的上下文先信息存储在 /etc/selinux/targeted/contexts/files/ 目录:

  • file_contexts 文件存放的是大部分文件的默认上下文信息,包括semanage fcontext 命令更新的文件上下文信息;
  • file_contexts.local 文件存放的是 file_contexts 文件中不存在的、新增文件/目录的上下文信息。

有两个工具可以访问上述两个文件:setfilesrestorecon。前者在文件系统重新加载的时候重新标记文件上下文;后者在恢复指定文件的上下文信息的时候读取该文件的默认上下文。由此可知,semanage fcontext 命令修改的文件上下文是永久性的,无论文件系统是否重新加载。至于某个 Linux 系统用户能够修改指定文件的上下文信息,则由 SELinux 的策略规则控制。

semanage 命令的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜ semanage -h
usage: semanage [-h]
{import,export,login,user,port,interface,module,node,fcontext,boolean,permissive,dontaudit}
...
semanage is used to configure certain elements of SELinux policy with-out
requiring modification to or recompilation from policy source.
positional arguments:
{import,export,login,user,port,interface,module,node,fcontext,boolean,permissive,dontaudit}
import Output local customizations
export Output local customizations
login Manage login mappings between linux users and SELinux
confined users
user Manage SELinux confined users (Roles and levels for an
SELinux user)
port Manage network port type definitions
interface Manage network interface type definitions
module Manage SELinux policy modules
node Manage network node type definitions
fcontext Manage file context mapping definitions
boolean Manage booleans to selectively enable functionality
permissive Manage process type enforcement mode
dontaudit Disable/Enable dontaudit rules in policy
optional arguments:
-h, --help show this help message and exit

可以看到 semanage 命令下有个 子命令 fcontext ,可以管理文件的上下文,其 Usage 为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
➜ semanage fcontext -h
usage: semanage fcontext [-h] [-n] [-N] [-s STORE] [ --add ( -t TYPE -f FTYPE -r RANGE -s SEUSER | -e EQUAL ) FILE_SPEC ) | --delete ( -t TYPE -f FTYPE | -e EQUAL ) FILE_SPEC ) | --deleteall | --extract | --list -C | --modify ( -t TYPE -f FTYPE -r RANGE -s SEUSER | -e EQUAL ) FILE_SPEC ) ]
positional arguments:
file_spec file_spec
optional arguments:
-h, --help show this help message and exit
-C, --locallist List fcontext local customizations
-n, --noheading Do not print heading when listing fcontext object
types
-N, --noreload Do not reload policy after commit
-S STORE, --store STORE
Select an alternate SELinux Policy Store to manage
-a, --add Add a record of the fcontext object type
-d, --delete Delete a record of the fcontext object type
-m, --modify Modify a record of the fcontext object type
-l, --list List records of the fcontext object type
-E, --extract Extract customizable commands, for use within a
transaction
-D, --deleteall Remove all fcontext objects local customizations
-e EQUAL, --equal EQUAL
Substitute target path with sourcepath when generating
default label. This is used with fcontext. Requires
source and target path arguments. The context labeling
for the target subtree is made equivalent to that
defined for the source.
-f {a,f,d,c,b,s,l,p}, --ftype {a,f,d,c,b,s,l,p}
File Type. This is used with fcontext. Requires a file
type as shown in the mode field by ls, e.g. use -d to
match only directories or -- to match only regular
files. The following file type options can be passed:
-- (regular file),-d (directory),-c (character
device), -b (block device),-s (socket),-l (symbolic
link),-p (named pipe) If you do not specify a file
type, the file type will default to "all files".
-s SEUSER, --seuser SEUSER
SELinux user name
-t TYPE, --type TYPE SELinux Type for the object
-r RANGE, --range RANGE
MLS/MCS Security Range (MLS/MCS Systems only) SELinux
Range for SELinux login mapping defaults to the
SELinux user record range.

semanage fcontext 命令永久性修改 文件/目录 的上下文,需要两个步骤:

  1. 设定指定 文件/目录 的默认上下文(注意文件/目录的路径要用完整路径):

    1
    semanage fcontext -a [options] [file|dir]
  2. restorecon 命令还原该文件/目录的上下文:

    1
    restorecon -v [file|dir]

永久修改文件/目录上下文

下面实例演示一下如何永久性修改一个文件/目录的上下文信息。

  1. root 用户身份在 /etc/ 目录下新建一个文件 file(按照 SELinux 策略规则,/etc/ 目录下的新建文件默认类型为 etc_t):
    1
    2
    3
    /etc ➜ touch file1
    /etc ➜ ls -Z file1
    -rw-r--r--. root root unconfined_u:object_r:etc_t:s0 file1

NOTE:
此处的 file1,也可是新建一个目录,查看指定目录的 SELinux 上下文信息可用命令:ls -dZ [dir]

  1. root 用户身份运行以下命令将 file1类型 修改为 samba_share_t :
    1
    2
    3
    ➜ semanage fcontext -a -t samba_share_t /etc/file1
    ➜ ls -Z /etc/file1
    -rw-r--r--. root root unconfined_u:object_r:etc_t:s0 /etc/file1

上述 semanage fcontext 命令往 /etc/selinux/targeted/contexts/files/file_contexts. local 文件中新增了一条规则:

1
2
3
4
➜ tail /etc/selinux/targeted/contexts/files/file_contexts.local
# This file is auto-generated by libsemanage
# Do not edit directly.
/etc/file1 system_u:object_r:samba_share_t:s0

NOTE:
选项 -a 代表要 新增 一条规则到前文所述的 file_contexts.local 文件;选项 -t 代表要修改的上下文元素为 类型。可以看到 semanage fcontext 命令执行后,/etc/file1 文件新的上下文并没有立即生效

  1. restorecon 命令恢复 /etc/file1 的上下文。因为前面的操作为该文件在 file_contexts.local 中新增了一条记录, restorecon 命令会从 file_contexts.local 中读取这一条规则为 /etc/file1 恢复上下文,进而将 /etc/file1类型改为 samba_share_t
    1
    2
    3
    4
    5
    ➜ restorecon -v /etc/file1
    restorecon reset /etc/file1 context unconfined_u:object_r:etc_t:s0->unconfined_u:object_r:samba_share_t:s0
    ➜ ls -Z /etc/file1
    -rw-r--r--. root root unconfined_u:object_r:samba_share_t:s0 /etc/file1

一次性永久修改目录和其中文件的上下文

semanage fcontext 命令还支持通过匹配目录下所有文件/目录的正则表达式,来批量永久修改指定目录及其下属所有文件/目录的 SELinux 上下文。下面实例演示如何新建一个目录,将它以及它内部所有的文件/子目录类型修改为可供 Apache HTTP 服务器访问的 httpd_sys_content_t(如果需要将 HTTP 服务器主目录改到别处,此例会非常有用)。

  1. root 用户身份新建一个目录 /web/ ,并在其中新建三个文件 file1/file2/file3 ,该目录和其中的文件默认 类型 将会是 default_t

    1
    2
    3
    4
    5
    6
    7
    8
    ➜ mkdir /web
    ➜ touch /web/file{1,2,3}
    ➜ ls -dZ /web
    drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /web
    ➜ ls -Z /web
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file1
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file2
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file3
  2. root 用户身份用 semanage fcontext 命令将 /web 目录及其中的 3 个文件的 类型 改为 httpd_sys_content_t (同前面的例子,不会马上生效):

    1
    2
    3
    4
    5
    6
    7
    ➜ semanage fcontext -a -t httpd_sys_content_t "/web(/.*)?"
    ➜ ls -dZ /web
    drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /web
    ➜ ls -Z /web
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file1
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file2
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file3

同前面的例子,semanage fcontext 命令往 /etc/selinux/targeted/contexts/files/file_contexts. local 文件中新增了一条规则:

1
/web(/.*)? system_u:object_r:httpd_sys_content_t:s0

NOTE:
上述例子中 semanage fcontext 命令里的正则表达式 "/web(/.*)?" 表示 /web/ 目录自己机器中的所有 文件/子目录。

  1. 同样,用 restorecon 命令恢复 /web 目录及其中所有文件/子目录的上下文:
    1
    2
    3
    4
    5
    ➜ restorecon -Rv /web
    restorecon reset /web context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
    restorecon reset /web/file1 context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
    restorecon reset /web/file2 context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
    restorecon reset /web/file3 context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0

NOTE:
按照 SELinux 的默认策略,目录中新增文件的 SELinux 上下文将继承自目录的 SELinux 上下文,所以,经过上面的操作,如果 /web/ 目录下再有新增文件/子目录,它们的 SELinux 上下文的 类型 将会是 httpd_sys_content_t


删除新增 SELinux 策略规则

通过 semanage fcontext -a 命令新增的 SELinux 上下文规则,可以通过 semanage fcontext -d [file|dir] 命令来删除(可以参考semanage fcontextUsage )。通常分两个步骤,先删除,后 restorecon。如果删除时遇到正则表达式(比如上面的 /web(/.*)?),一定要用半角双引号将正则括起来,例如:

1
➜ semanage fcontext -d "/web(/.*)?"

选项 -d 后面的 [file|dir]semanage fcontext -a [options] [file|dir] 命令添加上下文规则是指定的 文件/目录 ,也是 /etc/selinux/targeted/contexts/files/file_contexts. local 文件中的 第一列,如下面的 /etc/file/web(/.*)? :

1
2
3
4
5
➜ tail /etc/selinux/targeted/contexts/files/file_contexts.local
# This file is auto-generated by libsemanage
# Do not edit directly.
/etc/file1 system_u:object_r:samba_share_t:s0
/web(/.*)? system_u:object_r:httpd_sys_content_t:s0

比如我们要删除上面对 /web 目录及其里面文件/子目录的新增上下文类型,只需按照下面的步骤来操作:

  1. 删除记录(不会立即生效):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ➜ semanage fcontext -d "/web(/.*)?"
    ➜ cat /etc/selinux/targeted/contexts/files/file_contexts.local
    # This file is auto-generated by libsemanage
    # Do not edit directly.
    ➜ ls -dZ /web
    drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 /web
    ➜ ls -Z /web
    -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file1
    -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file2
    -rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file3
  2. 恢复默认上下文:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ➜ restorecon -Rv /web
    restorecon reset /web context unconfined_u:object_r:httpd_sys_content_t:s0->unconfined_u:object_r:default_t:s0
    restorecon reset /web/file1 context unconfined_u:object_r:httpd_sys_content_t:s0->unconfined_u:object_r:default_t:s0
    restorecon reset /web/file2 context unconfined_u:object_r:httpd_sys_content_t:s0->unconfined_u:object_r:default_t:s0
    restorecon reset /web/file3 context unconfined_u:object_r:httpd_sys_content_t:s0->unconfined_u:object_r:default_t:s0
    ➜ ls -dZ /web
    drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /web
    ➜ ls -Z /web
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file1
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file2
    -rw-r--r--. root root unconfined_u:object_r:default_t:s0 file3

可以看到 /web 目录及其下属所有文件的 SELinux 上下文类型都恢复到初始的default_t了。

9. SELinux 上下文保持


本小节解释 文件/目录 在 拷贝/移动/归档打包 过程中其 SELinux 上下文发生的变化,以及如何在 拷贝 和 归档打包 过程中保留原 SELinux 上下文。

9.1 拷贝 文件/目录

默认情况下,将一个文件/目录 拷贝到另一个地方(假设目的目录的 SELinux上下文不同于文件原来的上下文),最后在新位置的文件 SELinux 上下文将继承目的目录的上下文,示例如下:

1
2
3
4
5
6
7
pwd
/root
➜ ls -Z
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 test.txt
➜ cp -v test.txt /etc/
"test.txt" -> "/etc/test.txt"➜ ls -Z /etc/test.txt
-rw-r--r--. root root unconfined_u:object_r:etc_t:s0 /etc/test.txt

由此可知,原来在 /root 目录下 SELinux 上下文类型为 admin_home_ttest.txt 文件,用 cp 命令直接拷贝到 /etc/ 目录,其 SELinux 上下文 类型 就会变成 /etc/ 目录中默认的类型 etc_t (假设 /etc/test.txt 文件原来不存在)。

上面的例子中,如果 /etc/test.txt 文件原本就存在,那么直接用 cp 命令将执行 覆盖拷贝 ,但新的 /etc/test.txt SELinux 上下文将 不变(既不继承 /etc/ 默认的上下文类型,也不会被原来 /root/test.txt 的 SELinux 上下文类型所覆盖)。


保留原上下文

若要拷贝过程中,保留源文件的 SELinux 上下文类型,则需要对 cp 命令加上选项 --preserve=context ,示例如下:

1
2
3
4
5
6
➜ rm -v /etc/test.txt
已删除"/etc/test.txt"
➜ cp -v --preserve=context test.txt /etc/
"test.txt" -> "/etc/test.txt"
➜ ls -Z /etc/test.txt
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 /etc/test.txt

由此例可知,对 cp 命令加上选项 --preserve=context 后,将文件 test.txt/root 目录拷贝到 /etc/ 目录,其 SELinux 上下文 类型 依然是 admin_home_t


拷贝过程中更改上下文

还可以在拷贝文件过程中更改 SELinux 上下文,新生成的文件上下文将会既不同于源文件的上下文,也不同于目的目录的默认上下文。只要对 cp 命令加上选项 --context=[user:role:type:level] 即可,示例如下 :

1
2
3
4
5
6
➜ rm -v /etc/test.txt
已删除"/etc/test.txt"
➜ cp -v --context=system_u:object_r:samba_share_t:s0 test.txt /etc/
"test.txt" -> "/etc/test.txt"
➜ ls -Z /etc/test.txt
-rw-r--r--. root root system_u:object_r:samba_share_t:s0 /etc/test.txt

由此例可知,在拷贝过程中对 cp 命令加上选项 --context=system_u:object_r:samba_share_t:s0 后,新生成的 /etc/test.txt 文件的 SELinux 上下文就变成了 system_u:object_r:samba_share_t:s0

9.2 移动 文件/目录

Linux 中的 mv 命令来移动 文件/目录,会将 源文件/源目录 的 SELinux 上下文也一并附带移动,到目的地址新生成的 文件/目录 将保持与 源文件/源目录 相同的 SELinux 上下文。所以,在启用 SELinux 的 Linux 系统中,移动操作需要额外注意 SELinux 上下文信息。此处无示例演示。

9.3 使用 tar 归档打包 文件/目录

默认情况下,使用 tar 命令归档打包 文件/目录 时,是不附带 SELinux 上下文信息的,将一个 tar 包解开到新目录,其中的文件将会继承新目录的 SELinux 上下文,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cd /var/www/html
➜ touch file{1,2,3}
➜ ls -Z
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file1
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file2
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file3
➜ tar cf test.tar file{1,2,3}
➜ ls -Z
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file1
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file2
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file3
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 test.tar
➜ cp -v test.tar ~/
"test.tar" -> "/root/test.tar"
cd ~
➜ ls -Z
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 test.tar
➜ tar -xf test.tar
➜ ls -Z
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 file1
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 file2
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 file3
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 test.tar

上面的例子可知,在 /var/www/html 目录新建的 3 个 SELinux 上下文类型为 httpd_sys_content_t 的文件,进过 tar 命令直接打包,然后拷贝到 /root 目录再解压,其中的 3 根文件 SELinux 上下文 类型 却变成了 admin_home_t

有没有办法在打包归档过程中保留原始文件的 SELinux 上下文信息?

其实,tar 命令提供了一个选项 --selinux ,使用 tar --selinux -cf test. tar [files] 的形式,即可保留源文件的 SELinux 上下文信息,此处不做演示。

10. 检查默认上下文


matchpathcon 工具可以检查指定的 文件/目录 的 SELinux 上下文是否“正确”,该工具会索引系统默认的 SELinux 策略,并以此检查指定的 文件/目录 的 SELinux 上下文是否与默认策略吻合。下面示例演示如何使用该工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜ ls -Z /var/www/html
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file1
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file2
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file3
➜ chcon -t samba_share_t /var/www/html/file1
➜ ls -Z /var/www/html
-rw-r--r--. root root unconfined_u:object_r:samba_share_t:s0 file1
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file2
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file3
➜ matchpathcon -V /var/www/html/*
/var/www/html/file1 has context unconfined_u:object_r:samba_share_t:s0, should be system_u:object_r:httpd_sys_content_t:s0
/var/www/html/file2 verified.
/var/www/html/file3 verified.
➜ restorecon -v /var/www/html/file1
restorecon reset /var/www/html/file1 context unconfined_u:object_r:samba_share_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
➜ ls -Z /var/www/html
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file1
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file2
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 file3

在上面例子中,第 2 条命令用 chcon/var/www/html/file1 的 SELinux 上下文 类型 改为 samba_share_t,然后第 4 条命令用 matchpathcon 检测到该文件的 SELinux 上下文应为 system_u:object_r:httpd_sys_content_t:s0 ,随后用 restorecon 命令就会将该文件的 SELinux 上下文“修正”。

11. 总结

本系列 2 篇文档从最基本的安全模型开始,把 Linux 系统中实现的权限管理与访问控制从UGO+RWX机制到 ACL 机制到最后的 SELinux,详细介绍一遍。

由上面的内容可知,这些 Linux 系统中实现的安全机制与策略,是依次增强的,对权限和访问的管理、控制粒度是依次减小的,所以能得到更灵活也更安全的配置,当然,也更复杂繁琐。

机制本身的安全性毋庸置疑,只是如果在使用相关工具对这些安全机制进行安全配置时,如果掌握不好配置方法与注意事项,那么配置出来的系统可能会有安全隐患,例如不恰当地应用 setuid 位。

最后的 SELinux 部分,其实只介绍了其安全模型设计原理、SELinux 中的元素和工作原理、基础的管理与配置,这些都是使用 SELinux 的基础知识,掌握这些基础知识,才能对 SELinux 进行基础地使用。而 SELinux 的一些高级用法,比如自定义编写、编译策略规则,本文档并未介绍。

12. 参考资料

  1. SELinux Manual For RHEL7