龙之介大人

SELinux的基本使用
16.5 什么是SELinux从进入了 CentOS 5.x 之后的 CentOS 版本中 (当然包括 CentO...
扫描右侧二维码阅读全文
30
2019/10

SELinux的基本使用

16.5 什么是SELinux

从进入了 CentOS 5.x 之后的 CentOS 版本中 (当然包括 CentOS 7),SELinux 已经是个非常完备的核心模块了!尤其 CentOS 提供了很多管理 SELinux 的指令与机制,因此在整体架构上面是单纯且容易操作管理的!所以,在没有自行开发网络服务软件以及使用其他第三方协力软件的情况下,也就是全部使用 CentOS 官方提供的软件来使用我们服务器的情况下,建议大家不要关闭 SELinux ! 让我们来仔细的玩玩这家伙吧!

什么是 SELinux 呢?其实他是Security Enhanced Linux的缩写,字面上的意义就是安全强化的 Linux 之意!那么所谓的『安全强化』是强化哪个部分? 是网络资安还是权限管理?底下就让我们来谈谈吧!

16.5.1 当初设计的目标:避免资源的误用

SELinux 是由美国国家安全局 (NSA) 开发的,当初开发这玩意儿的目的是因为很多企业界发现,通常系统出现问题的原因大部分都在于『内部员工的资源误用』所导致的,实际由外部发动的攻击反而没有这么严重。那么什么是『员工资源误用』呢?举例来说,如果有个不是很懂系统的系统管理员为了自己设定的方便,将网页所在目录 /var/www/html/ 的权限设定为 drwxrwxrwx 时,你觉得会有什么事情发生?

现在我们知道所有的系统资源都是透过进程来进行存取的,那么 /var/www/html/ 如果设定为 777 ,代表所有进程均可对该目录存取,万一你真的有启动 WWW 服务器软件,那么该软件所触发的进程将可以写入该目录,而该进程却是对整个 Internet 提供服务的!只要有心人接触到这支进程,而且该进程刚好又有提供用户进行写入的功能,那么外部的人很可能就会对你的系统写入些莫名其妙的东西!那可真是不得了!一个小小的 777 问题可是大大的!

为了控管这方面的权限与进程的问题,所以美国国家安全局就着手处理操作系统这方面的控管。由于 Linux 是自由软件,程序代码都是公开的,因此她们便使用 Linux 来作为研究的目标,最后更将研究的结果整合到 Linux 核心里面去,那就是 SELinux 啦!所以说,SELinux 是整合到核心的一个模块!更多的 SELinux 相关说明可以参考:

这也就是说:其实 SELinux 是在进行进程、文件等细部权限设定依据的一个核心模块! 由于启动网络服务的也是进程,因此刚好也能够控制网络服务能否存取系统资源的一道关卡!所以,在讲到 SELinux 对系统的访问控制之前,我们得先来回顾一下之前谈到的系统文件权限与用户之间的关系。因为先谈完这个你才会知道为何需要 SELinux 的啦!

16.5.2 传统的文件权限与账号关系:自主式访问控制,DAC

我们第十三章的内容,知道系统的账号主要分为系统管理员 (root) 与一般用户,而这两种身份能否使用系统上面的文件资源则与 rwx 的权限设定有关。 不过你要注意的是,各种权限设定对 root 是无效的。因此,当某个进程想要对文件进行存取时,系统就会根据该进程的拥有者/群组,并比对文件的权限,若通过权限检查,就可以存取该文件了。

这种存取文件系统的方式被称为『自主式访问控制 (Discretionary Access Control, DAC)』,基本上,就是依据进程的拥有者与文件资源的 rwx 权限来决定有无存取的能力。不过这种 DAC 的访问控制有几个困扰,那就是:

  • root 具有最高的权限:如果不小心某个进程被有心人士取得,且该进程属于 root 的权限,那么这支进程就可以在系统上进行任何资源的存取!
  • 使用者可以取得进程来变更文件资源的访问权限:如果你不小心将某个目录的权限设定为 777 ,由于对任何人的权限会变成 rwx ,因此该目录就会被任何人所任意存取!

这些问题是非常严重的!尤其是当你的系统是被某些漫不经心的系统管理员所掌控时!她们甚至觉得目录权限调为 777 也没有什么了不起的危险...

16.5.3 以政策规则订定特定进程读取特定文件:委任式访问控制,MAC

现在我们知道 DAC 的困扰就是当使用者取得进程后,他可以藉由这支进程与自己预设的权限来处理他自己的文件资源。万一这个用户对 Linux 系统不熟,那就很可能会有资源误用的问题产生。为了避免 DAC 容易发生的问题,因此 SELinux 导入了委任式访问控制 (Mandatory Access Control, MAC) 的方法!

委任式访问控制 (MAC) 有趣啦!他可以针对特定的进程与特定的文件资源来进行权限的控管! 也就是说,即使你是 root ,那么在使用不同的进程时,你所能取得的权限并不一定是 root ,而得要看当时该进程的设定而定。如此一来,我们针对控制的『主体』变成了『进程』而不是使用者! 此外,这个主体进程也不能任意使用系统文件资源,因为每个文件资源也有针对该主体进程设定可取用的权限! 如此一来,控件目就细的多了!但整个系统进程那么多、文件那么多,一项一项控制可就没完没了! 所以 SELinux 也提供一些预设的政策 (Policy) ,并在该政策内提供多个规则 (rule) 让你可以选择是否启用该控制规则!

在委任式访问控制的设定下,我们的进程能够活动的空间就变小了!举例来说, WWW 服务器软件的达成进程为 httpd 这支程序,而默认情况下,httpd 仅能在 /var/www/ 这个目录底下存取文件,如果 httpd 这个进程想要到其他目录去存取数据时,除了规则设定要开放外,目标目录也得要设定成 httpd 可读取的模式 (type) 才行喔!限制非常多! 所以,即使不小心 httpd 被 cracker 取得了控制权,他也无权浏览 /etc/shadow 等重要的配置文件!

简单的来说,针对 Apache 这个 WWW 网络服务使用 DAC 或 MAC 的结果来说,两者间的关系可以使用下图来说明。底下这个图示取自 Red Hat 训练教材.

左图是没有 SELinux 的 DAC 存取结果,apache 这只 root 所主导的进程,可以在这三个目录内作任何文件的新建与修改~ 相当麻烦~右边则是加上 SELinux 的 MAC 管理的结果,SELinux 仅会 针对 Apache 这个『 process 』放行部份的目录, 其他的非正规目录就不会放行给 Apache 使用! 因此不管你是谁,就是不能穿透 MAC 的框框!

16.6 SELinux的运作模式

再次的重复说明一下,SELinux 是透过 MAC 的方式来控管进程,他控制的主体是进程,而目标则是该进程能否读取的『文件资源』!所以先来说明一下这些东西的相关性!

  • 主体 (Subject):

    • SELinux 主要想要管理的就是进程,因此你可以将『主体』跟本章谈到的 process 划上等号;
  • 目标 (Object):

    • 主体进程能否存取的『目标资源』一般就是文件系统。因此这个目标项目可以等文件系统划上等号;
  • 政策 (Policy):
由于进程与文件数量庞大,因此 SELinux 会依据某些服务来制订基本的存取安全性政策。这些政策内还会有详细的规则 (rule) 来指定不同的服务开放某些资源的存取与否。在目前的 CentOS 7.x 里面仅有提供三个主要的政策,分别是:
+ targeted:针对网络服务限制较多,针对本机限制较少,是预设的政策;
+ minimum:由 target 修订而来,仅针对选择的进程来保护! 
+ mls:完整的 SELinux 限制,限制方面较为严格。

建议使用预设的 targeted 政策即可。

  • 安全性本文 (security context):

我们刚刚谈到了主体、目标与政策面,但是主体能不能存取目标除了政策指定之外,主体与目标的安全性本文必须一致才能够顺利存取。这个安全性本文 (security context) 有点类似文件系统的 rwx !安全性本文的内容与设定是非常重要的! 如果设定错误,你的某些服务(主体进程)就无法存取文件系统(目标资源),当然就会一直出现『权限不符』的错误讯息了!

由于 SELinux 重点在保护进程读取文件系统的权限,因此我们将上述的几个说明搭配起来,绘制成底下的流程图,比较好理解:

上图的重点在『主体』如何取得『目标』的资源访问权限! 由上图我们可以发现,(1)主体进程必须要通过 SELinux 政策内的规则放行后,就可以与目标资源进行安全性本文的比对,(2)若比对失败则无法存取目标,若比对成功则可以开始存取目标。问题是,最终能否存取目标还是与文件系统的 rwx 权限设定有关喔!如此一来,加入了 SELinux 之后,出现权限不符的情况时,你就得要一步一步的分析可能的问题了!

安全性本文 (Security Context)

CentOS 7.x 的 target 政策已经帮我们制订好非常多的规则了,因此你只要知道如何开启/关闭某项规则的放行与否即可。那个安全性本文比较麻烦!因为你可能需要自行配置文件案的安全性本文呢! 为何需要自行设定啊? 举例来说,你不也常常进行文件的 rwx 的重新设定吗?这个安全性本文你就将他想成 SELinux 内必备的 rwx 就是了!这样比较好理解啦。

安全性本文存在于主体进程中与目标文件资源中。进程在内存内,所以安全性本文可以存入是没问题。那文件的安全性本文是记录在哪里呢?事实上,安全性本文是放置到文件的 inode 内的,因此主体进程想要读取目标文件资源时,同样需要读取 inode,这 inode 内就可以比对安全性本文以及 rwx 等权限值是否正确,而给予适当的读取权限依据。

那么安全性本文到底是什么样的存在呢?我们先来看看 /root 底下的文件的安全性本文好了。观察安全性本文可使用『 ls -Z 』去观察如下:(注意:你必须已经启动了 SELinux 才行!若尚未启动,这部份请稍微看过一遍即可。底下会介绍如何启动 SELinux!)

[root@study ~]# ls -Z
-rw-------. root root system_u:object_r:admin_home_t:s0 anaconda-ks.cfg
drwx------. root root unconfined_u:object_r:admin_home_t:s0 backups
-rw-------. root root system_u:object_r:admin_home_t:s0 initial-setup-ks.cfg
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 run1.txt
#上述特殊字体的部分,就是安全性本文的内容!仅列出数个预设的文件而已,学习过程中所写下的文件则没有列在上头喔!

如上所示,安全性本文主要用冒号分为三个字段,这三个字段的意义为:

Identify:role:type
身份识别:角色:类型
这三个字段的意义仔细的说明一下吧:
  • 身份识别 (Identify):相当于账号方面的身份识别!主要的身份识别常见有底下几种常见的类型:

    • unconfined_u:不受限的用户,也就是说,该文件来自于不受限的进程所产生的!一般来说,我们使用可登入账号来取得 bash 之后,预设的 bash 环境是不受 SELinux 管制的~因为 bash 并不是什么特别的网络服务!因此,在这个不受 SELinux 所限制的 bash 进程所产生的文件,其身份识别大多就是 unconfined_u 这个『不受限』用户啰!
    • system_u:系统用户,大部分就是系统自己产生的文件!
基本上,如果是系统或软件本身所提供的文件,大多就是 system\_u 这个身份名称,而如果是我们用户透过 bash 自己建立的文件,大多则是不受限的 unconfined\_u 身份~如果是网络服务所产生的文件,或者是系统服务运作过程产生的文件,则大部分的识别就会是 system_u 啰!
因为这边教大家使用文字界面来产生许多的数据,因此你看上面的三个文件中,系统安装主动产生的 anaconda-ks.cfs 及 initial-setup-ks.cfg 就会是 system\_u,而我们自己从网络上面抓下来的 run1.txt 就会是 unconfined\_u 这个识别啊!
  • 角色 (Role):透过角色字段,我们可以知道这个资料是属于进程、文件资源还是代表使用者。一般的角色有:

    • object_r:代表的是文件或目录等文件资源,这应该是最常见的;
    • system_r:代表的就是进程!不过,一般使用者也会被指定成为 system_r !
你也会发现角色的字段最后面使用『 _r 』来结尾!因为是 role 的意思!
  • 类型 (Type) (最重要!):

    在预设的 targeted 政策中, Identify 与 Role 字段基本上是不重要的!重要的在于这个类型 (type) 字段! 基本上,一个主体进程能不能读取到这个文件资源,与类型字段有关!而类型字段在文件与进程的定义不太相同,分别是:

    • type:在文件资源 (Object) 上面称为类型 (Type);
    • domain:在主体进程 (Subject) 则称为领域 (domain) 了!
    • domain 需要与 type 搭配,则该进程才能够顺利的读取文件资源!

进程与文件 SELinux type 字段的相关性

那么这三个字段如何利用呢?首先我们来瞧瞧主体进程在这三个字段的意义为何!透过身份识别与角色字段的定义,我们可以约略知道某个进程所代表的意义!先来动手瞧一瞧目前系统中的进程在 SELinux 底下的安全本文为何?

#观察一下系统『进程的 SELinux 相关信息』
[root@study modules]# ps -eZ
LABEL                             PID TTY          TIME CMD
system_u:system_r:init_t:s0         1 ?        00:00:04 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
....
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 24678 pts/0 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 24709 pts/0 00:00:00 bash

#基本上进程主要就分为两大类,一种是系统有受限的 system_u:system_r,另一种则可能是用户自己的, 
#比较不受限的进程 (通常是本机用户自己执行的程序),亦即是 unconfined_u:unconfined_r 这两种!
基本上,这些对应资料在 targeted 政策下的对应如下:
身份识别角色该对应在 targeted 的意义
unconfined_uunconfined_r一般可登入使用者的进程啰!比较没有受限的进程之意!大多数都是用户已经顺利登入系统 (不论是网络还是本机登入来取得可用的 shell) 后,所用来操作系统的进程!如 bash, X window 相关软件等.
system_usystem_r由于为系统账号,因此是非交谈式的系统运作进程,大多数的系统进程均是这种类型!

但就如上所述,在预设的 target 政策下,其实最重要的字段是类型字段 (type),主体与目标之间是否具有可以读写的权限,与进程的 domain 及文件的 type 有关!这两者的关系我们可以使用 crond 以及他的配置文件来说明! 亦即是 /usr/sbin/crond, /etc/crontab, /etc/cron.d 等文件来说明。首先,看看这几个咚咚的安全性本文内容先:

#1.先看看 crond 这个『进程』的安全本文内容:
[root@study ~]# ps -eZ | grep cron
system_u:system_r:crond_t:s0-s0:c0.c1023 1527 ? 00:00:00 crond
system_u:system_r:crond_t:s0-s0:c0.c1023 1547 ? 00:00:00 atd
#这个安全本文的类型名称为 crond_t 格式!


#2.再来看看执行档、配置文件等等的安全本文内容!
[root@study ~]# ll -Zd /usr/sbin/crond  /etc/crontab /etc/cron.d/
drwxr-xr-x. root root system_u:object_r:system_cron_spool_t:s0 /etc/cron.d/
-rw-r--r--. root root system_u:object_r:system_cron_spool_t:s0 /etc/crontab
-rwxr-xr-x. root root system_u:object_r:crond_exec_t:s0 /usr/sbin/crond

当我们执行 /usr/sbin/crond 之后,这个程序变成的进程的 domain 类型会是 crond_t 这一个~而这个 crond_t 能够读取的配置文件则为 system_cron_spool_t 这种的类型。因此不论 /etc/crontab, /etc/cron.d 以及 /var/spool/cron 都会是相关的 SELinux 类型 (/var/spool/cron 为 user_cron_spool_t)。 文字看起来不太容易了解,我们使用图示来说明这几个东西的关系!

  • 上图的意义我们可以这样看的:
  1. 首先,我们触发一个可执行的目标文件,那就是具有 crond_exec_t 这个类型的 /usr/sbin/crond 文件;
  2. 该文件的类型会让这个文件所造成的主体进程 (Subject) 具有 crond 这个领域 (domain),我们的规则针对这个领域已经制定了许多规则,其中包括这个领域可以读取的目标资源类型;
  3. 由于 crond domain 被设定为可以读取 system_cron_spool_t 这个类型的目标文件 (Object), 因此你的配置文件放到 /etc/cron.d/ 目录下,就能够被 crond 那支进程所读取了;
  4. 但最终能不能读到正确的资料,还得要看 rwx 是否符合 Linux 权限的规范!

上述的流程告诉我们几个重点,第一个是规则内需要制订详细的 domain/type 相关性;第二个是若文件的 type 设定错误,那么即使权限设定为 rwx 全开的 777,该主体进程也无法读取目标文件资源的!不过如此一来,也就可以避免用户将他的家目录设定为 777 时所造成的权限困扰。

  • 让我们来做个测试练习!就是,万一你的 crond 配置文件的 SELinux 并不是 system_cron_spool_t 时,该配置文件真的可以顺利的被读取运作吗?来看看底下的范例!
#1. 先假设你因为不熟的缘故,因此是在『root 家目录』建立一个如下的 cron 设定:
[root@study ~]# vim checktime
10 * * * * root sleep 60s

#2. 检查后才发现文件放错目录了,又不想要保留副本,因此使用 mv 移动到正确目录:
[root@study ~]# mv checktime /etc/cron.d/checktime
[root@study ~]# ll /etc/cron.d/checktime     <--权限都正常:644 任何进程都可以正常读取
-rw-r--r--. 1 root root 27 10月 27 17:57 /etc/cron.d/checktime

#3. 强制重新启动 crond ,然后偷看一下登录日志,看看有没有问题发生!
[root@study ~]# systemctl restart crond
[root@study ~]# tail /var/log/cron
Oct 27 18:01:06 study crond[25449]: ((null)) Unauthorized SELinux context=system_u:system_r:system_cronjob_t:s0-s0:c0.c1023 file_context=unconfined_u:object_r:admin_home_t:s0 (/etc/cron.d/checktime)
Oct 27 18:01:06 study crond[25449]: (root) FAILED (loading cron table)
#上面的意思是,有错误!因为原本的安全本文与文件的实际安全本文无法搭配的缘故!

从上面的测试案例来看,我们的配置文件确实没有办法被 crond 这个服务所读取喔!而原因在登录档内就有说明,主要就是来自 SELinux 安全本文 (context) type 的不同所致喔!没办法读.

16.7 SELinux 三种模式的启动、关闭与观察

并非所有的 Linux distributions 都支持 SELinux 的,所以你必须要先观察一下你的系统版本为何! 这里介绍的 CentOS 7.x 本身就有支持 SELinux!所以你不需要自行编译 SELinux 到你的 Linux 核心中! 目前 SELinux 依据启动与否,共有三种模式,分别如下:

  • enforcing:强制模式,代表 SELinux 运作中,且已经正确的开始限制 domain/type 了;
  • permissive:宽容模式:代表 SELinux 运作中,不过仅会有警告讯息并不会实际限制 domain/type 的存取。这种模式可以运来作为 SELinux 的 debug 之用;
  • disabled:关闭,SELinux 并没有实际运作。

这三种模式跟图 16.5.2所说的关系如何呢?我们前面不是谈过主体进程需要经过政策规则、安全本文比对之后,加上 rwx 的权限规范,若一切合理才会让进程顺利的读取文件吗?那么这个 SELinux 的三种模式与上面谈到的政策规则、安全本文的关系为何呢?我们还是使用图标加上流程来让大家理解一下:

就如上图所示,首先,你得要知道,并不是所有的进程都会被 SELinux 所管制,因此最左边会出现 一个所谓的『有受限的进程主体』!那如何观察有没有受限 (confined )呢? 很简单啊!就透过 ps -eZ 去截取!举例来说,我们来找一找 crond 与 bash 这两只进程是否有被限制吧?

[root@study ~]# ps -eZ | grep -E 'cron|bash'
system_u:system_r:crond_t:s0-s0:c0.c1023 1547 ? 00:00:00 atd
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 23607 pts/0 00:00:00 bash
system_u:system_r:crond_t:s0-s0:c0.c1023 25449 ? 00:00:00 crond
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 27402 pts/0 00:00:00 bash
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 27433 pts/0 00:00:00 bash

如前所述,因为在目前 target 这个政策底下,只有第三个类型 (type) 字段会有影响,因此我们上表仅列出第三个字段的数据而已。我们可以看到,crond 确实是有受限的主体进程,而 bash 因为是本机进程,因此就是不受限 (unconfined_t) 的类型!也就是说,bash 是不需要经过上图的流程,而是直接去判断 rwx 而已~。

了解了受限的主体进程的意义之后,再来了解一下,三种模式的运作吧!首先,如果是 Disabled 的模式,那么 SELinux 将不会运作,当然受限的进程也不会经过 SELinux , 也是直接去判断 rwx 而已。那如果是宽容 (permissive) 模式呢?这种模式也是不会将主体进程抵挡 (所以箭头是可以直接穿透的喔!),不过万一没有通过政策规则,或者是安全本文的比对时,那么该读写动作将会被纪录起来 (log),可作为未来检查问题的判断依据。

至于最终那个 Enforcing 模式,就是实际将受限主体进入规则比对、安全本文比对的流程,若失败,就直接抵挡主体进程的读写行为,并且将他记录下来。如果通通没问题,这才进入到 rwx 权限的判断喔!这样可以理解三种模式的行为了吗?

  • 那你怎么知道目前的 SELinux 模式呢?就透过 getenforce !
[root@study ~]# getenforce 
Enforcing        <--显示出目前的模式为 Enforcing

另外,我们又如何知道 SELinux 的政策 (Policy) 为何呢?这时可以使用 sestatus 来观察:

[root@study ~]# sestatus [-vb]
选项与参数:
-v :检查列于 /etc/sestatus.conf 内的文件与进程的安全性本文内容;
-b :将目前政策的规则布尔值列出,亦即某些规则 (rule) 是否要启动 (0/1) 之意


#范例一:列出目前的 SELinux 使用哪个政策 (Policy)?
[root@study ~]# sestatus
SELinux status:                 enabled            <--否启动 SELinux
SELinuxfs mount:                /sys/fs/selinux    <--SELinux 的相关文件数据挂载点
SELinux root directory:         /etc/selinux      <--SELinux 的根目录所在
Loaded policy name:             targeted        <--目前的政策为何?
Current mode:                   enforcing        <--目前的模式
Mode from config file:          enforcing        <--目前配置文件内规范的 SELinux 模式
Policy MLS status:              enabled            <--是否含有 MLS 的模式机制
Policy deny_unknown status:     allowed            <--是否预设抵挡未知的主体进程
Max kernel policy version:      28

如上所示,目前是启动的,而且是 Enforcing 模式,而由配置文件查询得知亦为 Enforcing 模式。 此外,目前的预设政策为 targeted 这一个。你应该要有疑问的是,SELinux 的配置文件是哪个文件啊? 其实就是 /etc/selinux/config 这个文件喔!我们来看看内容:

[root@study ~]# vim /etc/selinux/config 
SELINUX=enforcing    <--调整 enforcing|disabled|permissive
SELINUXTYPE=targeted <--目前仅有 targeted, mls, minimum 三种政策

若有需要修改预设政策的话,就直接改 SELINUX=enforcing 那一行即可喔!

16.7.1 SELinux 的启动与关闭

上面是默认的政策与启动的模式!你要注意的是,如果改变了政策则需要重新启动;如果由 enforcing 或 permissive 改成 disabled ,或由 disabled 改成其他两个,那也必须要重新启动。这是因为 SELinux 是整合到核心里面去的,你只可以在 SELinux 运作下切换成为强制 (enforcing) 或宽容 (permissive) 模式,不能够直接关闭 SELinux 的! 如果刚刚你发现 getenforce 出现 disabled 时,请到上述文件 修改成为 enforcing 然后重新启动吧!

不过你要注意的是,如果从 disable 转到启动 SELinux 的模式时,由于系统必须要针对文件写入安全性本文的信息,因此开机过程会花费不少时间在等待重新写入 SELinux 安全性本文 (有时也称为 SELinux Label),而且在写完之后还得要再次的重新启动一次喔!你必须要等待粉长一段时间! 等到下次开机成功后,再使用 getenforce 或 sestatus 来观察看看有否成功的启动到 Enforcing 的模式!

如果你已经在 Enforcing 的模式,但是可能由于一些设定的问题导致 SELinux 让某些服务无法正常的运作, 此时你可以将 Enforcing 的模式改为宽容 (permissive) 的模式,让 SELinux 只会警告无法顺利联机的讯息,而不是直接抵挡主体进程的读取权限。让 SELinux 模式在 enforcing 与 permissive 之间切换的方法为:

[root@study ~]# setenforce [0|1] 
选项与参数:
0 :转成 permissive 宽容模式;
1 :转成 Enforcing 强制模式


#范例一:将 SELinux 在 Enforcing 与 permissive 之间切换与观察
[root@study ~]# setenforce 0
[root@study ~]# getenforce 
Permissive
[root@study ~]# setenforce 1
[root@study ~]# getenforce 
Enforcing

不过请注意, setenforce 无法在 Disabled 的模式底下进行模式的切换喔!

在某些特殊的情况底下,你从 Disabled 切换成 Enforcing 之后,竟然有一堆服务无法顺利启动,都会跟你说在 /lib/xxx 里面的数据没有权限读取,所以启动失败。这大多是由于在重新写入 SELinux type (Relabel) 出错之故,使用 Permissive 就没有这个错误。那如何处理呢?最简单的方法就是在 Permissive 的状态下, 使用『 restorecon -Rv / 』重新还原所有 SELinux 的类型,就能够处理这个错误!

16.8 SELinux 政策内的规则管理

从图 16.7的图里面,我们知道 SELinux 的三种模式是会影响到主体进程的放行与否。如果是进入 Enforcing 模式,那么接着下来会影响到主体进程的,当然就是第二关:『 target 政策内的各项规则 (rules) 』了! 好了,那么我们怎么知道目前这个政策里面到底有多少会影响到主体进程的规则呢? 很简单,就透过 getsebool 来瞧一瞧即可。

16.8.1 各个规则的布尔值查询 getsebool

如果想要查询系统上面全部规则的启动与否 (on/off,亦即布尔值),很简单的透过 sestatus -b 或 getsebool -a 均可!

[root@study ~]# getsebool [-a] [规则的名称]
选项与参数:
-a :列出目前系统上面的所有 SELinux 规则的布尔值为开启或关闭值


#范例一:查询本系统内所有的布尔值设定状况
[root@study ~]# getsebool  -a | more
abrt_anon_write --> off
abrt_handle_event --> off
...
cron_can_relabel --> off    <--这个跟 cornd 比较有关!
cron_userdomain_transition --> on
...
httpd_enable_homedirs --> off    <--这当然就是跟网页,亦即 http 有关的!
...
#这么多的 SELinux 规则喔!每个规则后面都列出现在是允许放行还是不许放行的布尔值喔!

16.8.2 各个规则规范的主体进程能够读取的文件SELinux type查询seinfo,sesearch

我们现在知道有这么多的 SELinux 规则,但是每个规则内到底是在限制什么东西?如果你想要知道的话,那就得要使用 seinfo 等工具!这些工具并没有在我们安装时就安装了,因此需要安装后才能执行!

[root@study ~]# yum install -y setools-console

很快的安装完毕之后,我们就可以来使用 seinfo, sesearch 等指令了!

[root@study ~]# seinfo [-Atrub]
选项与参数:
-A :列出 SELinux 的状态、规则布尔值、身份识别、角色、类别等所有信息 
-u :列出 SELinux 的所有身份识别 (user) 种类
-r :列出 SELinux 的所有角色 (role) 种类
-t :列出 SELinux 的所有类别 (type) 种类
-b :列出所有规则的种类 (布尔值)


#范例一:列出 SELinux 在此政策下的统计状态
[root@study ~]# seinfo 

Statistics for policy file: /sys/fs/selinux/policy
Policy Version & Type: v.28 (binary, mls)

   Classes:           130    Permissions:       272
   Sensitivities:       1    Categories:       1024
   Types:            4792    Attributes:        253
   Users:               8    Roles:              14
   Booleans:          316    Cond. Expr.:       362
   Allow:          107373    Neverallow:          0
   Auditallow:        157    Dontaudit:       10022
   Type_trans:      18129    Type_change:        74
   Type_member:        35    Role allow:         39
   Role_trans:        416    Range_trans:      5899
   Constraints:       143    Validatetrans:       0
   Initial SIDs:       27    Fs_use:             32
   Genfscon:          103    Portcon:           614
   Netifcon:            0    Nodecon:             0
   Permissives:         0    Polcap:              5

#从上面我们可以看到这个政策是 targeted ,此政策的安全本文类别有 4792 个; 
#而各种 SELinux 的规则 (Booleans) 共制订了 316 条!

我们在 16.6 里面简单的谈到了几个身份识别 (user) 以及角色 (role) 而已,如果你想要查询目前所有的身份识别与角色,就使用『 seinfo -u 』及『 seinfo -r 』就可以知道了!至于简单的统计数据,就直接输入 seinfo 即可!但是上面还是没有谈到规则相关的东西 没关系~一个一个来 我们在 16.6.2 的最后面谈到 /etc/cron.d/checktime 的 SELinux type 类型不太对那我们也知道 crond 这个进程的 type 是 crond_t,能不能找一下 crond_t 能够读取的文件 SELinux type 有哪些呢?

[root@study ~]# sesearch [-A] [-s 主体类别] [-t 目标类别] [-b 布尔值] 
选项与参数:
-A :列出后面数据中,允许『读取或放行』的相关数据
-t :后面还要接类别,例如 -t httpd_t
-b :后面还要接 SELinux 的规则,例如 
-b httpd_enable_ftp_server


#范例一:找出 crond_t 这个主体进程能够读取的文件 SELinux type
[root@study ~]# sesearch -A -s crond_t | grep spool
   allow crond_t var_spool_t : dir { ioctl read getattr lock search open } ; 
   allow crond_t system_cron_spool_t : dir { ioctl read getattr lock search open } ; 
   allow crond_t user_cron_spool_t : lnk_file { read getattr } ; 
   allow crond_t user_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; 
   allow crond_t system_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; 
   allow crond_t var_spool_t : file { ioctl read getattr lock open } ; 
   allow crond_t cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; 
   allow daemon user_cron_spool_t : file { ioctl read write getattr lock append } ; 
   allow crond_t cron_spool_t : dir { ioctl read write getattr lock add_name remove_name search open } ; 
   allow crond_t user_cron_spool_t : dir { ioctl read write getattr lock add_name remove_name search open } ; 
   allow crond_t user_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; 
   allow crond_t system_cron_spool_t : file { ioctl read write create getattr setattr lock append unlink link rename open } ; 
#allow 后面接主体进程以及文件的 SELinux type,上面的数据是撷取出来的, 
#意思是说,crond_t 可以读取 system_cron_spool_t 的文件/目录类型~等等!



#范例二:找出 crond_t 是否能够读取 /etc/cron.d/checktime 这个我们自定义的配置文件?
[root@study ~]# ll -Z /etc/cron.d/checktime 
-rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 /etc/cron.d/checktime
#两个重点,一个是 SELinux type 为 admin_home_t,一个是文件 (file)

[root@study ~]# sesearch -A -s crond_t | grep 'admin_home_t'
   allow domain admin_home_t : dir { getattr search open } ; 
   allow crond_t admin_home_t : dir { ioctl read getattr lock search open } ; 
   allow userdom_filetrans_type admin_home_t : lnk_file { read getattr } ; 
   allow userdom_filetrans_type admin_home_t : dir { ioctl read write getattr lock add_name remove_name search open } ; 
   allow domain admin_home_t : lnk_file { read getattr } ; 
   allow crond_t admin_home_t : lnk_file { read getattr } ; 
#仔细看!虽然有 crond_t admin_home_t 存在,但是这是总体的信息,
#并没有针对某些规则的寻找,所以还是不确定 checktime 能否被读取。但是,基本上就是 SELinux
#type 出问题.因此才会无法读取的!

所以,现在我们知道 /etc/cron.d/checktime 这个我们自己复制过去的文件会没有办法被读取的原因,就是因为 SELinux type 错误啦! 根本就无法被读取,那现在我们来查一查,那 getsebool -a 里面看到的 httpd_enable_homedirs 到底是什么?又是规范了哪些主体进程能够读取的 SELinux type 呢?

[root@study ~]# semanage boolean -l | grep 'httpd_enable_homedirs'
httpd_enable_homedirs          (关    ,    关)  Allow httpd to enable homedirs
#httpd_enable_homedirs 的功能是允许 httpd 进程去读取用户家目录的意思~


#范例三:列出 httpd_enable_homedirs 这个规则当中,主体进程能够读取的文件 SELinux type
[root@study ~]# sesearch -A -b httpd_enable_homedirs
Found 77 semantic av rules:
   allow httpd_t home_root_t : dir { ioctl read getattr lock search open } ; 
   allow httpd_t home_root_t : lnk_file { read getattr } ; 
   allow httpd_t user_home_type : dir { getattr search open } ; 
   allow httpd_t user_home_type : lnk_file { read getattr } ; 
 ....
#从上面的数据才可以理解,在这个规则中,主要是放行 httpd_t 能否读取用户家目录的文件! 
#所以,如果这个规则没有启动,基本上,httpd_t 这种进程就无法读取用户家目录下的文件!

16.8.3 修改 SELinux 规则的布尔值 setsebool

那么如果查询到某个 SELinux rule,并且以 sesearch 知道该规则的用途后,想要关闭或启动他,又该如何处置?

[root@study ~]# setsebool [-P] 『规则名称』 [0|1] 
选项与参数:
-P :直接将设定值写入配置文件,该设定数据未来会生效的!


#范例一:查询 httpd_enable_homedirs 这个规则的状态,并且修改这个规则成为不同的布尔值
[root@study ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off    <--果是 off ,依题意给他启动看看!

[root@study ~]# setsebool -P httpd_enable_homedirs 1
[root@study ~]# getsebool httpd_enable_homedirs     
httpd_enable_homedirs --> on

这个 setsebool 最好记得一定要加上 -P 的选项!因为这样才能将此设定写入配置文件! 这是非常棒的工具组!你一定要知道如何使用 getsebool 与 setsebool 才行!

15.9 SELinux 安全本文的修改

再次的回到图 16.7的图,现在我们知道 SELinux 对受限的主体进程有没有影响,第一关考虑 SELinux 的三种类型,第二关考虑 SELinux 的政策规则是否放行,第三关则是开始比对 SELinux type!从刚刚 16.8 小节我们也知道可以透过 sesearch 来找到主体进程与文件的 SELinux type 关系!好,现在总算要来修改文件的 SELinux type,以让主体进程能够读到正确的文件!这时就得要几个重要的小东西了~

15.9.1 使用 chcon 手动修改文件的 SELinux type

[root@study ~]# chcon [-R] [-t type] [-u user] [-r role] 文件 
[root@study ~]# chcon [-R] --reference=范例文件 文件
选项与参数:
-R :连同该目录下的次目录也同时修改;
-t :后面接安全性本文的类型字段!例如 httpd_sys_content_t ;
-u :后面接身份识别,例如 system_u; (不重要)
-r :后面街角色,例如 system_r; (不重要)
-v :若有变化成功,请将变动的结果列出来
--reference=范例文件:拿某个文件当范例来修改后续接的文件的类型!


#范例一:查询一下 /etc/hosts 的 SELinux type,并将该类型套用到 /etc/cron.d/checktime 上
[root@study ~]# ll -Z /etc/hosts
-rw-r--r--. root root system_u:object_r:net_conf_t:s0  /etc/hosts
[root@study ~]# chcon -v -t net_conf_t /etc/cron.d/checktime 
正在更改"/etc/cron.d/checktime" 的安全环境
[root@study ~]# 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 SELinux type 套用到 /etc/cron.d/checktime 上!
[root@study ~]# chcon -v --reference=/etc/shadow /etc/cron.d/checktime 
正在更改"/etc/cron.d/checktime" 的安全环境
[root@study ~]# 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

上面的练习『都没有正确的解答!』因为正确的 SELinux type 应该就是要以 /etc/cron.d/ 底下的文件为标准来处理才对啊~既然如此,能不能让 SELinux 自己解决默认目录下的 SELinux type 呢?可以!就用 restorecon 吧!

15.9.2 使用restorecon让文件恢复正确的SELinux type

[root@study ~]# restorecon [-Rv] 文件或目录 
选项与参数:
-R :连同次目录一起修改;
-v :将过程显示到屏幕上


#范例三:将 /etc/cron.d/ 底下的文件通通恢复成预设的 SELinux type!
[root@study ~]# restorecon -Rv /etc/cron.d/
restorecon reset /etc/cron.d/checktime context system_u:object_r:shadow_t:s0->system_u:object_r:system_cron_spool_t:s0
#上面这一行表示将 checktime 由 shadow_t 改为 system_cron_spool_t


#范例四:重新启动 crond 看看有没有正确启动 checktime 啰!?
[root@study ~]# systemctl restart crond.service
[root@study ~]# tail /var/log/cron
#再去瞧瞧这个 /var/log/cron 的内容,应该就没有错误讯息了

其实,chcon 这个指令用的少!因为 restorecon 主动的回复预设的 SELinux type 要简单很多!而且可以一口气恢复整个目录下的文件!所以,建议你只要记得 restorecon 搭配 -Rv 同时加上某个目录这样的指令串即可~修改 SELinux 的 type 就变得非常的轻松!

15.9.3 semanage 默认目录的安全性本文查询与修改

你应该要觉得奇怪,为什么 restorecon 可以『恢复』原本的 SELinux type 呢?那肯定就是有个地方在纪录每个文件/目录的 SELinux 默认类型啰? 没错!是这样~那要如何查询预设的 SELinux type 以及如何增加/修改/删除预设的 SELinux type 呢?很简单,透过 semanage 即可!他是这样使用的:

[root@study ~]# semanage {login|user|port|interface|fcontext|translation} -l 
[root@study ~]# semanage fcontext -{a|d|m} [-frst] file_spec
选项与参数:
fcontext :主要用在安全性本文方面的用途, -l 为查询的意思;
-a :增加的意思,你可以增加一些目录的默认安全性本文类型设定;
-m :修改的意思; 
-d :删除的意思。



#范例一:查询一下 /etc /etc/cron.d 的预设 SELinux type 为何?
[root@study ~]# semanage fcontext -l | grep -E '^/etc|^/etc/cron'
/etc                                               all files          system_u:object_r:etc_t:s0 
/etc/cron\.d(/.*)?                                 all files          system_u:object_r:system_cron_spool_t:s0 

看到上面输出的最后一行,那也是为啥我们直接使用 vim 去 /etc/cron.d 底下建立新文件时,预设的 SELinux type 就是正确的!同时,我们也会知道使用 restorecon 回复正确的 SELinux type 时,系统会去判断默认的类型为何的依据。现在让我们来想一想,如果 (当然是假的!不可能这么干) 我们要建立一个 /srv/mycron 的目录,这个目录默认也是需要变成 system_cron_spool_t 时, 我们应该要如何处理呢?基本上可以这样作:

1. 先建立 /srv/mycron 同时在内部放入配置文件,同时观察 SELinux type
[root@study ~]# ll -dZ /srv/mycron/ /srv/mycron/checktime 
SELinux             fcontext                             type Context
drwxr-xr-x. root root unconfined_u:object_r:var_t:s0   /srv/mycron/
-rw-r--r--. root root unconfined_u:object_r:var_t:s0   /srv/mycron/checktime


#2. 观察一下上层 /srv 的 SELinux type
[root@study ~]# semanage fcontext -l | grep '^/srv'
SELinux                                         fcontext                             type Context
/srv                                               all files          system_u:object_r:var_t:s0 
#怪不得 mycron 会是 var_t 啰!


#3. 将 mycron 默认值改为 system_cron_spool_t!
[root@study ~]# semanage fcontext -a -t system_cron_spool_t "/srv/mycron(/.*)?"
[root@study ~]# semanage fcontext -l | grep '^/srv/mycron'
SELinux                                         fcontext                             type Context
/srv/mycron(/.*)?                                  all files          system_u:object_r:system_cron_spool_t:s0 


#4.恢复 /srv/mycron 以及子目录相关的 SELinux type!
[root@study ~]# restorecon -Rv /srv/mycron/
restorecon reset /srv/mycron context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:system_cron_spool_t:s0
restorecon reset /srv/mycron/checktime context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:system_cron_spool_t:s0
[root@study ~]# ll -dZ /srv/mycron /srv/mycron/* 
drwxr-xr-x. root root unconfined_u:object_r:system_cron_spool_t:s0 /srv/mycron
-rw-r--r--. root root unconfined_u:object_r:system_cron_spool_t:s0 /srv/mycron/checktime
#有了默认值,未来就不会不小心被乱改了!这样比较妥当些~

semanage 的功能很多,不过主要用到的仅有 fcontext 这个项目的动作而已。如上所示,你可以使用 semanage 来查询所有的目录默认值,也能够使用他来增加默认值的设定!如果您学会这些基础的工具,那么 SELinux 对你来说,也不是什么太难的!

15.10 一个网络服务的案例及登录文件的协助

本章在 SELinux 小节当中谈到的各个指令中,尤其是 setsebool, chcon, restorecon 等,都是为了当你的某些网络服务无法正常提供相关功能时,才需要进行修改的一些指令动作。但是,我们怎么知道哪个时候才需要进行这些指令的修改啊?我们怎么知道系统因为 SELinux 的问题导致网络服务不对劲啊?如果都要靠客户端联机失败才来哭诉,那也太没有效率了!所以,我们的 CentOS 7.x 有提供几支侦测的服务在登录 SELinux 产生的错误!那就是 auditd 与 setroubleshootd。

15.10.1 setroubleshoot--> 错误信息写入 /var/log/messages

几乎所有 SELinux 相关的程序都会以 se 为开头,这个服务也是以 se 为开头!而 troubleshoot 大家都知道是错误克服,因此这个 setroubleshoot 自然就得要启动他啦!这个服务会将关于 SELinux 的错误讯息与克服方法记录到 /var/log/messages 与 /var/log/setroubleshoot/* 里头,所以你一定得要启动这个服务才好。启动这个服务之前当然就是得要安装它啦!这玩意儿总共需要两个软件,分别是 setroublshoot 与 setroubleshoot-server,如果你没有安装,请自行使用 yum 安装吧!

此外,原本的 SELinux 信息本来是以两个服务来记录的,分别是 auditd 与 setroubleshootd。既然是同样的信息,因此 CentOS 6.x (含 7.x) 以后将两者整合在 auditd 当中啦!所以,并没有setroubleshootd 的服务存在了喔!因此,当你安装好了 setroubleshoot-server 之后,请记得要重新启动 auditd,否则 setroubleshootd 的功能不会被启动的。

事实上,CentOS 7.x 对 setroubleshootd 的运作方式是:(1)先由 auditd 去呼叫 audispd服务,(2)然后 audispd 服务去启动 sedispatch 程序,(3)sedispatch 再将原本的 auditd 讯息转成 setroubleshootd 的讯息,进一步储存下来的!

[root@study ~]# rpm -qa | grep setroubleshoot      
setroubleshoot-server-3.2.24-1.1.el7.x86_64
setroubleshoot-plugins-3.0.59-1.el7.noarch
setroubleshoot-3.2.24-1.1.el7.x86_64

在预设的情况下,这个 setroubleshoot 应该都是会安装的!是否正确安装可以使用上述的指令去查询。万一没有安装,请使用 yum install 去安装吧! 再说一遍,安装完毕最好重新启动 auditd 这个服务喔!不过,刚刚装好且顺利启动后,setroubleshoot 还是不会有作用,为啥? 因为我们并没有任何受限的网络服务主体进程在运作啊!所以,底下我们将使用一个简单的 FTP 服务器软件为例, 让你了解到我们上头讲到的许多重点的应用!

透过 vsftpd 这个 FTP 服务器来存取系统上的文件

这里只是简单的利用 vsftpd 这个软件与 FTP 的协议来讲解 SELinux 的问题与错误克服而已。不过既然要使用到 FTP 协议,一些简单的知识还是得要存在才好!否则等一下我们没有办法了解为啥要这么做! 首先,你得要知道,客户端需要使用『FTP 账号登入 FTP 服务器』才行!而有一个称为『匿名 (anonymous) 』的账号可以登入系统! 但是这个匿名的账号登入后,只能存取某一个特定的目录,而无法脱离该目录~!

在 vsftpd 中,一般用户与匿名者的家目录说明如下:

  • 匿名者:如果使用浏览器来联机到 FTP 服务器的话,那预设就是使用匿名者登入系统。而匿名者的家目录默认是在 /var/ftp 当中!同时,匿名者在家目录下只能下载数据,不能上传数据到 FTP 服务器。同时,匿名者无法离开 FTP 服务器的 /var/ftp 目录喔!
  • 一般 FTP 账号:在预设的情况下,所有 UID 大于 1000 的账号,都可以使用 FTP 来登入系统! 而登入系统之后,所有的账号都能够取得自己家目录底下的文件数据!当然预设是可以上传、下载文件的!

为了避免跟之前章节的用户产生误解的情况,这里我们先建立一个名为 ftptest 的账号,且账号密码为 myftp123,先来建立一下吧!

[root@study ~]# useradd -s /sbin/nologin ftptest
[root@study ~]# echo "myftp123" | passwd --stdin ftptest
更改用户 ftptest 的密码 。
passwd:所有的身份验证令牌已经成功更新。

接下来当然就是安装 vsftpd 这只服务器软件,同时启动这只服务,另外,我们也希望未来开机都能够启动这只服务:

[root@study ~]# yum install -y vsftpd
[root@study ~]# systemctl start vsftpd
[root@study ~]# systemctl enable vsftpd
Created symlink from /etc/systemd/system/multi-user.target.wants/vsftpd.service to /usr/lib/systemd/system/vsftpd.service.
[root@study ~]# netstat -luntp | grep vsftp
tcp6       0      0 :::21                   :::*                    LISTEN      18544/vsftpd    

匿名者无法下载的问题

现在让我们来模拟一些 FTP 的常用状态!假设你想要将 /etc/securetty 以及主要的 /etc/sysctl.conf 放置给所有人下载,那么你可能会这样做!

[root@study ~]# cp -a /etc/securetty /etc/sysctl.conf /var/ftp/pub/
[root@study ~]# ll /var/ftp/pub/
总用量 8
-rw-------. 1 root root 221 8月  12 2015 securetty
-rw-r--r--. 1 root root 225 11月 20 2015 sysctl.conf

一般来说,默认要给用户下载的 FTP 文件会放置到上面表格当中的 /var/ftp/pub 目录!现在让我们使用简单的终端机浏览器 curl 来观察看看!看你能不能查询到上述两个文件的内容呢?

#1. 先看看 FTP 根目录底下有什么文件存在?
[root@study ~]# curl ftp://localhost                               
drwxr-xr-x    2 0        0              40 Oct 29 15:25 pub
#确实有存在一个名为 pub 的文件!那就是在/var/ftp 底下的 pub!


#2. 再往下看看,能不能看到 pub 内的文件呢?
[root@study ~]# curl ftp://localhost/pub/  <--带上文件夹名
-rw-------    1 0        0             221 Aug 12  2015 securetty
-rw-r--r--    1 0        0             225 Nov 20  2015 sysctl.conf

#3.继续看一下 sysctl.conf 的内容好了!
[root@study ~]# curl ftp://localhost/pub/sysctl.conf
#System default settings live in /usr/lib/sysctl.d/00-system.conf.
#To override those settings, enter new settings here, or in an /etc/sysctl.d/<name>.conf file
#
#For more information, see sysctl.conf(5) and sysctl.d(5).
#真的有看到这个文件的内容!所以确定是可以让 vsftpd 读取到这文件的!

#4. 再来瞧瞧 securetty!
[root@study ~]# curl ftp://localhost/pub/securetty
curl: (78) RETR response: 550
#看不到!但是,基本的原因应该是权限问题喔!因为 vsftpd 默认放在 /var/ftp/pub 内的资料,
#不论什么 SELinux type 几乎都可以被读取的才对喔!所以要这样处理!

#5.修改权限之后再一次观察 securetty 看看!
[root@study ~]# chmod a+r /var/ftp/pub/securetty 
[root@study ~]# curl ftp://localhost/pub/securetty
console
vc/1
vc/2
vc/3
vc/4
vc/5
vc/6
......
#此时你就可以看到实际的文件内容!


#6.修订 SELinux type 的内容 (非必备)
[root@study ~]# restorecon -Rv /var/ftp/
restorecon reset /var/ftp/pub/securetty context system_u:object_r:etc_runtime_t:s0->system_u:object_r:public_content_t:s0
restorecon reset /var/ftp/pub/sysctl.conf context system_u:object_r:system_conf_t:s0->system_u:object_r:public_content_t:s0

上面这个例子在告诉你,要先从权限的角度来瞧一瞧,如果无法被读取,可能就是因为没有 r 或没有 rx!并不一定是由 SELinux 引起的!好 再来瞧瞧如果是一般账号呢?如何登入?

无法从家目录下载文件的问题分析与解决

我们前面建立了 ftptest 账号,那如何使用文字界面来登入呢?就使用如下的方式来处理。同时请注意,因为文字型的 FTP 客户端软件,默认会将用户丢到根目录而不是家目录,因此,你的 URL 可能需要修订一下如下!

#1.为了让 curl 这个文字浏览器可以传输数据,我们先建立一些数据在 ftptest 家目录
[root@study ~]# echo "testing" > ~ftptest/test.txt
[root@study ~]# cp -a /etc/hosts /etc/sysctl.conf ~ftptest/
[root@study ~]# ll ~ftptest/
总用量 12
-rw-r--r--. 1 root root 158 6月   7 2013 hosts
-rw-r--r--. 1 root root 225 11月 20 2015 sysctl.conf
-rw-r--r--. 1 root root   8 10月 29 23:33 test.txt



#2.一般账号直接登入 FTP 服务器,同时变换目录到家目录去!
[root@study ~]# curl ftp://ftptest:myftp123@localhost/~/
-rw-r--r--    1 0        0             158 Jun 07  2013 hosts
-rw-r--r--    1 0        0             225 Nov 20  2015 sysctl.conf
-rw-r--r--    1 0        0               8 Oct 29 15:33 test.txt
#真的有数据~看文件最左边的权限也是没问题,所以,来读一下 test.txt 的内容看看


#2.开始下载 test.txt, sysctl.conf 等有权限可以阅读的文件看看!
[root@study ~]# curl ftp://ftptest:myftp123@localhost/~/test.txt
curl: (78) RETR response: 550
#竟然说没有权限!明明我们的 rwx 是正常没问题!那是否有可能是 SELinux 造成的?


#3.先将 SELinux 从 Enforce 转成 Permissive 看看情况!同时观察登录档
[root@study ~]# setenforce 0
[root@study ~]# curl ftp://ftptest:myftp123@localhost/~/test.txt
testing
[root@study ~]# setenforce 1 # 确定问题后,一定要转成 Enforcing!

 
#确定有数据内容!所以,确定就是 SELinux 造成无法读取的问题~那怎办?要改规则?还是改 type? 
#因为都不知道,所以,就检查一下登录档看看有没有相关的信息可以提供给我们处理!



[root@study ~]# vim /var/log/messages
Aug 9 02:55:58 station3-39 setroubleshoot: SELinux is preventing /usr/sbin/vsftpd
from lock access on the file /home/ftptest/test.txt. For complete SELinux messages.
run sealert -l 3a57aad3-a128-461b-966a-5bb2b0ffa0f9
Aug 9 02:55:58 station3-39 python: SELinux is preventing /usr/sbin/vsftpd from
lock access on the file /home/ftptest/test.txt.
***** Plugin catchall_boolean (47.5 confidence) suggests ******************
If you want to allow ftp to home dir
Then you must tell SELinux about this by enabling the 'ftp_home_dir' boolean   
You can read 'None' man page for more details.
Do
setsebool -P ftp_home_dir 1
 ***** Plugin catchall_boolean (47.5 confidence) suggests ******************
If you want to allow ftpd to full access
Then you must tell SELinux about this by enabling the 'ftpd_full_access' boolean. 
You can read 'None' man page for more details.
Do
setsebool -P ftpd_full_access 1
***** Plugin catchall (6.38 confidence) suggests **************************
........
#基本上,你会看到有个特殊字体的部份,就是 sealert 那一行。虽然底下已经列出可能的解决方案了, 
#就是一堆底线那些东西。至少就有三个解决方案 (最后一个没列出来),哪种才是正确的?
#为了了解正确的解决方案,我们还是还执行一下 sealert 那行吧!看看情况再说!


#4.透过 sealert 的解决方案来处理问题
[root@study ~]# sealert -l 3a57aad3-a128-461b-966a-5bb2b0ffa0f9
SELinux is preventing /usr/sbin/vsftpd from lock access on the file /home/ftptest/test.txt.

#底下说有 47.5% 的机率是由于这个原因所发生,并且可以使用 setsebool 去解决的意思!
***** Plugin catchall_boolean (47.5 confidence) suggests ******************
If you want to allow ftp to home dir
Then you must tell SELinux about this by enabling the 'ftp_home_dir' boolean. 
You can read 'None' man page for more details.
Do
setsebool -P ftp_home_dir 1

#底下说也是有 47.5% 的机率是由此产生的!
***** Plugin catchall_boolean (47.5 confidence) suggests ******************
If you want to allow ftpd to full access
Then you must tell SELinux about this by enabling the 'ftpd_full_access' boolean. 
You can read 'None' man page for more details.
Do
setsebool -P ftpd_full_access 1

#底下说,仅有 6.38% 的可信度是由这个情况产生的!
***** Plugin catchall (6.38 confidence) suggests **************************
If you believe that vsftpd should be allowed lock access on the test.txt 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:
#grep vsftpd /var/log/audit/audit.log | audit2allow -M mypol 
#semodule -i mypol.pp

#底下就重要了!是整个问题发生的主因~最好还是稍微瞧一瞧!
Additional Information:
Source Context system_u:system_r:ftpd_t:s0-s0:c0.c1023
Target Context unconfined_u:object_r:user_home_t:s0
Target Objects /home/ftptest/test.txt [ file ]
Source vsftpd
Source Path /usr/sbin/vsftpd
Port <Unknown>
Host station3-39.gocloud.vm
Source RPM Packages vsftpd-3.0.2-9.el7.x86_64
Target RPM Packages
Policy RPM selinux-policy-3.13.1-23.el7.noarch
Selinux Enabled True
Policy Type targeted
Enforcing Mode Permissive
Host Name station3-39.gocloud.vm
Platform Linux station3-39.gocloud.vm 3.10.0-229.el7.x86_64
                              #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64
Alert Count 3
First Seen 2015-08-09 01:00:12 CST
Last Seen 2015-08-09 02:55:57 CST
Local ID 3a57aad3-a128-461b-966a-5bb2b0ffa0f9

Raw Audit Messages
type=AVC msg=audit(1439060157.358:635): avc: denied { lock } for pid=5029 comm="vsftpd" 
 path="/home/ftptest/test.txt" dev="dm-2" ino=141 scontext=system_u:system_r:ftpd_t:s0-s0:
 c0.c1023 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file

type=SYSCALL msg=audit(1439060157.358:635): arch=x86_64 syscall=fcntl success=yes exit=0 
 a0=4 a1=7 a2=7fffceb8cbb0 a3=0 items=0 ppid=5024 pid=5029 auid=4294967295 uid=1001 gid=1001
 euid=1001 suid=1001 fsuid=1001 egid=1001 sgid=1001 fsgid=1001 tty=(none) ses=4294967295
 comm=vsftpd exe=/usr/sbin/vsftpd subj=system_u:system_r:ftpd_t:s0-s0:c0.c1023 key=(null)

Hash: vsftpd,ftpd_t,user_home_t,file,lock

经过上面的测试,现在我们知道主要的问题发生在 SELinux 的 type 不是 vsftpd_t 所能读取的原因.经过仔细观察 test.txt 文件的类型,我们知道他原本就是家目录,因此是 user_home_t 也没啥了不起的!因此,分析两个比较可信 (47.5%) 的解决方案后,可能是与 ftp_home_dir 比较有关啊!所以,我们应该不需要修改 SELinux type,修改的应该是 SELinux rules 才对!所以,这样做看看:

#1.先确认一下 SELinux 的模式,然后再瞧一瞧能否下载 test.txt,最终使用处理方式来解决~
[root@study ~]# getenforce 
Enforcing
[root@study ~]# curl ftp://ftptest:myftp123@localhost/~/test.txt
curl: (78) RETR response: 550
#确定还是无法读取!
[root@study ~]# setsebool -P ftp_home_dir 1
[root@study ~]# curl ftp://ftptest:myftp123@localhost/~/test.txt 
testing
#现在用户可以在自己的家目录上传/下载文件了!


#2.开始下载其他文件试看看!
[root@study ~]# curl ftp://ftptest:myftp123@localhost/~/sysctl.conf
#System default settings live in /usr/lib/sysctl.d/00-system.conf.
#To override those settings, enter new settings here, or in an /etc/sysctl.d/<name>.conf file
#
# or more information, see sysctl.conf(5) and sysctl.d(5).

没问题!透过修改 SELinux rule 的布尔值,现在我们就可以使用一般账号在 FTP 服务来上传/下载数据!那万一我们还有其他的目录也想要透过 FTP 来提供这个 ftptest用户上传与下载呢?

一般账号用户从非正规目录上传/下载文件

假设我们还想要提供 /srv/gogogo 这个目录给 ftptest 用户使用,那又该如何处理呢?假设我们都没有考虑 SELinux ,那就是这样的情况:

#1.先处理好所需要的目录数据
[root@study ~]# mkdir /srv/gogogo
[root@study ~]# chgrp ftptest /srv/gogogo/
[root@study ~]# echo "test" > /srv/gogogo/test.txt


#2.开始直接使用 ftp 观察一下数据!
[root@study ~]# curl ftp://ftptest:myftp123@localhost//srv/gogogo/test.txt
curl: (78) RETR response: 550
有问题了!来瞧瞧登录档怎么说!


[root@study ~]# grep sealert /var/log/messages | tail
Aug 9 04:23:12 station3-39 setroubleshoot: SELinux is preventing /usr/sbin/vsftpd from
 read access on the file test.txt. For complete SELinux messages. run sealert -l
 08d3c0a2-5160-49ab-b199-47a51a5fc8dd
[root@study ~]# sealert -l 08d3c0a2-5160-49ab-b199-47a51a5fc8dd
SELinux is preventing /usr/sbin/vsftpd from read access on the file test.txt.

# 虽然这个可信度比较高~不过,因为会全部放行FTP ,所以不太考虑!
***** Plugin catchall_boolean (57.6 confidence) suggests ******************

If you want to allow ftpd to full access
Then you must tell SELinux about this by enabling the 'ftpd_full_access' boolean.
You can read 'None' man page for more details.
Do
setsebool -P ftpd_full_access 1

# 因为是非正规目录的使用,所以这边加上预设SELinux type 恐怕会是比较正确的选择!
***** Plugin catchall_labels (36.2 confidence) suggests *******************

If you want to allow vsftpd to have read access on the test.txt file
Then you need to change the label on test.txt
Do
# semanage fcontext -a -t FILE_TYPE 'test.txt'
where FILE_TYPE is one of the following: NetworkManager_tmp_t, abrt_helper_exec_t, abrt_tmp_t,
 abrt_upload_watch_tmp_t, abrt_var_cache_t, abrt_var_run_t, admin_crontab_tmp_t, afs_cache_t,
 alsa_home_t, alsa_tmp_t, amanda_tmp_t, antivirus_home_t, antivirus_tmp_t, apcupsd_tmp_t, ...
Then execute:
restorecon -v 'test.txt'

***** Plugin catchall (7.64 confidence) suggests **************************

If you believe that vsftpd should be allowed read access on the test.txt 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:
# grep vsftpd /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp

Additional Information:
Source Context system_u:system_r:ftpd_t:s0-s0:c0.c1023
Target Context unconfined_u:object_r:var_t:s0
Target Objects test.txt [ file ]
Source vsftpd
.....(底下省略).....

因为是非正规目录,所以感觉上似乎与 semanage 那一行的解决方案比较相关~接下来就是要找到 FTP 的 SELinux type 来解决! 所以,让我们查一下 FTP 相关的数据!

#3.先查看一下 /var/ftp 这个地方的 SELinux type 吧!
[root@study ~]# ll -Zd /var/ftp/
drwxr-xr-x. root root system_u:object_r:public_content_t:s0 /var/ftp/

#4.以 sealert 建议的方法来处理好 SELinux type!
[root@study ~]# semanage fcontext -a -t public_content_t "/srv/gogogo(/.*)?" 
[root@study ~]# restorecon -Rv /srv/gogogo
[root@study ~]# curl ftp://ftptest:myftp123@localhost//srv/gogogo/test.txt
test

在这个范例中,我们是修改了 SELinux type!与前一个修改 SELinux rule 不太一样!

无法变更 FTP 联机端口问题分析与解决

在某些情况下,可能你的服务器软件需要开放在非正规的端口,举例来说,如果因为某些政策问题,导致 FTP 启动的正常的 21 号端口无法使用,因此你想要启用在 555 号端口时,该如何处理呢? 基本上,既然 SELinux 的主体进程大多是被受限的网络服务,没道理不限制放行的端口啊! 所以,很可能会出问题~那就得要想想办法才行!

#1.先处理 vsftpd 的配置文件,加入换 port 的参数才行!
[root@study ~]# vim /etc/vsftpd/vsftpd.conf
#请按下大写的 G 跑到最后一行,然后新增加底下这行设定!前面不可以留白! 
listen_port=555

#2.重新启动 vsftpd 并且观察登录档的变化!
[root@study ~]# systemctl restart vsftpd 
[root@study ~]# grep sealert /var/log/messages
Oct 30 00:01:27 study setroubleshoot: SELinux is preventing /usr/sbin/vsftpd from name_bind access on the tcp_socket port 555. For complete SELinux messages. run sealert -l 40f473b6-c05d-40e3-aed0-761008a2db2c

[root@study ~]# sealert -l 40f473b6-c05d-40e3-aed0-761008a2db2c
SELinux is preventing /usr/sbin/vsftpd from name_bind access on the tcp_socket port 555.

*****  Plugin bind_ports (92.2 confidence) suggests   ************************

If you want to allow /usr/sbin/vsftpd to bind to network port 555
Then you need to modify the port type.
Do
# semanage port -a -t PORT_TYPE -p tcp 555
    其中 PORT_TYPE 是以下之一:certmaster_port_t, cluster_port_t, ephemeral_port_t, ftp_data_port_t, ftp_port_t, hadoop_datanode_port_t, hplip_port_t, isns_port_t, port_t, postgrey_port_t, unreserved_port_t。

*****  Plugin catchall_boolean (7.83 confidence) suggests   ******************

If 您要 allow nis to enabled
Then 您必须启用 'nis_enabled' 布尔值告知 SELinux 此情况。
您可以阅读 'None' 手册页面来了解详情。
Do
setsebool -P nis_enabled 1

*****  Plugin catchall (1.41 confidence) suggests   **************************

If 您确定应默认允许 vsftpd name_bind 访问 port 555 tcp_socket。
Then 您应该将这个情况作为 bug 报告。
您可以生成本地策略模块允许这个访问。
Do
请执行以下命令此时允许这个访问:
# grep vsftpd /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp


Additional Information:
Source Context                system_u:system_r:ftpd_t:s0-s0:c0.c1023
Target Context                system_u:object_r:hi_reserved_port_t:s0
Target Objects                port 555 [ tcp_socket ]
Source                        vsftpd
Source Path                   /usr/sbin/vsftpd
Port                          555
Host                          study.centos.xiaoqi
Source RPM Packages           vsftpd-3.0.2-25.el7.x86_64
Target RPM Packages           
Policy RPM                    selinux-policy-3.13.1-252.el7.1.noarch
Selinux Enabled               True
Policy Type                   targeted
Enforcing Mode                Enforcing
Host Name                     study.centos.xiaoqi
Platform                      Linux study.centos.xiaoqi 3.10.0-327.el7.x86_64 #1
                              SMP Thu Nov 19 22:10:57 UTC 2015 x86_64 x86_64
Alert Count                   2
First Seen                    2019-10-30 00:00:40 CST
Last Seen                     2019-10-30 00:01:26 CST
Local ID                      40f473b6-c05d-40e3-aed0-761008a2db2c

Raw Audit Messages
type=AVC msg=audit(1572364886.618:1461): avc:  denied  { name_bind } for  pid=21932 comm="vsftpd" src=555 scontext=system_u:system_r:ftpd_t:s0-s0:c0.c1023 tcontext=system_u:object_r:hi_reserved_port_t:s0 tclass=tcp_socket


type=SYSCALL msg=audit(1572364886.618:1461): arch=x86_64 syscall=bind success=no exit=EACCES a0=4 a1=7f1d62d537c0 a2=1c a3=3 items=0 ppid=21930 pid=21932 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm=vsftpd exe=/usr/sbin/vsftpd subj=system_u:system_r:ftpd_t:s0-s0:c0.c1023 key=(null)

Hash: vsftpd,ftpd_t,hi_reserved_port_t,tcp_socket,name_bind
#看一下信任度,高达 92.2% !几乎就是这家伙~因此不必再看~就是他了!比较重要的是, 
#解决方案里面,那个 PORT_TYPE 有很多选择~但我们是要开启 FTP 端口!所以,
#就由后续数据找到 ftp_port_t 那个项目!带入实验看看!


#3.实际带入 SELinux 端口修改后,在重新启动 vsftpd 看看
[root@study ~]# semanage port -a -t ftp_port_t -p tcp 555
[root@study ~]# systemctl restart vsftpd
[root@study ~]# netstat -tlnp | grep 'vsftpd'
tcp6       0      0 :::21                   :::*                    LISTEN      18544/vsftpd    

#4.实验看看这个 port 能不能用?
[root@study ~]# curl ftp://localhost:555/pub/
-rw-r--r-- 1 0 0 221 Oct 29 2014 securetty 
-rw-r--r-- 1 0 0 225 Mar 06 03:05 sysctl.conf

透过上面的几个小练习,你会知道在正规或非正规的环境下,如何处理你的 SELinux 问题!在仔细研究看看!

最后修改:2020 年 01 月 21 日 04 : 34 PM

发表评论