起因

事情是这样的,因为不想在 OS X 上装一大堆东西,所以我装了台 CentOS 7 虚拟机作开发环境。我很开心地 yum install httpd php ,然后随便 git clone 了个 PHP 脚本,结果竟然挂了:

[Tue Jul 22 15:27:24.474225 2014] [:error] [pid 3739] [client 192.168.0.2:59118] PHP Warning:  fopen(sessions/86e9d16ee7c20c458f): failed to open stream: Permission denied in /var/www/html/cookielesscookies/index.php on line 38, referer: http://192.168.0.251/cookielesscookies/

嗯, Permission denied 好说,权限问题嘛,把用户和用户组改成 apache 嘛。结果问题依旧。呃,那就简单粗暴地递归修改权限为 0777 嘛。问题仍然存在……

这就有点匪夷所思了, Permission denied 这种问题一般来说就是文件权限的问题,可我甚至检查了若干个父目录,权限设置看起来都没问题。在折腾了一个多小时后,突然一拍脑袋想起来有个叫做 SELinux 的东西!

果然一把 SELinux 关掉,问题就解决了!然后我就发了条微博来吐槽 SELinux。没想到转着转着,竟然有人给了这么个网站:Stop Disabling SELinux 。里面有个链接,是 Red Hat 2012年峰会的一个关于 SELinux 的讲座。看了之后真的觉得,确实是冤枉了 SELinux,自己也确实是太年轻了。

有关 SELinux

下面简单记录下我从视频里面学到了的东西吧。

SELinux 里一个非常重要的概念就是: type !没错,就像我们说 C++ 是一门强类型语言一样, SELinux 也会对每个文件、进程做类型检查。举个例子:

  • Apache 进程的类型是 httpd_t
  • 文件夹 /var/www/html/ 及里面的东西的类型是 httpd_sys_content_t
  • 文件夹 /var/home/ 及里面的东西类型是 home_t
  • 文件 /etc/shadow 的类型是 shadow_t
  • 对于一个 httpd_t 的进程,访问 httpd_sys_content_t 的文件似乎十分的合理
  • 对于一个 httpd_t 的进程,访问 home_tshadow_t 的文件似乎就不那么合理了

要知道,对于 Web 服务——这是一个暴露在十分恶劣环境下的服务——要是你以 root 的身份运行 httpd ,碰巧你的脚本又写得漏洞百出(比如 <?php echo file_get_contents($_GET['path']); ?> ),那么借此 hack 服务器可并不是什么难事。 SELinux 的存在使得就算是 root 也不能够我行我素,对于前面这个弱智代码, SELinux 可以保护服务器的敏感信息不被窃取。

SELinux 的好助手

虽然说起来 SELinux 是一个很不错的东西,但是当你的程序都无法正常运行的时候,你就会真的很想把 SELinux 关掉。好在有一些非常棒的工具可以帮助我们用好 SELinux 。

setroubleshoot-server

首先,这东西读作 SE Troubleshoot Server ,我想应该没人和我一样断句断错了吧……安装的方法很简单:

yum install setroubleshoot-server
service auditd restart

这个东西好用在,当你因为 SELinux 的原因发生了些错误,你可以直接在 /var/log/messages 文件中查看到具体信息,甚至是 用自然语言书写的解决办法

比方说我之前那个 Permission denied ,查看错误消息可以得到如下信息:

Jul 22 15:55:10 vm-centos setroubleshoot: Plugin Exception restorecon
Jul 22 15:55:10 vm-centos setroubleshoot: SELinux is preventing /usr/sbin/httpd from write access on the directory . For complete SELinux messages. run sealert -l b741594f-af42-4368-9a8a-5f6fffe97da5
Jul 22 15:55:10 vm-centos python: SELinux is preventing /usr/sbin/httpd from write access on the directory .

*****  Plugin httpd_write_content (92.2 confidence) suggests   ***************

If you want to allow httpd to have write access on the  directory
Then you need to change the label on '$FIX_TARGET_PATH'
Do
# semanage fcontext -a -t httpd_sys_rw_content_t '$FIX_TARGET_PATH'
# restorecon -v '$FIX_TARGET_PATH'

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

If you want to allow httpd to unified
Then you must tell SELinux about this by enabling the 'httpd_unified' boolean.

Do
setsebool -P httpd_unified 1

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

If you believe that httpd should be allowed write access on the  directory 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 httpd /var/log/audit/audit.log | audit2allow -M mypol
# semodule -i mypol.pp

看那个有92.2%信心的提示,问题就出在这里。 /var/www/html/ 里面的东西的类型都是 httpd_sys_content_t 是只读的,然而我却想往里面写东西,这必然会导致错误。所以我们只要把这里面的东西改成 httpd_sys_rw_content_t 类型即可。

chcon

chcon 可以改变文件或者文件夹的类型属性,类似于 chmod 之类的命令。比如可以这么用:

chcon -R -t httpd_sys_rw_content_t /var/www/html/

ls -laZ

ls 命令在加上 -Z 之后就可以查看有关 SELinux 的文件属性。

[root@vm-centos ~]# ls -laZ /var/www/html/cookielesscookies/
drwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 .
drwxrwxr-x. root        apache system_u:object_r:httpd_sys_rw_content_t:s0 ..
-rwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 config.php
-rwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 etags.jpg
-rwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 fingerprinting.jpg
drwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 .git
-rwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 .htaccess
-rwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 index.php
-rw-rw-r--. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 LICENSE
-rw-rw-r--. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 README.md
drwxrwxr-x. abcdabcd987 apache unconfined_u:object_r:httpd_sys_rw_content_t:s0 sessions