SELinux简介

SELinux全称是Security Enhanced Linux,由美国国家安全部(National Security Agency)领导开发的GPL项目,它拥有一个灵活而强制性的访问控制结构,旨在提高Linux系统的安全性,提供强健的安全保证,可防御未知攻击,据称相当于B1级的军事安全性能。比MS NT所谓的C2等高得多。
应用SELinux后,可以减轻恶意攻击或恶意软件带来的灾难,并提供对机密性和完整性有很高要求的信息很高的安全保障。 SELinux vs Linux 普通Linux安全和传统Unix系统一样,基于自主存取控制方法,即DAC,只要符合规定的权限,如规定的所有者和文件属性等,就可存取资源。在传统的安全机制下,一些通过setuid/setgid的程序就产生了严重安全隐患,甚至一些错误的配置就可引发巨大的漏洞,被轻易攻击。
而SELinux则基于强制存取控制方法,即MAC,透过强制性的安全策略,应用程序或用户必须同时符合DAC及对应SELinux的MAC才能进行正常操作,否则都将遭到拒绝或失败,而这些问题将不会影响其他正常运作的程序和应用,并保持它们的安全系统结构。

SELinux的相关配置文件

SELinux的配置相关文件都在/etc/selinux下,其中/etc/selinux/targeted目录里就包含了策略的详细配置和context定义,以下是主要文件及功用:
/etc/selinux/targeted/contexts/*_context 默认的context设置
/etc/selinux/targeted/contexts/files/* 精确的context类型划分
/etc/selinux/targeted/policy/* 策略文件

Apache under SELinux

Apache under SELinux - 让Apache跑得顺起来!
对于刚使用Redhat Enterprise Linux 4 或Fedora Core 2以上/CentOS 4的用户,一定会为Apache经常无法正常运转,报以"Permission denied"等错误而大为不解,甚至大为恼火。
其实这是因为这些系统里激活了SELinux,而用户的apache配置与SELinux的配置策略有抵触产生的,只有通过适当调整,使apache的配置和访问符合策略才能正常使用。
现在下面来分析一下SELinux中有关httpd(apache)的context定义(略有删节)

/home/[^/]+/((www)|(web)|(public_html))(/.+)? system_u:object_r:httpd_user_content_t
/var/www(/.*)? system_u:object_r:httpd_sys_content_t
/var/www/cgi-bin(/.*)? system_u:object_r:httpd_sys_script_exec_t
/usr/lib/cgi-bin(/.*)? system_u:object_r:httpd_sys_script_exec_t
/var/www/perl(/.*)? system_u:object_r:httpd_sys_script_exec_t
/var/www/icons(/.*)? system_u:object_r:httpd_sys_content_t
/var/cache/httpd(/.*)? system_u:object_r:httpd_cache_t
/etc/vhosts -- system_u:object_r:httpd_config_t
/usr/sbin/httpd -- system_u:object_r:httpd_exec_t
/usr/sbin/apache(2)? -- system_u:object_r:httpd_exec_t
/usr/sbin/suexec -- system_u:object_r:httpd_suexec_exec_t
/var/log/httpd(/.*)? system_u:object_r:httpd_log_t
/var/log/apache(2)?(/.*)? system_u:object_r:httpd_log_t
/var/log/cgiwrap\.log.* -- system_u:object_r:httpd_log_t
/var/cache/ssl.*\.sem -- system_u:object_r:httpd_cache_t
/var/cache/mod_ssl(/.*)? system_u:object_r:httpd_cache_t
/var/run/apache(2)?\.pid.* -- system_u:object_r:httpd_var_run_t
/var/lib/httpd(/.*)? system_u:object_r:httpd_var_lib_t
/var/lib/php/session(/.*)? system_u:object_r:httpd_var_run_t
/etc/apache-ssl(2)?(/.*)? system_u:object_r:httpd_config_t
/usr/lib/apache-ssl(/.*)? -- system_u:object_r:httpd_exec_t
/usr/sbin/apache-ssl(2)? -- system_u:object_r:httpd_exec_t
/var/log/apache-ssl(2)?(/.*)? system_u:object_r:httpd_log_t
/var/run/apache-ssl(2)?\.pid.* -- system_u:object_r:httpd_var_run_t
/var/run/gcache_port -s system_u:object_r:httpd_var_run_t
/var/lib/squirrelmail/prefs(/.*)? system_u:object_r:httpd_squirrelmail_t
/usr/bin/htsslpass -- system_u:object_r:httpd_helper_exec_t
/usr/share/htdig(/.*)? system_u:object_r:httpd_sys_content_t
/var/lib/htdig(/.*)? system_u:object_r:httpd_sys_content_t

针对上述的内容,可以对如下的几个常见问题进行简单处理:

1.phpmyadmin在非默认/var/www/html目录下无法运转

通常类似的情况都是在配置了虚拟主机时,访问/phpmyadmin等提示403访问拒绝,日志里也提示Permission denied,这是因为phpmyadmin防止的目录及文件本身属性不符合context要求。
假设phpmyadmin放在/web目录下,那么执行:
chcon -R -t httpd_user_content_t /web
则会令/web及其下所有子目录/文件,包括phpmyadmin文件都获得了httpd_user_content_t的属性,如果其传统的Unix属性对httpd来说是可读的话,再重新访问一下就应该可以了。

2./home目录下的虚拟主机无法运转

与问题1也是类似的,不过根据上文中context的定义,/home目录下必须是用户的$HOME/www或public_html或web目录才是 httpd_user_content_t类型,因此建议将要作为web页面的内容放置在用户的$HOME/www或web或public_html里,并确保其属性是httpd_user_content_t,使用如下命令查看:
ls -Z /home/abc/
drwxr-xr-x abc abc user_u:object_r:user_home_dir_t tmp
drwxrwxr-x abc abc user_u:object_r:httpd_user_content www
如不是,则可通过chcon来逐级目录及文件更改,直至最后能访问:
chcon -R -t httpd_user_content_t /home/abc/web
chcon -t user_home_dir_t /home/abc

3.CGI程序无法运行

如果cgi程序放在/var/www/cgi-bin/里也无法执行,遇到403或500错误的话,可以检查cgi程序的属性,按SELinux contexts文件里定义的,/var/www/cgi-bin/里必须是httpd_sys_script_exec_t 属性。通过ls -Z查看,如果不是则通过如下命令更改:
chcon -t httpd_sys_script_exec_t /var/www/cgi-bin/*.cgi
如果是虚拟主机里的cgi,则参考问题2使之能正常使用普通的功能后,再通过chcon设置cgi文件的context为httpd_sys_script_exec_t即可。

4.Setuid/gid 程序无法运行

例如早期的SqWebMail及qmailadmin等,需要setuid/gid的支持,但在SELinux下这将受到严格限制。第一种方法是比较彻底的办法,能保留系统的安全性,通过:
audit2allow -l -i /var/log/messages
将SELinux拒绝的信息转换为相应的policy allow指令,将这些指令添加到SELinux policy 的src里相应的配置文件,重新生成policy并加载。但这样做相对比较麻烦。
另一个方法最简单,但将使apache得不到保护。首先确定SELinux 类型是targeted的:
cat /etc/selinux/config|grep SELINUXTYPE
然后,使apache脱离SELinux保护:
setsebool -P httpd_disable_trans 1
然后重启动apache:
/etc/init.d/httpd restart
这样所有apache强制的检查都失效,需要setuid/gid的程序可以正常使用。但这样带来了增加漏洞的危险,对于迫切需要运行而又很急的情况,本方法是一个最大限度减少系统安全缺失的最后办法。对于取消SELinux 未必是一个好方法。
 

SElinux的几个相关命令

一.
ps -Z
ls -Z
id -Z

例:
[root@ljj cgi-bin]# ls -Z
-rwxrwxrwx root root root:object_r:httpd_sys_script_exec_t a.cgi
-rw-r--r-- root root root:object_r:httpd_sys_script_exec_t a.txt
 
二. chcon
修改文件的属性 fild1:fild2:fild3

chcon -u fild1 file
chcon -l fild2 file
chcon -t fild3 file

例:
chcon -u root file1

三.getsebool
获取se相关的bool值
例:
[root@ljj cgi-bin]# getsebool -a | grep httpd
httpd_builtin_scripting --> inactive
httpd_disable_trans --> active
httpd_enable_cgi --> active
httpd_enable_homedirs --> active
httpd_ssi_exec --> active
httpd_tty_comm --> inactive
httpd_unified --> inactive

得到了一些与httpd相关的bool值,配置httpd.conf中的user_dir时,要保证这里的httpd_enable_homedirs是 active的,还要保证:
chcon -R -t httpd_sys_content_t ~user/public_html;
  • httpd与selinux之间的关系更多详见:man httpd_selinux
四. togglesebool
给se的相关bool值取反
例:
togglesebool httpd_enable_homedirs
********************************************************************
在使用了SELinux的系统中,每一个进程的上下文都包含三个组成部分:一个ID(identity),一个角色(role)和一个域(domain)
ID是指这个进程的所有者,就是UNIX账户,但前提是这个账户必须被预先编译到SELinux策略中去使SELinux认识这个账户,不然的话SELinux默认地将那些未知的系统进程ID记为 system_u ,将那些未知的用户进程ID记为 user_u;角色用来判断某个处于此角色的ID可以进入哪些域,还用来防止某个处于此角色的ID进入其它不该进入的域。比如, user_r角色就不允许进入 sysadm_t (重要的系统管理域)。
换句话说就是,那些只有 user_u ID的进程只能扮演 user_r 这个角色,而 user_r 这个角色永远不能被许可进入 sysadm_t 域。从而,那些只有 user_u 这个ID的人是别想进入 sysadm_t 域的。这些特色在缺省的Fedora Core2策略中并没有完全使用,当前我们只是把努力花在制定守护进程上,而对用户域的策略限制的很少(targeted策略没有对用户登陆做任何限制)。
一个安全上下文可以像 identity:role:domain 这样一种描述符的方式简明的表现出来。
比如,典型的系统管理上下文可以表示成 root:sysadm_r:sysadm_t 。任何可以被访问的对象都可以这样来表示。值得注意的是,"域"其实也是和一个进程相对应的一个"类型"。所以当检查某个进程是否有权向另一个进程发送信号(比如为ps命令检阅/proc文件系统)时,接受信号的进程的"域"就会充当"域-类型"模型中的"类型"的角色,从而完成"域-类型"的规则检查。即完成了进程间通信权限的检查。由于对于文件还没有使用角色这个机制,所以目前每个文件都被规定为object_r 角色(这个角色只是占个位置罢了,对策略没有任何影响)。
文件的ID就是文件创建者的ID。constraints 策略源文件中使用这个方式来判断一个访问是否有权改变某个文件的上下文描述符。除非被访问的文件的描述符中的ID字段和访问该文件的进程的所有者ID字段相同,无论是改变前还是改变后,否则进程无权改变一个文件的上下文描述符。
例如,一个拥有 rjc:user_r:user_t 描述符的进程可以将一个拥有 rjc:object_r:user_games_rw_t 描述符的文件的描述符改为rjc:object_r:user_games_ro_t ,但是它无权改变一个拥有 john:object_r:user_games_rw_t 描述符的文件的任何属性。
要想查看当前运行的进程的上下文描述符,可以使用ps命令并加入 "-Z"选项,如例一:"ps ax -Z的输出":
PID CONTEXT COMMAND
1634 root:user_r:user_t -bash
1662 root:user_r:user_t ps ax -Z
Example 1. Example Output of ps ax -Z
要想查看目录下的文件的上下文描述符,可以使用ls命令并加入 "-Z"选项,如例一:"ls -Z的输出":
drwxr-xr-x root root system_u:object_r:bin_t bin
drwxr-xr-x root root system_u:object_r:boot_t boot
drwxr-xr-x root root system_u:object_r:device_t dev
drwxr-xr-x root root system_u:object_r:etc_t etc
drwxr-xr-x root root system_u:object_r:home_root_t home
drwxr-xr-x root root system_u:object_r:root_t initrd
drwxr-xr-x root root system_u:object_r:lib_t lib
drwx------ root root system_u:object_r:lost_found_t lost+found
drwxr-xr-x root root system_u:object_r:default_t misc
drwxr-xr-x root root system_u:object_r:mnt_t mnt
drwxr-xr-x root root system_u:object_r:usr_t opt
?--------- ? ? oracle
dr-xr-xr-x root root proc
drwxr-x--- root root system_u:object_r:user_home_dir_t root
drwxr-xr-x root root system_u:object_r:sbin_t sbin
drwxr-xr-x root root selinux
drwxr-xr-x root root system_u:object_r:default_t srv
drwxr-xr-x root root sys
drwxrwxrwt root root system_u:object_r:tmp_t tmp
drwxr-xr-x root root system_u:object_r:usr_t usr
drwxr-xr-x root root system_u:object_r:var_t var
Example 2. Example Output of ls -Z
值得注意的是:对于那些没有指定上下文的文件(一般是指那些不支持rwx标签的文件系统,如/sys、/proc、/selinux),ls命令就不会显示其上下文。对于不能用stat命令查看当前状态的那些文件系统。ls命令返回"?---------",其所有者和所有组也被标记为"?",同样的,他的上下文也不会显示。
如例三所示,id命令将返回当前shell的上下文
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:user_r:user_t
Example 3. Example Output of the id Command
如果你的SELinux系统使用了strict 策略,你会发现应用程序做一些不正常的事情是很容易发生的。你经常会发现某些程序中会有些bug,这些bug使程序做那些你的SELinux策略不允许其做的其它事情。
SELinux要求进程的上下文只有在被执行的"那一刻"才允许改变。新进程的域和角色信息可以从exec系统函数的上下文和文件类型中自动获得。进程也可以在执行exec之前被指明上下文。这些过程自然受SELinux策略的控制,因为ID,角色和域都受SELinux策略的控制。
Fedora中策略的实现:
Fedora Core 3 开始,SELinux的策略数据存储于/etc/security/selinux/X/src/policy/目录下(X指你选则的策略,可以是 "strict" 或者 "targeted")。在这个目录中,你可以使用"make load"来编译并装在策略。也就是用那个命令将策略编译成二进制格式并装在到内核中,并立即生效。除了装如内核,该命令还将策略的二进制格式存储到 /etc/selinux/X/policy/policy.YY文件中,这里X指你选则的策略,可以是"strict" 或者 "targeted",YY是策略的版本号(Fedora Core 3支持最新的版本是18)。这是为了在系统开机的第一时间,init可以迅速装载策略到内核。/etc/selinux/config这个配置文件用来告诉init那些策略需要装载。
当你启动一个SELinux时,init所做的第一件事就是挂载/proc文件系统,并判断SELinux是否被激活。init通过selinuxfs文件系统类型来判断内核中是否有SELinux,如果内核中没有SELinux或者内核参数中 selinux=0 这一项,那么系统就会以一种叫做 non-SE的状态被继续引导启动。如果发现了SELinux,那么/selinux虚拟文件系统将被创建,然后,init通过 /selinux/policyvers来检查内核所支持的SELinux版本。最后,相应的策略数据 /etc/selinux/X/policy/policy.YY就会被装在到内核中去了。
当策略被装载完之后,所有正在运行着的进程(指的就是init和内核的所有线程)都将被指定 system_u:system_r:kernel_t 这样一种上下文(内核线程其实无论在什么时间被创建,其上下文都是system_u:system_r:kernel_t )。当init装载完策略之后,它还要重新执行自己。策略中有一个规则叫domain_auto_trans(kernel_t, init_exec_t, init_t)。他的意思就是当 kernel_t 域执行了一个据有 init_exec_t 类型的可执行文件(如/sbin/init),那么该执行文件对应的进程的域就会自动过渡到 init_t 域(这是/sbin/init正确的所在域)。当这些都完成之后,init就继续完成那些通常的任务来完成机器的启动。内核线程自始至终都以 kernel_t 这个类型运行。
文件和目录的上下文存储于扩展属性当中 (XATTRs)。更多关于XATTRs的信息请参考attr(5), getfattr(1) 和 setfattr(1)的manpage。
简单的说,一个XATTR就是硬盘上某个文件的所有权,它由文件名和其它一些信息组成。对于每个 persistent 文件系统来说,它的每个文件或目录的SELinux上下文就存储在 security.selinux属性当中。虽然/proc文件系统不是persistent 文件系统,但SELinux还是在幕后为其文件和目录做了上下文标记,只是不能用getxattr获得罢了。和它对比,devpts文件系统(应用于/dev/pts Unix98伪终端)中各个文件(各个伪终端)的上下文是可以通过getxattr来获得的,并可以通过 setxattr 来更改每个设备的上下文(以便sshd等类似程序可以更改tty设备的上下文)。对于那些拥有固定存储的文件系统(ext2, ext3, Reiserfs, XFS, vfat, 等等) 有两种选择用于设定文件上下文:
第一种:文件系统的类型就规定了其内部每一个文件英据有统一的上下文;
第二种:使用 XATTRs 为每个文件标记不同上下文。例如,在iso9660(CD-ROM)文件系统中的每一个文件的上下文都是system_u:object_r:iso9660_t ,这被称做 genfs标记。
Ext2, ext3 和XFS支持 XATTRs 而且 Fedora 系统也支持安全标记名字空间,所以Fedora默认使用 XATTRs 来标记文件的上下文,但这也是可选方法之一。因为 直到 Reiser4发布之前 ,Hans Reiser对支持XATTR没多大兴趣,所以Fedora的 ReiserFS 文件系统不能很好的支持SELinux标记操作。也就是说,使用 ReiserFS 文件系统作为SELinux的根文件系统是不可能的事情。
XFS有一个与XATTRs相关的重要的问题:如果XATTRs不能被装入超级块(inode),那么它们就会被装入数据块,每一个超级块就要使用一个数据块用来装 SELinux XATTR 。建立XFS时,mkfs.xfs在默认情况下创建的数据块大小为4096,超级块大小为256(要是用于安装 SELinux XATTR ,大约缺少30字节)。这就是说,默认的XFS文件系统中,每一个超级块要占用4096字节用来装载 SELinux XATTR ,这对于磁盘空间来说是严重的浪费!当你使用"-i size=512"选项创建一个XFS文件系统时,超级块的大小就变成了512字节,这样就可以将SELinux XATTR 装入超级块,即节省了空间,又提高的性能。512字节的超级块也有可能给其它操作带来好处。所以,如果你使用XFS并且打算将来使用 SELinux 的话,将超级块的大小规定为512字节肯定是个好主意。
如果你使用的是较新的内核(如最新的Fedora内核或标准2.6.8.1内核)并且使用最新的mount工具,那么当你挂载一个文件系统时,有一个选项可以用来为整个文件系统指定上下文标签。比如你要挂载的文件系统是一个邮件池(mail spool),你可以使用"-o context=system_u:object_r:mail_spool_t"选项来挂载他,这样会将其内部的全部文件的上下文标记为 system_u:object_r:mail_spool_t。如果你的这个邮件池很大,而且还是XFS文件系统,这个方法还可以避免上一段所讨论的 inode大小的问题。甚至对于ext3那样的对XATTRs 开销较小的文件系统来说,使用context=选项挂载文件系统也会进一步减少开销,也可以避免为已经建立的文件系统重新设置 SElinux上下文(如果文件系统中文件较多的话,会浪费很长的时间)。
Fedora的缺省SELinux策略:
Fedora Core 3系统中,缺省的策略是"targeted "策略。对于此种选择人们议论纷纷,问题在于我们希望能使尽可能多得人使用SELinux。如果人们觉得这玩意太可怕并且妨碍了人们做他们想做的事,那么人们会把它关掉。所以我们在这个时候宁愿为大多数人提供适量的保护策略,也不会为了少部分人而提供严格的保护策略。缺省的Fedora Core 3安装会激活 SELinux 并使用"targeted "策略,你也可以通过运行 system-config-securitylevel 这个程序将策略改为更加严格的 "strict "策略。
如果你安装了策略的源文件包,那么策略的源文件就在/etc/selinux/X/src/policy/目录里面(X指你选则的策略,可以是 "strict" 或者 "targeted")在这个目录下有一个叫作domains/program/的子目录,里面为每一个守护进程对应了一个.te文件。你可以删去那些你不使用也不打算使用的守护进程所对应的.te文件,从而减少内核内存空间的使用,并提高性能。比如,你的系统没有BIND服务,你完全可以删掉 named.te 文件。然后如果你使用"make load"命令重新装载策略到内核的话,内核对内存的使用量就会减少。然而也要小心,如果你误删了其它文件,那么你的系统将不能正常启动进入 enforcing模式。所以目前,这种调节最好还是由内行来做。
刚开始接触SElinux的你,要注意一个内核参数,它用来决定你系统的内核运行于强制(enforcing )模式还是自由(permissive)模式,那就是"enforcing"参数。在自由模式下SELinux只是记录他该做什么,而事实上并不做任何动作。在强制模式下SElinux会来真格的。如果你的策略有错误,在强制模式下系统可能会阻止你登陆!所以正常情况下你应改在启动时传 enforcing=1 给内核,当你的SELinux策略有问题时,你可以临时传enforcing=0给内核来查错。在/etc/selinux/config这个init的配置文件中相应的有一个选项,通过设置它,也可以使系统进入自由模式。
如果你打算中止使用SELinux,你可以使用 selinux=0 这个内核参数。这会关闭SELinux的全部功能,就好像你的内核并没有把SElinux机制编译进去一样。所以当系统出了问题时,比如系统崩溃,有些人就用此方法进行理事的恢复工作,但是我并不赞成这种做法。当你临时使用 selinux=0 进入系统后,你所建立的任何文件将不会拥有SELinux上下文标记。这就意味着,如果你替换了诸如/etc/passwd 或/etc/shadow等重要文件,那么下一次进入SELinux时,系统就不会正常的工作。这不是严重的问题,当你从CD引导时,做系统恢复时,或使用一个不支持 XATTRs备份软件恢复系统时,同样的问题也会发生。这个问题虽然可以通过为受到影响的文件系统重新标记上下文来解决,但是使用 enforcing=0 来代替 selinux=0 不是更好吗?
你可以编辑/etc/selinux/config来临时关闭SELinux。
Fedora Core 2的释放是SELinux一次重要的发展。他是第一套提供对SELinux完整支持的主流Linux发行版本。Fedora Core 3 也是一个重要的里程碑,因为他是第一套将SELinux作为默认安装选项的Linux发行版本。
Red Hat Enterprise Linux 4 见会跟随 Fedora Core 的开发步伐,其对SELinux的支持也会得到进一步的发展。当 RHEL 4 发布时,她将会从 Fedora Core 系统和在Fedora 上学习SELinux的用户那里获得非常大的益处。
你可以在 irc.freenode.net的 IRC服务器上的#fedora-selinux频道找到对SELinux的支持。还有 Fedora SELinux邮件列表 http://www.redhat.com/mailman/listinfo/fedora-selinux-list 。我经常挂在#fedora-selinux上面,并也订阅了邮件列表,我期望着在那里回答您提出的任何问题。
更多关于SELinux的资料:
NSA site for Security-Enhanced Linux: http://www.nsa.gov/selinux/
Fedora Core SELinux FAQ: http://people.redhat.com/kwade/fedora-docs/selinux-faq/
IRC channel for SELinux in Fedora Core: irc.freenode.net, #fedora-selinux
Mailing list for SELinux in Fedora Core: http://www.redhat.com/mailman/listinfo/fedora-selinux-list
SELinux play machine: http://www.coker.com.au/selinux/play.html