SELinux

1. 引言

安全增强式 Security-Enhanced Linux(SELinux)是一个在内核中实践的强制存取控制(MAC)安全性机制。SELinux 首先在 CentOS 4 出现,并在其后的 CentOS 发行版本获得重大改善。这些改善代表用 SELinux 解决问题的方法亦随著时间而改变。

1.1. 部份问题所在

要更了解 SELinux 为何是重要、及能够为你做甚么,最简单的方法就是参考一些例子。在未启用 SELinux 的情况下,要控制用户的档案存取权,唯有透过酌情存取控制(DAC)方法如档案权限或存取控制清单(ACL)。不论用户或程式都可以将不安全的档案权限赋予其他人,或反过来存取系统在正常运作下无须存取的部份。举个例说:

基本上在传统 DAC 模式只在两个权限级别,root 及用户,而当中无法简易地实施最小权限的理念。很多由 root 启动1的进程在后期会撇除它们的权限并以受限制的用户身份来运行,有些则会在 chroot 的情况下执行,但这些安全措施都是酌情的。

1.2. 解决方案

SELinux 更能遵从最小权限的理念。在预设的 enforcing 情况下,一切均被拒绝,接著有一系列例外的政策来允许系统的每个元素(服务、程式、用户)运作时所需的存取权。当一项服务、程式或用户尝试存取或修改一个它不须用的档案或资源时,它的请求会遭拒绝,而这个行动会被记录下来。

由于 SELinux 是在内核中实践的,应用程式无须被特别编写或重写便可以采用 SELinux。当然,如果一个程式特别留意稍后所提及的 SELinux 错误码,它的运作可能会更畅顺。假若 SELinux 拦阻了一个行动,它会以一个标准的(至少是常规的)「拒绝存取」类错误来汇报给该应用程式。然而,很多应用程式不会测试系统函数所传回的错误码,因此它们也许不会输出讯息解释问题所在,或者输出错误讯息。

不过请留意,那些理论上能提供更高安全度的案例,如:局限只有某些获授权的程式可读取用户的 ~/.ssh/ 目录,防止邮件派发程式(Mail Delivery Agent)更改拥有群组、群组设定或其它读档权限,或阻止浏覧器读取用户的主目录,这些都未被加进行 CentOS 6 或以下版本的 SELinux 政策内。CentOS 6 及 7 有限量地如上述局限用户程式,但所复盖的范围不及那些专属的系统服务。要是有系统管理员想更改预设没限制的登入设定,他们可以参阅下文的 基于角色的存取控制 部份。

2. SELinux 模式

SELinux 拥有三个基本的操作模式,当中 Enforcing 是预设的模式。此外,它还有一个 targetedmls 的修饰语。这管制 SELinux 规则的应用有多广泛,当中 targeted 是较宽松的级别。

SELinux 的模式可以透过 Adminstration 选单里的 SELinux 图像管理介面、或者在指令行执行 system-config-selinux 来检视及更改(SELinux 图像管理介面是 policycoreutils-gui 套件的一部份,预设是不会被安装的)。

较喜欢指令行的用户可使用 sestatus 这个指令来检视现时的 SELinux 状况:

# sestatus
SELinux status:                 enabled
SELinuxfs mount:                /selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy version:                 21
Policy from config file:        targeted

setenforce 这个指令可以即时切换 EnforcingPermissive 这两个模式,但请注意这些改动在系统重新开机时不会被保留。

要令改动过渡系统开机,请在 /etc/selinux/config 内修改 SELINUX= 这一行为 enforcing、permissive 或 disabled。例如:SELINUX=permissive。

注: 当你由 Diabled 切换至 PermissiveEnforcing 模式时,我们强烈建议你重新启动系统并重新标签档案系统。

3. SELinux 政策

正如我们所说,SELinux 遵从最小权限这个理念;在预设情况下一切均被拒绝,而系统的每部份有一个政策来允许它存取所需的功能。这个描述用来形容 strict(严格型) 政策最为贴切。不过要令这类政策适用于企业级 Linux 可能会应用到的各个环境,编写方面是困难的。后果可能是 SELinux 会为系统管理员及用户产生很多问题,而系统管理员索性停用 SELinux 而不解决这些问题,最后还是违背了内置的保护。

SELinux 的设计允许调配不同类型的政策。CentOS 内的预设政策是 targeted(针对型) 政策,专门「针对」和规限选定的系统进程。CentOS 4 只定义了 15 个目标(包括 http、named、dhcpd、mysqld)。随后在 CentOS 5 这个数字已升超过 200 个目标。

系统内其它一切系统进程、用户程式和所有自制应用程式都在 unconfined(不受规限) 的区域下运行,并且不纳入 SELinux 的保护范围内。

这样做的目标之一,是要令每个已安装及在开机时执行的进程,都预设在一个受规限的区域下运行。 targeted 政策在设计时尽量保护最多的主要进程而不会对用户的经验产生不利影响,所以多数用户甚至乎不应察觉 SELinux 正在运行中。

4. SELinux 存取控制

CentOS 的 targeted SELinux 政策包含四种存取控制方法:

所有进程及档案都拥有一个 SELinux 的安全性脉络。让我们检视 Apache 的主页,/var/www/html/index.html 的 SELinux 安全性脉络来看看它们如何运作:

$ ls -Z /var/www/html/index.html -rw-r--r--  username username system_u:object_r:httpd_sys_content_t /var/www/html/index.html 

注: -Z 这个标旗在多数工具内都可用来显示 SELinux 安全性脉络(例如:ls -Z、 ps axZ 等)。

除了标准的档案权限及拥有权,我们更可以看到 SELinux 脉络栏:system_u:object_r:httpd_sys_content_t。

这是建基于「用户:角色:类别:多层保障」。在上述例子里,「用户:角色:类别」栏都有显示,而「多层保障」是隐藏的。在预设的 targeted 政策里,类别是用来实施「强制类别」的重要栏位,在这里它是 httpd_sys_content_t。

现在让我们看看 Apache 网页伺服器,httpd,这个进程的 SELinux 安全性脉络:

$ ps axZ | grep httpd
system_u:system_r:httpd_t        3234 ?        Ss     0:00 /usr/sbin/httpd

从类别栏我们看出 Apache 在 httpd_t 这个类别区域内运行。

最后,让我们看看位于我们的主目录内的一个档案的安全性脉络:

$ ls -Z /home/username/myfile.txt
-rw-r--r--  username username user_u:object_r:user_home_t      /home/username/myfile.txt

它的类别是 user_home_t,这是位于每个户主目录内的档案的预设类别。

唯有相似的类别才可互相存取,因此以 httpd_t 运行的 Apache 可以读取拥有 httpd_sys_content_t 类别的 /var/www/html/index.html。由于 Apache 在 httpd_t 这个区域内运行但不属 username 这个用户,纵使 /home/username/myfile.txt 可供任何人读取,Apache 却无法存取该档案,因为它的 SELinux 安全性脉络并不是 httpd_t 类别。倘若 Apache 被人占用,又假设它仍未取得更改 SELinux 标签至另一个脉络的 root 权限,它将会无法启动 httpd_t 区域外的进程(藉此防止权限升级),或存取与 httpd_t 区域不相关的档案。

4.1. 基于角色的存取控制(RBAC)

虽然 targeted 政策的预设值不限制登入,管理员却可以轻易地改用 基于角色的存取控制 模式。这个模式在用户范畴亦转用「严格」模式,以便能针对个别程式。若要启用此功能,请利用 semanage-login 为你的用户新增登入对照:

semanage login -a -s "staff_u" -r "s0-s0:c0.c1023" <username>

semanage-login 这个指令将一位 Linux 用户对照对 staff_u 的 SELinux 用户,及设定 MLS/MCS 范围为 s0-s0:c0.c1023。从此,登入后执行 id -Z 将会显示 staff_u:staff_r:staff_t:s0-s0:c0.c1023,而不再是 unconfined_u:unconfined_r:unconfined_t:s0。虽然 staff_r 不是留作管理用的角色,但它却容许用户转换至其它角色。当一位系统管理员想执行管理系统的工作时,他应该在 sudo 内以 -r 参数转换至 sysadm_r 的角色。

sudo -r sysadm_r -i

你可以在 /etc/sudoers.d/ 内新增设定档,自动把用户对照至预设的管理员身份。

%wheel ALL=(ALL) TYPE=sysadm_t ROLE=sysadm_r ALL

你仍可以通过 newrole 登入为成为不受限制的用户或转换至不受限制的角色,但这样做会损失局限用户范畴带来的益处。你亦可移除这个功能,做法是新增一个只扮演某些角色的 SELinux 用户:

semanage user -a -R "staff_r sysadm_r system_r -r "s0-s0:c0.c1023" my_staff_u

现在以 my_staff_u 取代 semanage-login 指令中的 staff_u。当你尝试转换至 unconfined_r ⻆色时,便会出现 AVCSELINUX_ERR 的信息。假若系统管理员有意阻止所有不受限制用户的登入,他可以把 __default__ 这个登入对照到一个更合适的 SELinux 用户,做法依然是透过 semanage-login。

semanage login -m -s "user_u" -r "s0" __default__

要是一位用户以别于惯常的角色登入,这功能就视乎登入程式。SSH 支援以另一个 SELinux 角色登入,做法是指定于登入身份内(例如:staff 用户以 unconfined_r 角色登入):

ssh <username>/unconfined_r@hostname.net

从最小权限的角度来看,基于角色的存取控制下的严格模式仍未完善;利用政策分析程式进行快速搜寻,我们便会发现仍有数个受限制的程式依然能存取用户的 SSH 私钥。

sesearch -ACS -t ssh_home_t -c file -p read
Found 132 semantic av rules:
   allow snapperd_t file_type : file { ioctl read getattr lock open } ;
   allow oddjob_mkhomedir_t user_home_type : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow mplayer_t non_security_file_type : file { ioctl read getattr lock open } ;
   allow sendmail_t user_home_type : file { ioctl read getattr lock open } ;
   allow systemd_tmpfiles_t non_auth_file_type : file { ioctl read write create getattr setattr lock relabelfrom relabelto append unlink link rename open } ;
   allow login_pgm ssh_home_t : file { ioctl read getattr lock open } ;
   allow ssh_keygen_t ssh_home_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ;
   allow colord_t user_home_type : file { read getattr } ;
   ... snip ...

mplayer_t 大概不必读取 SSH 私钥,但由于要容让用户能从档案系统内的任何位置播放媒体,因此它有权限读取被视为 non_security_file_type 的非安全性相关档案。SELinux 的基本政策可以进一步局限这些存取权,但一如上文所说,这不是上游发行商的主要焦点。

除了严格模式外,基于角色的存取控制亦提供一个机制,局限用户以 sudo 转成 root 后可以做的事情。一般来说,以特定角色如资料库管理员或审计员给予用户最小权限是可取的做法,而 targeted 政策内含有一些适用于此用途的角色,它们的文档已收录在 政策文档 的相关页内。

seinfo -r

Roles: 14
   auditadm_r
   dbadm_r
   guest_r
   staff_r
   user_r
   logadm_r
   object_r
   secadm_r
   sysadm_r
   system_r
   webadm_r
   xguest_r
   nx_server_r
   unconfined_r

若要把用户对照到其中一个管理员角色,你可利用上述的 semanage-user 指令联系一位新增的 SELinux 用户到所需的角色,然后利用 semanage-login 把 Linux 的登入联系到该 SELinux 用户。要是该用户亦需要从他们的用户范畴启动他们所管理的系统服务(例如:从指令壳以 dbadm_r 角色启动 mysql 供侦错用),那么 system_r 亦应是他们所联系的角色之一。

semanage user -a -R "staff_r system_r auditadm_r" -r "s0-s0:c0.c1023" auditor_u
semanage login -a -s "auditor_u" -r "s0-s0:c0.c1023" <username>

4.2. 多种类保障(MCS)

多种类保障提供方法把 间隔 子集或系列与 SELinux 脉络联系。targeted 政策负责把与 mcs_constrained_type 有联系的类别进行分隔。若要明白这是如何运作,你必须懂得如何检视 MLS 部份的安全性脉络。那是 用户:角色:类别 后面那部份,它包括一个范围,所表达的是低及高安全度级别。

system_u:system_r:httpd_t:s0 - s0:c0.c5
                          ▼ ▼
                  低安全级别,高安全级别,还有是
                  已联系及未曾联系的间隔
                  间隔:c0、c1、c2、c3、c4 及 c5。

以上值得留意的就是 low 安全度别没有间隔,还有就是两个安全级别是相同的。第一点是 MCS 模式在 targeted 政策下的实践细节。当计算一个与 mcs_constrained_type 有联系的进程的存取权时,只有高安全级别的 MCS 间隔会用来作比较。第二点是源于 MLS 并未被应用。

上述安全性脉络的间隔部份是一个 种类范围,但它也可以是一组以逗号分隔的种类。运用种类子集会令到脉络广泛地与该范围内的种类联系。如要理解如何运用种类子集来运算两个进程的存取权,你必须检视 SELinux 存取级别的 dominance 规则(唯有当来源的高安全级别 支配 目标的高安全级别)。这些规则如下(只考虑种类,不计 MLS 安全级别)

本著这些规则,我们知道拥有 c0.c5 种类子集的脉络将会获允许存取 c0,c3 子集的脉络,但不获允许存取 c0,c6c0.c1024 子集的脉络。正由于此规则,sVirt 产生随机的种类子集,好叫虚拟区域与区域之间不会重叠。Android 计划亦采用同样做法,把应用程式放进被隔离的区域内。

MCS 的其中一个例子就是利用拥有多个 vhost 的 NGINX 连接到以 httpd 区域(例如 PHP-FRM)运行的后端伺服器。正常来说,基于强制类别的规则,这些后端伺服器都可以互相更改及管理对方的区域。要是它们被联系到种类的话,只有当一个伺服器支配其它伺服器才能进行修改。由于 NGINX 本身属 HTTPD 区域,它支配所有后端伺服器,因此要是 HTTPD 区域含有 c0 到 c5 等种类,我们便应该以 system_u:system_r:httpd_t:s0-s0:c0.c5 这个脉络来执行 NGINX,好叫它能连线到上游伺服器。每个后端伺服器只需以 c0-c5 中的一个种类来运行,并拥有 system_u:system_r:httpd_t:s0-s0:c1 等脉络。

在达至此目标前,有以下先决条件。首先,httpd_t 必须与 mcs_constrained_type 联系,而这个特性在 CentOS 7 上暂时只联系下列类别:

seinfo -xamcs_constrained_type
   mcs_constrained_type
      netlabel_peer_t
      openshift_t
      openshift_app_t
      sandbox_min_t
      sandbox_x_t
      sandbox_web_t
      sandbox_net_t
      svirt_t
      svirt_tcg_t
      svirt_lxc_net_t
      svirt_qemu_net_t
      svirt_kvm_net_t

若要在此清单上加入一个类别,你必须建立一个本地政策块把所需的类别联系至该特性。这是透过 typeattribute 陈述语句,并可如此编写:

policy_module(httpd_mcs, 1.0)
gen_require(`
    type httpd_t;
    attribute mcs_constrained_type;
')

typeattribute httpd_t mcs_constrained_type;

自订本地政策 有关如何建立政策模块。

当该类别与 mcs_constrained_type 联系后,你必须重新标签每台后端伺服器的内容,令它们的档案脉络内含有适切的种类。做法是在 /etc/selinux/targeted/contexts/customizable_types 内加入档案类别,但这个有机会在更新政策后失效。另一个做法是利用 semanage-fcontext 新增含有种类的档案脉络规格:

semanage fcontext -a -t httpd_sys_content_t -r "s0-s0:c1" "/srv/backend1(/.*)?"
semanage fcontext -a -t httpd_sys_content_t -r "s0-s0:c2" "/srv/backend2(/.*)?"

下一步是确保后端伺服器以正确的安全性脉络开机。在 CentOS 7 你可以在 systemd 的项目档加入 SELinuxContext= 这个选项来达至目标;在旧版本你可以利用 runcon 这个指令达至同一目的。

runcon "system_u:system_r:httpd_t:s0-s0:c1" "/usr/local/bin/backend-server"

至于 systemd 的项目档:

SELinuxContext=system_u:system_r:httpd_t:s0-s0:c1

现在每个后端伺服器都会被互相隔离,但 NGINX 仍然有权管理及发放讯息给它们。

5. 排除 SELinux 疑难

你终有一天会被 SELinux 阻止你存取所需的东西,而且要解决这个问题。SELinux 拒绝某个档案、进程或资源被存取的基要原因有数个:

头三个情况我们可以处理,而发出警报及通告正正是第四个情况的预期表现。

日志档是排除任何疑难的关键,而 SELinux 亦不例外。SELinux 预设会透过 Linux 审计系统 auditd 将日志写在 /var/log/audit/audit.log 内,而该务服预设为启用的。假若 auditd 长驻程式并未运行,信息将会被写进 /var/log/messages。SELinux 的日志都以 AVC 这个关键字作标签,让 grep 等程式可轻易地把它们从其它信息中过滤出来。

由 CentOS 5 起,你可以用 SELinux 排除疑难工具协助你分析日志档,将它们转换为供人阅读的格式。这个工具包含一个以可读格式显示信息及解决方案的图像介面、一个桌面通报图示、与及一个长驻进程,setroubleshootd,它负责查阅新的 SELinux AVC 警告并传送至通报图示。不运行 X 伺服器的用户可设置以电邮通报。SELinux 排除疑难工具是由 setroubleshoot 套件所提供的。这个工具可以从 X 视窗图像管理员的「系统」选单或从指令行启动:

sealert -b 

不运行 X 伺服器的人可以透过指令行产生供人阅读的报告:

sealert -a /var/log/audit/audit.log > /path/to/mylogfile.txt 

5.1. SELinux 与 Auditd

如上文所描述,SELinux 通过 auditd 产生讯息,有助审计系统及排除问题。审计日志中来自 SELinux 的最常见讯息类别是 AVC。它们描述被 SELinux 安全性伺服器所拒绝(或获允许)的操作。虽然 AVC 很普遍,它们并非唯一被 SELinux 产生及传送到审计系统的讯息类别。

Auditd 记录类别

描述

AVC

每当存取权获允许或拒绝时,由内核产生的讯息。由 auditallow 政策规则所允许的存取权会产生审计讯息,而 dontaudit 规则可防止存取权被拒绝时产生讯息。

USER_AVC

类似 AVC 讯息,但它们是由采用 SELinux 安全性伺服器的用家程式所产生的。这些程式又称为用家物件管家,当中包括 D-Bus 及 system。

SELINUX_ERR

当操作失效但 AVC 的描述有不足时,SELinux 安全性伺服器便会发出这些错误。这些情况多数是在 systemd 尝试化身成一个服务时发生的,它的服务档内含有 NoNewPrivileges=True,但 SELinux 的政策内并未将 systemd 与自己的类别联系起来。当一个进程过用 setcon(3) 更改它的执行绪时,这个讯息有可能会出现。如果一个程式尝试设定无效的脉络时,这个问题亦会出现,例如:PAM 利用 setcon(3) 更改登入用户的区域,而错误设置的 SELinux 登入有可能导致 SELinux 传回不正确的脉络。

USER_SELINUX_ERR

类似 SELINUX_ERR,当用家物件管家出现 SELinux 的错误时,但会产生这类讯息。

|| MAC_POLICY_LOAD, USER_MAC_POLICY_LOAD || 载入 SELinux 政策时所产生的讯息。USER 变种亦会由用家物件管家产生来宣布已经处理载入的政策。

MAC_CONFIG_CHANGE

当管理员利用 setsebool 更改 SELinux 两元值时所产生的讯息。

MAC_STATUS

当 SELinux 的状况由 enforcing 转为 permissive 或反向转换。

USER_ROLE_CHANGE

当用户在登入,或通过 sudonewrole(1) 指令更改角色时所产生的讯息。只适用于 基于角色的存取控制

USER_LABEL_EXPORT

当用户透过 CUPS 汇出有标签的物件时所产生的日志。

注:还有一些与 IPSec/NetLabel 相关的 SELinux 审计事件未收录在此。这些事件计有: MAC_UNLBL_ALLOWMAC_UNLBL_STCADDMAC_UNLBL_STCDELMAC_MAP_ADDMAC_MAP_DELMAC_IPSEC_EVENTMAC_CIPSOV4_ADDMAC_CIPSOV4_DEL

纵使 sealert 对理解 AVC 记录有一定的帮助,审计工具能更有效地为管理员展示审计日志。ausearch 可用来搜寻日志内的特定事件,并提供一系列处理审计记录的选项。因此除了以 sealert 来演绎讯息,你更可利用 ausearch 来检视 SELinux 问题的可能成因。排除问题时,我们多数会检视 AVCUSER_AVCSELINUX_ERRUSER_SELINUX_ERRausearch 提供一个 -m 选项来指定以逗号分隔的审计记录过滤类别,还有一个 -i 选项让系统以个别方式演绎数目字为字串。这包括转换 UID/GID 为用户及群组名称,及对照系统函式和架构为系统函式名称:

ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -i

若要搜寻记录并建立一个检查点,以便下次搜寻时不再出现已检查的审计记录:

ausearch --checkpoint="./audit-checkpoint" -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -i

根据进程的 指令名称,即是内核的工作项目的执行名称搜寻记录:

ausearch -c "httpd" -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -i

寻找管理员(或程式)更改 SELinux 强制设定的事件:

ausearch -m MAC_STATUS -i

由于 dontaudit 可防止审计记录出现于日志内,有可能部份 AVCUSER_AVC 不获显示。若是要避免 dontaudit 规则隐藏这些讯息,管理员可执行 semodule -DB 来重建不包含 dontaudit 规则的 SELinux 政策。重建后,日志内将会有更多讯息,还有一些与问题无关的记录(noatsecurerlimitinhsiginh 是执行程式时循例必检查的,因此一般可忽略)。检视完被 dontaudit 隐藏的记录后,请执行 semodule -B 重建含有 dontaudit 规则的政策。

与此刚好相反的就是透过 auditallow 记录成功的操作。这此记录可以通过过滤成功的操作而搜寻到(虽然这样做亦会传回在 permissive 模式下成功的事件):

ausearch -m AVC,USER_AVC -i --success yes

5.2. 重新标签档案

chcon 这个指令可以用来更改一个或多个档案与目录的 SELinux 安全性脉络,正如 'chown' 或 'chmod' 可以用来更改一个档案的拥有者或标准权限。

让我们看一些例子。

就以 Apache 为例,假设你想修改 DocumentRoot 以另一个位置来伺服网页,替代预设的 /var/www/html 目录。譬如说我们在 /html 建立了一个目录(又或者挂载点),然后在那里建立一个 index.html 档:

# mkdir /html
# touch /html/index.html
# ls -Z /html/index.html
-rw-r--r--  root root user_u:object_r:default_t        /html/index.html
# ls -Z | grep html
drwxr-xr-x  root root user_u:object_r:default_t        html 

我们可以见到 /html 这个目录以及 /html/index.html 这个档案都拥有预设的 default_t 安全性脉络类型。如果我们开启浏览器并尝试检视该页,SELinux 将会正确地拒绝它们被存取并记录错误,因为该目录与档案拥有不正确的安全性脉络。我们须要设定供 Apache 使用的 httpd_sys_content_t 正确安全性脉络。

# chcon -v --type=httpd_sys_content_t /html
context of /html changed to user_u:object_r:httpd_sys_content_t
# chcon -v --type=httpd_sys_content_t /html/index.html
context of /html/index.html changed to user_u:object_r:httpd_sys_content_t
# ls -Z /html/index.html
-rw-r--r--  root root user_u:object_r:httpd_sys_content_t    /html/index.html
# ls -Z | grep html
drwxr-xr-x  root root user_u:object_r:httpd_sys_content_t    html 

我们同样也可以利用 -R 这个回递标旗同时将它们的脉络设定:

# chcon -Rv --type=httpd_sys_content_t /html 

以这个方式更改安全性脉络在系统重新开机后仍会获保留,直至该部份档案系统被重新标签。这个动作也算颇常,因此正确的解决方法,就是于测试后编写一条自订的规则(所谓的政策模块),并把它与本地的规则进行合并。它将会是原有的 200 多条规则外的一条规则。要作出永久性、能过渡档案系统重新标签的安全性脉络改动,我们可以采用 SELinux 管理工具,或者在指令行执行 semanage 这个指令:

semanage fcontext -a -t httpd_sys_content_t "/html(/.*)?" 

如此便会将 /html 以下的一切加入 httpd_sys_content_t 这个档案脉络类型。

5.3. 还原预设的安全性脉络

restorecon 这个指令可以用来还原为档案预设的安全性脉络。

让我们再次以 Apache 作为范例。设假有位用户在他的主目录内编辑了一个 index.html 档并将该档案迁移(mv)至 DocumentRoot 的 /var/www/html 内。纵使复制(cp)这个指令普遍会沿用目标目录或档案的安全性脉络,迁移(mv)指令则会保留来源档的安全性脉络。我们可以利用 chcon 这个指令来更改问题档案的安全性脉络,但由于这些档案已经位于 Apache 预设的 DocumentRoot(/var/www/html)内,我们只须还原这个目录或档案的安全性脉络便成了。要单单还原 index.html 档的脉络,我们可以利用:

# restorecon -v /var/www/html/index.html 

如果要以回递的方式还原整个目录的预设安全性脉络:

# restorecon -Rv /var/www/html 

除此之外,如果我们只想检查 /var/www/html 目录内有哪些档案的安全性脉络需要被还原,我们在采用 restorecon 时可以应用 -n 这个标旗来防止重新标签的行动:

# restorecon -Rv -n /var/www/html 

在某些情况下,用户也许移动了拥有 /etc/selinux/targeted/contexts/customizable_types 内的类别的档案。restorecon 会特别对待这些类别,而 restorecon 一般是不会重新标签它们的,除非你额外加上 -F 这个标旗。这些类别要不是与 种类 有关连(见「多重种类保障),就是用户为内容自订的类别。若是要重新标签拥有自订类别的内容,请在执行 restorecon 时加上额外标旗:

# restorecon -RvF /var/www/html 

5.4. 重新标签整个档案系统

有时我们也许会需要将整个档案系统重新标签,虽然这是在启用已停用的 SELinux 时,或在 SELinux 政策由预设的 targeted 改为 strict 时才有真正需要。要在重新开机后自动将整个档案系统重新标签,请执行:

# touch /.autorelabel
# reboot 

假若一个系统在升级至 CentOS-5.2 时停用了 SELinux,然后 SELinux 被启用,重新标签整个档案系统时或许会失败。如果以上的步骤无法正确地将整个档案系统重新标签,请尝试先执行 genhomedircon 这个指令:

# genhomedircon
# touch /.autorelabel
# reboot 

5.5. 允许存取某个连接埠

我们或许会想容让 Apache 连结至某个非标准的埠并聆听对内的连线。SELinux 的政策预设只允许每个服务存取被公认与该服务有关的连接埠。如果我们想容让 Apache 在 tcp 埠 81 上聆听,我们可以利用 semanage 这个指令来新增一条规则批准此事:

# semanage port -a -t http_port_t -p tcp 81 

你可以这样令 SELinux 完整地列出每个服务可存取的连接埠:

# semanage port -l 

5.6. 在 Permissive 模式下搜集审计日志

当一个程式重复地被 SELinux 拒绝某个操作,有时在 permissive 模式下进行侦错会较容易。一般的做法是 setenforce 0,但这样做会导致所有区域都进行 permissive 模式,而不限于有问题的进程。为了避免这个情况,SELinux 支援 permissive 类别,让管理员可选择只将一个区域放进 permissive 模式,而不是整个系统。

若要根据审计日志的记录来设定,请查阅 scontext 栏内的类别:

type=AVC msg=audit(1218128130.653:334): avc: denied { connectto } for pid=9111 comm="smtpd" path="/var/spool/postfix/postgrey/socket"
scontext=system_u:system_r:postfix_smtpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=unix_stream_socket

这个是:postfix_smtpd_t。我们可以利用 semanage 新增一个 permissive 类别,以便能临时允许此区域的所有操作:

semanage permissive -a postfix_smtpd_t

接著我们可观察日志,看看 postfix_smtpd_t 需要那些允准许才能成功地操作。完成后,我们可以再次将该类别还原为 enforcing 模式:

semanage permissive -d postfix_smtpd_t

这样做便可避免 setenforce 0 的霸道手段,并容许系统上的其它服务继续受惠于 SELinux 的存取控制。

5.7. 运用分析工具

很多时候当 SELinux 拒绝某个操作时,该操作其实是政策所允许的,但档案的标签却不正确,或进程未转换至正确的区域。你适宜汇报这些问题给政策的作者或维护者,但利用 setools-console 套件内的分析工具来弄个清楚亦非天方夜谭。

针对审计日志内的某个 AVC 记录,我们可以利用 sesearch 辨认任何允许存取所需目标的强制类别规则,与及经二元值设定后允许存取的规则。AVC 记录中值得留意的部份包括有 scontexttcontext、`tclassdenied: <允许权> 讯息中所征求的权限。

就以 scontext=antivirus_ttcontext=antivirus_ttclass=process 附带 { denied: execmem  讯息的被拒 AVC 记录为例,它很可能源自一个防毒软件尝试进行 JIT 编译,而我们可辨认出允许存取权的强制类别规则及相关二元值:

sesearch -AC -s antivirus_t -t antivirus_t -c process -p execmem
Found 2 semantic av rules:
DT allow antivirus_t antivirus_t : process execmem ; [ antivirus_use_jit ]
DT allow antivirus_t antivirus_t : process execmem ; [ antivirus_use_jit ]

注: 此处所采用的选项已记载于 sesearch(1) 手册内。-s-t 指定来源及目标类别,-c 指定 SELinux 物件级别,-p 可指定要搜寻的规则的不同权限,-A 搜寻允许的规则,而 -C 更延伸为搜寻附带条件的充许规则。

这是一个简单的例子。方括号内的字串是允许存取的二元值名称,而规则的 DT 字首意味著它们现正被停用。我们可以利用 setsebool -P antivirus_use_jit=1 来启用这些规则,但我们也许应先检视这个二元值所允许的权限,而 sesearch 这个工具可再次为我们服务:

sesearch -AC -b antivirus_use_jit
Found 2 semantic av rules:
DT allow antivirus_t antivirus_t : process execmem ; [ antivirus_use_jit ]
DT allow antivirus_t antivirus_t : process execmem ; [ antivirus_use_jit ]

在这个例子中 antivirus_use_jit 这个二元值单单允许我们所需的存取权,但日后我们可以用这个方法,通过 sesearch 来辨认两个提供相同存取权,但其中一个却允许其它不理想操作的二元值。举个例说,我们想写进标签为 httpd_sys_content_t 的 HTTPD 内容。建议的做法是启用 httpd_unified 二元值,因此我们可以检视它能如何协助我们:

sesearch -AC -b httpd_unified -c file -p write
Found 5 semantic av rules:
DT allow httpd_sys_script_t httpdcontent : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_enable_cgi httpd_unified && ]
DT allow httpd_user_script_t httpd_user_content_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_enable_cgi httpd_unified && ]
DT allow httpd_t httpd_sys_rw_content_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_enable_cgi httpd_unified && httpd_builtin_scripting && ]
DT allow httpd_user_script_t httpd_user_ra_content_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_enable_cgi httpd_unified && ]
DT allow httpd_t httpdcontent : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_enable_cgi httpd_unified && httpd_builtin_scripting && ]

由于只有少量与此二元值有关的规则是关乎写入档案的,因此要分析它所允许的是很容易。上述规则告诉我们虽然此二元值允许我们写进 AVC 记录内涉及的档案,它亦容许我们读写本来只支援增生及唯读的内容。从上面我们看见有一个 httpd_sys_rw_content_t 类别,根据其名称很有机会能解决我们的问题。然而,我们亦可以从中发现 httpd_t 可写进的档案类别。留意 httpd 间接地透过属性或停用的二元值有可能写进很多不同类别的内容,因此我们利用 -R 表示我们所提供的称号是正规表示式,藉此过滤以 httpd 起首的 目标 类别。

sesearch -ACR -s httpd_t -t "httpd.*" -c file -p write
DT allow httpd_t httpd_sys_rw_content_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_enable_cgi httpd_unified && httpd_builtin_scripting && ]
ET allow httpd_t httpd_sys_rw_content_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; [ httpd_builtin_scripting ]
DT allow httpd_t httpd_sys_rw_content_t : lnk_file { ioctl read write create getattr setattr lock append unlink link rename } ; [ httpd_enable_cgi httpd_unified && httpd_builtin_scripting && ]
ET allow httpd_t httpd_sys_rw_content_t : lnk_file { ioctl read write create getattr setattr lock append unlink link rename } ; [ httpd_builtin_scripting ]
ET allow httpd_t httpd_sys_rw_content_t : sock_file { read write getattr append open } ; [ httpd_builtin_scripting ]

这里有数条规则已停用,但其中 3 条预设是启用的。因此根据这一切我们可归纳在 HTTPD 区域中,读写内容的类别应是 httpd_sys_rw_content_t。带著这份见解,我们可以沿用相同程序辨别出那些区域能存取其它目标类别,这有助辨认出那些程式在错误的脉络下运行。

6. 自订 SELinux 政策

透过设定选项的二元值,你可以微调 SELinux 政策而不必重新编译政策的源代码。这些选项包括允许用户在 Samba 下分享他们的主目录,或者允许 Apache 从用户的主目录伺服档案。否则这些都会被 SELinux 政策所拒绝。

有另一版 Wiki 页是关于二元值的。

7. 利用 audit2allow 建立自订 SELinux 政策模块

在某些情形下,上述方法都无法解决问题,而我们必须通过建立自订的政策模块来延伸 SELinux 政策,允许一组特定的状态出理。其中一个例子就是在 smtp 邮件伺服器上增加 postgrey 服务。我们的 smtp 伺服器须要透过一个 Unix 通讯端与 postgrey 沟通,但预设的 SELinux 政策却禁止我们的 smtp 伺服器如此做。结果该服务会被 SELinux 所封锁。这个问题无法透过更改或还原档案的安全性脉络来解决,也没有可供切换二元值。我们可以通过一个二元值来停止 SELinux 保护 smtp 伺服器,这样总比完全停用 SELinux 好,但不太理想。

如果我们将 SELinux 切换至 Permissive 模式并让邮件伺服器运行一段时间,我们便可以在允许存取的情况下记录 SELinux 的问题(正如 在 Permissive 模式收集审计日志 所提及)。检视日志时,我们会看见以下 SELinux AVC 信息:

type=AVC msg=audit(1218128130.653:334): avc:  denied  { connectto } for  pid=9111 comm="smtpd" path="/var/spool/postfix/postgrey/socket"
scontext=system_u:system_r:postfix_smtpd_t:s0 tcontext=system_u:system_r:initrc_t:s0 tclass=unix_stream_socket
type=AVC msg=audit(1218128130.653:334): avc:  denied  { write } for  pid=9111 comm="smtpd" name="socket" dev=sda6 ino=39977017
scontext=system_u:system_r:postfix_smtpd_t:s0 tcontext=system_u:object_r:postfix_spool_t:s0 tclass=sock_file 

接著我们可以用 audit2allow 来产生一组允许所需行动的政策规则。我们我可建立一个本地的 postgrey 强制类别政策档(postgreylocal.te):

# grep smtpd_t /var/log/audit/audit.log | audit2allow -m postgreylocal > postgreylocal.te
# cat postgreylocal.te
module postgreylocal 1.0;
require {
        type postfix_smtpd_t;
        type postfix_spool_t;
        type initrc_t;
        class sock_file write;
        class unix_stream_socket connectto;
}
#============= postfix_smtpd_t ==============
allow postfix_smtpd_t initrc_t:unix_stream_socket connectto;
allow postfix_smtpd_t postfix_spool_t:sock_file write; 

在上面我们看见如何从 audit.log 筛选有关 smtp 伺服器的问题,并将这些问题导向 audit2allow,让它产生一组规则,是它认为可用来允许被 SELinux 政策所封锁的行动。查阅这些规则时,我们可发现该 smtp 伺服器想连线及写进一个 Unix 通讯端,而从日志里我们看见这个 Unix 通讯端正正是 postgrey 服务所聆听的那个。既然这一些都合情合理,我们可以续继用 audit2allow 建立一个自订的政策模块,允许这些行动:

# grep smtpd_t /var/log/audit/audit.log | audit2allow -M postgreylocal 

接著我们利用 semodule 这个指令将我们的 postgrey 政策模块载入现有的 SELinux 政策内:

semodule -i postgreylocal.pp 

如此便会将我们的 postgrey 政策模块新增到 /etc/selinux/targeted/modules/active/modules/postgreylocal.pp。我们可以透过 semodule -l 来检查该政策模块已被正确地载入。

然后我们可以继续监视 SELinux 的日志来确定自订的政策模块有效用。满意时,我们便可以重新启用 SELinux 的 Enforcing 模式,让功能已全备的 smtp 伺服器再次享有 SELinux 的保障。

7.1. 手动式自订政策模块

audit2allow 在大多数情况下都可以自动建立一个自订政策模块来解决某个特定问题,但有时它未能完全正确,而我们也许会想以人手编辑并编译该政策模块。就以下列的 AVC 审计日志为例:

Summary:
SELinux is preventing postdrop (postfix_postdrop_t) "getattr" to
/var/log/httpd/error_log (httpd_log_t).
Detailed Description:
SELinux denied access requested by postdrop. It is not expected that this access
is required by postdrop and this access may signal an intrusion attempt. It is
also possible that the specific version or configuration of the application is
causing it to require additional access.
Allowing Access:
Sometimes labeling problems can cause SELinux denials. You could try to restore
the default system file context for /var/log/httpd/error_log,
restorecon -v '/var/log/httpd/error_log'
If this does not work, there is currently no automatic way to allow this access.
Instead, you can generate a local policy module to allow this access - see FAQ
(http://fedora.redhat.com/docs/selinux-faq-fc5/#id2961385) Or you can disable
SELinux protection altogether. Disabling SELinux protection is not recommended.
Please file a bug report (http://bugzilla.redhat.com/bugzilla/enter_bug.cgi)
against this package.
Additional Information:
Source Context                system_u:system_r:postfix_postdrop_t
Target Context                root:object_r:httpd_log_t
Target Objects                /var/log/httpd/error_log [ file ]
Source                        postdrop
Source Path                   /usr/sbin/postdrop
Port                          <Unknown>
Host                          sanitized
Source RPM Packages           postfix-2.3.3-2
Target RPM Packages
Policy RPM                    selinux-policy-2.4.6-137.1.el5
Selinux Enabled               True
Policy Type                   targeted
MLS Enabled                   True
Enforcing Mode                Enforcing
Plugin Name                   catchall_file
Host Name                     sanitized
Platform                      Linux sanitized 2.6.18-53.1.21.el5 #1 SMP Tue
                              May 20 09:35:07 EDT 2008 x86_64 x86_64
Alert Count                   599
First Seen                    Wed Jul  2 08:27:15 2008
Last Seen                     Sun Aug 10 22:47:52 2008
Local ID                      c303a4ea-8e7a-4acc-9118-9cc61c6a2ec8
Line Numbers
Raw Audit Messages
host=sanitized type=AVC msg=audit(1218397672.372:352): avc:  denied  { getattr } for  pid=4262 comm="postdrop"
path="/var/log/httpd/error_log" dev=md2 ino=117005 scontext=system_u:system_r:postfix_postdrop_t:s0
tcontext=root:object_r:httpd_log_t:s0 tclass=file
host=sanitized type=SYSCALL msg=audit(1218397672.372:352): arch=c000003e syscall=5 success=no exit=-13 a0=2
a1=7fffd6febca0 a2=7fffd6febca0 a3=0 items=0 ppid=4261 pid=4262 auid=4294967295 uid=48 gid=48 euid=48 suid=48
fsuid=48 egid=90 sgid=90 fsgid=90 tty=(none) comm="postdrop" exe="/usr/sbin/postdrop"
subj=system_u:system_r:postfix_postdrop_t:s0 key=(null) 

就上述错误执行 audit2allow 并查阅所得的 postfixlocal.te 政策档时,我们会看见:

# grep postdrop /var/log/audit/audit.log | audit2allow -M postfixlocal
# cat postfixlocal.te
    module postfixlocal 1.0;
    require {
            type httpd_log_t;
            type postfix_postdrop_t;
            class dir getattr;
            class file { read getattr };
    }
    #============= postfix_postdrop_t ==============
    allow postfix_postdrop_t httpd_log_t:file getattr; 

我希望第一件引起你关注的事,就是 postdrop 为何须要存取 /var/log/httpd/error_log?这应该在我们预料之外,因此我们便要评估应否允许这个行动。我们有数个选择:我们可以忽略这个错误并容让 SELinux 继续封锁及记录这些存取的行动,又或者我们可以允许这些行动并建立 audit2allow 所建议的自订政策模块。我们也可以选择编辑这个自订政策模块的 .te 档并防止审计这个特定错误,并让 SELinux 继续封锁该存取权。我们可以编辑 allow 这一行,并改为 dontaudit

    #============= postfix_postdrop_t ==============
    dontaudit postfix_postdrop_t httpd_log_t:file getattr; 

现在我们可以手动地编译及载入已修改的自订政策模块:

# checkmodule -M -m -o postfixlocal.mod postfixlocal.te
# semodule_package -o postfixlocal.pp -m postfixlocal.mod
# semodule -i postfixlocal.pp 

postdrop 存取 /var/log/httpd/error_log 时仍然会被 SELinux 封锁,但我们再不会在发生封锁时接收到警报,或在日志内填满错误信息。

8. 政策文档

很多 SELinux 政策是利用 GNU m4 巨集编写出来的,亦正是如此,你必须利用 devel 套件来建立那些依赖现有政策 API 的新模块。若要列出已安装的政策下的巨集及模块,你可安装 selinux-policy-doc 套件把 HTML 文档放进 /usr/share/doc/selinux-policy/html/。它亦为每个 SELinux 模块提供说明,当中包含每个设定值的简介,档案脉络及模块所定义的类别。说明页的名称采用 <模块>_selinux 这个格式。举例说,HTTPD 模块的文档名为 httpd_selinux(8),而 audit administrator 这个角色被收录于 auditadm_selinux(8)

9. 总结

这篇文章尝试向 SELinux 的新用户简介如何应用它。SELinux 预设会被安装及启用,对多数用户来说,它会默默地提高系统的安全性。SELinux 适用于各类型的安装,包括:伺服器、工作台、桌面电脑、及笔记型电脑。

虽然 SELinux 对不熟识它的用户来说似乎很复杂及令人生畏,但这不是在安装时停用它的一个理由。若果 SELinux 确实带来问题,你可以简单地将它切换至 Permissive 模式,到时它便会将问题记录下来,而不会进行封锁。当问题出现时,这篇文章所示范的技巧可以用来排除疑难及解决它们。

10. 额外资源

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide/

http://fedoraproject.org/wiki/SELinux

http://docs.fedoraproject.org/en-US/Fedora/13/html-single/Security-Enhanced_Linux/

http://danwalsh.livejournal.com/

11. 用户备注及陷阱

此部份由一位靠此页学懂 SELinux 的用户所提供。此文档是一个优越及详尽的资源。不过,它有点枯燥。它违漏了数个实际的重点,令我尝试正式做事时感到很沮丧。留意这里针对 CentOS 6。

  1. semanage 收录在 policycoreutils-python 这个套件内,它预设是不被安装的。留意 policycoreutils 是另一个套件。CentOS 7 似乎已经预设安装 semanage。它依然位于 policycoreutils-python 套件内。

  2. 在管理系统时,寻找适用的脉络是困难的。其中一个起始点是 ls -Z。检视一个套件所安装的目录及数据,然后复制所采用的脉络。下一个工具是 seinfo -t,它会列出系统现正应用的所有脉络。grep 你的应用程式的名称。

  3. 某些情况可透过运用 public_content_rw_t 脉络来解决。一位用户有个目录要由 NFS、Samba 及 Apache 共用。此脉络允许这样做。它也可构成一个安全性漏洞,因此在重视安全的系统上请特别留意。

  4. 不要忘记 chcon 的 -t 参变。它单单设定脉络种类,这也是你普要做的事情,而且较指定 ls -Z 所汇报的整句字串更为容易。

  5. 应用 audit2allow 比这里的介绍来得简单。当两个脉络出现冲突时,请从 audit.log 找出错误信息并将它们放入独立的文字档内。然后这样把错误传给 audit2allow

audit2allow -M mynewpolicyname <errors.txt

module mynewpolicy 1.0;

require {
        type spamc_t;
        type postfix_pipe_tmp_t;
        class file { read write };
}

#============= spamc_t ==============
allow spamc_t postfix_pipe_tmp_t:file { read write };

Translation of revision 39

zh/HowTos/SELinux (last edited 2017-06-27 13:16:35 by TimothyLee)