上个周末我的博客因为要设置一个网络,所以重启了一下实例,解决发现重启之后无法 SSH 连接,甚至于 VNC 都不能登录了,这很崩溃,后面通过特殊手段修改了 SELinux 默认为不启动之后再重启终于解决了问题,因为我周末刚好要出去,所以导致博客停机了大概 10 个小时,这比我前两年的总时还长,让我不由得感慨是我不会用还是 vultr 提供的服务有待完善?

甭管如何,至少我遇到这个问题是不能立刻解决掉的,所以这里我会写几篇文章来总结一下自己不能解决的问题。但是这不算是我对这次"事故"的复盘,毕竟这个问题一个是比较简单,复盘的话似乎重了一些,但是,这次会出现问题也是暴露了一些问题,是可以在我后续的迭代中改进的;二来则是我自己复盘并没有太大作用,可能 YY 的因素多一些,因为没有 reviewer 进行深入的探讨,所以结果可能并不是太理想。但是,因为这个事情而掌握的几个知识点还是很有必要总结一番的,因为这些都是 CentOS 下很重要的组件,以前、现在以及以后都会遇到,所以总是很有益处的。

SELinux 的由来

从以前一开始接触 Linux 开始,一个核心的权限模型便深刻得印记在心中,那就是最简单的 "UGO" 模型,其实也就是我们经常使用命令 "ls -al" 时看到的 "rwxrwxrwx" 的这个东西,以三段来区分拥有者、拥有组以及其他人的权限。同时,也知道 ROOT 是个独特的存在,没有什么权限是它没有的,即使你要使用 "rm -rf /" 来自杀也可以。

但是,因为这种模型太多简单的,所以会引发很多问题。例如,经常我们运行程序的时候会被提醒:"不要使用 root 用户运行,请创建一个普通用户执行。" ,这是因为一旦我们以 root 用户执行一个程序之后,该程序对应的进程将会拥有 root 的权限,如果你的程序的最终用户可以通过程序来操作一些系统的资源的话,那么可能引发的后果是不堪设想的,例如查看你系统中的:/etc/passwd 文件。如果你认为不以 root 用户来运行就万事大吉的话,那么现实又会啪啪啪打你脸,例如一个 http 程序,如果以 nginx 用户来运行,并且 nginx 用户拥有对 /var/www/html 目录的读写权限,那么如果你的用户可以通过你的 Web 应用往 /var/www/html 目录写入/修改文件的话,这后果也是杠杠的。

基于对这样简单的权限模型的增加,于是乎,Linux 引入了 SELinux 的子系统,从 Google 查询的资料来说,这个是 NSA(美国国家安全局)设计开发的,所以我想为啥有这么严格的限制能力也是有点道理的。

SELinux 的系统模型

虽然 SELinux 是属于 Linux 的子系统,但是各个发行版对于 SELinux 的应用可能会有所不同(猜测、未证实),所以我这里以 CentOS 7 为样例系统来验证我的说明,如果你不是使用这个系统,那么如有偏差,请对照得查找资料。

在介绍 SELinux 之前先思考一个问题,那就是当我们觉得 SELinux 太麻烦的时候,几乎所有时候都是简单了当得关闭 SELinux 处理,但是在这个时候我们会发现,关闭 SELinux 对我们的正常使用是无感的,而且甚至于让我们的使用体验觉得更爽。从这个角度出发,这里应该有一个概念,那么 SELinux 的系统的耦合不是强耦合的,可以说是额外的一个安全增强,所以这里借用鸟哥的一张图来表示一下:

这样描述可能就大概了解了 SELinux 的工作机制,所以当我们收到 "Access Denied" 的时候,除了看 UGO 之外,还需要看一下 SELinux 相关的信息。

SELinux 的概念

前面描述了一些过程了,其中涉及到了一些概念,这里对他们进行分析一下:

SELinux 实练

光叨叨了这么多,不如来点真刀实枪得好一些,所以这里就以 nginx 访问一个文件为例,看看权限控制的过程是怎么样的。第一步的怎么安装设置 Nginx 就不说了吧,直接进行 SELinux 相关的操作。首先我们先来看看 Nginx 的安全上下文是啥:

这里我删除掉了很多无关的信息,但是这里可能让你感到陌生的是第 3 和 4 行,这前面一段可能以前没怎么关注过(PS:这里说明一下,在 ps 或者 ls 的参数中加上大写的 Z 之后可以查看和 SELinux 相关的信息),这里其实也是和 UGO 类似的 4 段文本,分别是:

OK,假设这是我的网站的静态文件在 /usr/share/nginx/html/liqiang.io 的目录下,那么看一下 index.html 文件的 Context:

好的,没问题,正如我们所期待的那番,出现了文件的 Context,但是我们注意到 nginx 进程对应的 domain:httpd_t 和文件的 context:httpd_sys_content_t 不一样啊,虽然我们看上去很类似了,但是毕竟还是不一样,那么第一张图中的 AVC(Access Vector Cache ) 又是如何进行判断的呢?这里就涉及到 SELinux 的策略和规则管理的内容了,所以我们得用一些 SELinux 的工具来查看,这里不妨先安装一波:

然后再来看一下对应的文件的 Context:

因为内容实在太多了,所以我这里就选取了前面 10 条来表示,这里可以看到,SELinux 对权限的控制的粒度之细微。但是,这只是第一步,我们还没有和进程的 Domain 结合起来,所以再来看看进程的 Domain:

可以看到,这里直接就允许了访问 httpd_sys_content_t Context 的文件,这个时候似乎事情就有点恍然大悟的感觉了,是不是都匹配了。

SELinux 的操作

从前面可以看到 SELinux 的功能异常复杂,设计的项目繁多,例如在我使用的 CentOS 7 系统中,已经存在这么多的规则了:

可见如果要自己来定义这个 SELinux 规则是多么难的一件事情,所以大多数时候我们是不会这么做的,除非你知道你要怎么做已经觉得自己可以 Hold 得住了;所以,一般情况下,我们用以下这些手段应该就可以解决问题了:

查看 SELinux 状态

sestatus 可以让你清晰得掌握 SELinux 的各种状态。

临时管理 SELinux

setenforce 0 可以让你临时关闭 SELinux,同时相应的 getenforce 命令可以简单查看 SELinux 的当前状态;但是,必须再说一遍,这是临时关闭,如果重启之后就恢复默认设置了,这是深坑

永久关闭 SELinux

SELinux 的系统配置是 /etc/selinux/config,通过这个配置,你可以永久得关闭 SELinux:

小结

本文就以 CentOS 7 为示例系统,大概得描述了一下 SELinux 的功能和机制,最后介绍了几个方便管理 SELinux 的方式,但是我必须说,这都是引子,因为这些引子后面的内容是一个偌大的 SELinux 系统,所以有兴趣的同学不妨多看看其他人的系统性的资料。我在了解实践的过程中,特别感谢下面前三份资料,给予我莫大的帮助,虽然我只看了点皮毛: