上个周末我的博客因为要设置一个网络,所以重启了一下实例,解决发现重启之后无法 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 参不参与
- 如果 SELinux 参与,那么将查找 Policy 中的 Rules,构成 Program 的上下文(Domain)
- 然后通过 Domain 与文件的 Context(Type)进行比较,看是否拥有权限,如果有则继续我们以前的 UGO 模型(DAC)
这样描述可能就大概了解了 SELinux 的工作机制,所以当我们收到 “Access Denied” 的时候,除了看 UGO 之外,还需要看一下 SELinux 相关的信息。
SELinux 的概念
前面描述了一些过程了,其中涉及到了一些概念,这里对他们进行分析一下:
- 主体(Subject):需要访问管理资源的对象,可以简单认为是进程就好了,其实还可以是目录。
- 目标(Object):需要被访问的资源,这个直接认为是文件就好了,当然,复杂一些还可以是套接字等。
- 策略(Policy):因为进程和文件都比较多,所以如果你设置文件 A 可以被进程 pid1、pid2、pid3 访问这样的配置的话,将会非常麻烦,所以如果将这类进程归个类,这类文件也归个类,设置哪类进程可以访问哪类文件这样的 Rule 的话,事情就变得简单了,而 Policy 就定义了这些 Rule。
- 安全上下文(Context):策略定义了进程和文件的访问权限,但是当要他们交互的时候,是需要判断一下他们的权限是否匹配,所以这个就属于上下文的的内容,在上图可以看到。其实简单点说,程序的安全上下文就是程序拥有的权限,而文件安全上下文就是文件需要的权限。
SELinux 实练
光叨叨了这么多,不如来点真刀实枪得好一些,所以这里就以 nginx 访问一个文件为例,看看权限控制的过程是怎么样的。第一步的怎么安装设置 Nginx 就不说了吧,直接进行 SELinux 相关的操作。首先我们先来看看 Nginx 的安全上下文是啥:
这里我删除掉了很多无关的信息,但是这里可能让你感到陌生的是第 3 和 4 行,这前面一段可能以前没怎么关注过(PS:这里说明一下,在 ps 或者 ls 的参数中加上大写的 Z 之后可以查看和 SELinux 相关的信息),这里其实也是和 UGO 类似的 4 段文本,分别是:
- 身份表述(identify):不看了,反正和普通的系统用户不是同一个东西
- 角色(Role):同上
- 类型(Type):这个字段很重要,因为它映射到进程或者文件的时候代表的含义是不一样的
- 对应进程时(例如 Nginx):表示的是 domain,表示进程拥有的权限
- 对应文件时(例如 /var/www/html):表示的是 context,表示文件需要的权限
- 当一个进程想访问文件时,只有 domian 和 context 匹配才能进行 UGO 的判断
- 进程与文件 SELinux 字段的相关性
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 系统,所以有兴趣的同学不妨多看看其他人的系统性的资料。我在了解实践的过程中,特别感谢下面前三份资料,给予我莫大的帮助,虽然我只看了点皮毛: