Linux 权限管理与访问控制详解(2)——MAC 和 SELinux | JiaYu's Blog
本系列 前一篇 主要介绍了 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 综合提供了 RBAC、TE(类型增强)和 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 范围 则给出了 MLS 和 MCS(Multi-Category Security,多种类安全)机制所采用的 安全级别(Level)。关于 MCS 和 Level,下文详解;
- 最后一列的服务,定义了对应的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.c2 与 c0,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_t和etc_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_policy和setsebool来操作和管理 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 安装了 dbus 和 audit 服务,另外一个有用的 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 |
当然,不同形式的拒绝操作信息被保存到不同的文件中,是根据不同的守护进程耳钉的,下表列出了对应不同的守护进程的日志文件路径: