主要内容
什么是SELinux
SELinux是一个权限控制系统,全称Security-Enhanced Linux。关于权限的控制,前面的文章已经介绍过,分别是基于传统文件系统的权限控制和基于ACL的权限控制,这两者控制的是用户对系统资源的权限,即面向的是系统中的用户;而SELinux面向的是应用程序,控制的是应用程序对系统资源的权限:即应用程序可以访问系统哪些目录或者文件。
SELinux主要控制对外提供服务(通常是网络服务)的程序,例如ftp、apache(nginx)等等,对于这些服务的进程,出于安全考虑,它们只能访问具有特定属性的资源,这是SELinux限制的。
各版本Linux的SELinux安装情况
SELinux对于Linux系统并不是标配的,它是一个可选组件。也就是说,不是每种Linux发行版都默认安装有SELinux,从目前的情况来看,一般基于RPM包管理的发行版例如:RedHat、Centos、Fedora都预装SELinux,而基于deb包管理的发行版例如:Ubuntu、Debian都没有安装。
检查系统是否安装了SELinux
getenforce命令
在终端中输入getenforce命令,无论它返回什么值,只要这个命令可以正常运行,就说明系统中安装了SELinux组件,相反,如果运行getenforce命令的时候提示没有这个命令,则表示没有安装SELinux组件。
SELinux的三种模式
假设系统中安装了SELinux,那么运行getenforce命令可能的返回值如下:
- enforcing
- permissive
- disabled
enforcing:强制模式,程序被SELinux严格控制,enforcing模式是SELinux的核心模式。
permissive:宽容模式,跟踪程序的行为,如果程序访问的资源超出SELinux的限制,则给出警告,并使用日志记录,但放行。
disabled:完全禁用SELinux
在讨论SELinux的时候,基本上使用的都是enforcing模式。
setenforce命令
setenforce命令用于切换SELinux的工作模式,但紧限于enforcing和permissive模式,disabled模式只能通过配置文件设置,不能通过setenforce命令禁用SELinux。
setenforce命令的参数只有1个,它是数字1或者0,1对应enforcing,0对应permissive。这个命令只能通过管理员权限运行
1 2 3 4 5 |
#切换到enforcing模式 setenforce 1 #切换到permissive模式 setenforce 0 |
SELinux的组成部分
我们说,SELinux是限制应用程序对系统资源(通常是文件或者目录)的使用,那么,最基本的组成必然是:应用程序、系统资源和权限,这三者,都有一个专业的术语:
- Subject:即应用程序,通常是指进程或者线程
- Object:指系统资源,除了常见的文件和目录,其他的例如端口、sockets、IO设备等都属于资源的范围
- Policy:即权限,Policy由多个rule(规则)组成
安全上下文(Security Context)
在Subject和Object中,都有一个安全上下文,用于保存它们的权限标识(但Subject和Object中的上下文并不一样),这个安全上下文存在于文件的inode中,就像传统文件的权限(rwx)一样。安全上下文是给操作系统使用的,通过对比Subject和Object的安全上下文,如果匹配,Subject就拥有操作Object的权限。
查看Object(文件)的安全上下文
使用ls的时候,通过参数-l就可以查看文件的传统权限(rwx),同样,ls命令也提供了检查文件安全上下文的参数,它就是-Z。
1 2 3 4 5 |
[root@study ~]# ls -Z -rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 \ -rw-------. root root system_u:object_r:admin_home_t:s0 anaconda-ks.cfg -rw-r--r--. 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 test |
unconfined_u:object_r:admin_home_t:s0 就是文件的安全上下文,由4个部分构成,但是第4个部分是可选的,因此,重要的是前3个部分。
分别是:
- 用户(user)
- 角色(role)
- 类型(type)
用户通常分为两种,他们是:
- 受SELinux限制的用户(system_u),一般是系统内建用户
- 还没有被SELinux限制的用户(unconfined_u),一般是后期创建的用户
角色用来标识资源的类型,也可以分成两种,他们是:
- 文件(object_r) 说明资源是一种文件,例如普通文件、sockets或者管道
- 程序 (system_r) 说明该资源是一个进程
其中类型(type)是三个之中最重要的一列,当type作用于进程的时候,叫做domain type,而当type作用于资源的时候,叫做object type。操作系统正是通过对比domain type和object type来匹配权限的。
查看Subject(进程)的安全上下文
ls通过-Z参数检查文件的Security Context,ps命令也提供了-Z参数来列出进程的Security Context。
1 2 3 |
[root@study ~]# ps -ejf -Z | grep "nginx" | grep -v "grep" system_u:system_r:httpd_t:s0 root 5019 1 5019 5019 0 09:58 ? 00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf system_u:system_r:httpd_t:s0 nginx 5020 5019 5019 5019 0 09:58 ? 00:00:00 nginx: worker process |
第一列就是进程的安全上下文,它的组成列和文件的上下文基本匹配。
Policy、Rule和Type Environment
Policy由一系列Rule组成,Rule决定了一个进程是否能够访问指定的资源。Policy、Rule和上面的Security Context共同构成Type Environment。
那么Rule是什么,它是如何确立进程和资源之间的关系的呢?下面一步步来解释,以nginx进程为例子,它的domain type是httpd_t,常见的Rule格式如下:
1 2 |
#rule-type source-type target-type class permission allow httpd_t httpd_config_t : file { ioctl read getattr lock open } ; |
意思是:允许类型为httpd_t的进程对类型为httpd_config_t的文件(file)进行ioctl、read、open等操作。
一个Rule由5个部分组成,它们是:
- Rule类型(允许、禁止等)
- domain type(进程的类型)
- object type(资源的类型)
- class(用于标识资源是什么,一个文件,还是文件夹或者其他)
- permission(具体权限,多个权限使用大括号括起来,分隔符为空格)
上面的例子列出的是allow类型的规则,除此之外,还有其他3种,也就是说,规则一共有4种,它们是:
- allow
- dontaudit
- auditallow
- neverallow
显然,我们常用的还是allow规则。讲到这里,基本上就可以理解SELinux的工作原理,结合下图,理解起来会更简单:
Conditional Policy
Conditional Policy,即条件化策略:根据用户设置的条件来决定应该使用什么规则。
1 2 3 4 5 6 7 |
bool httpd_enable_homedirs true; if (httpd_enable_homedirs) { # Rules if condition is true; } else { # Rules if condition is false; } |
SELinux预设了很多条件化策略,例如和提供web服务相关的httpd_enable_homedirs策略。如果想知道系统中都有什么条件化策略,可以使用下面的命令:
1 2 3 4 5 6 |
#列出系统可用的条件化策略 [ginotang@localhost ~]$ seinfo -b #列出系统可用的条件化策略,并显示策略的当前状态 #这个命令只能通过管理员运行 [root@localhost ginotang]# semanage boolean -l |
条件化策略只是一个布尔值,非真即假,那么改变这个值,实际上改变了什么?依然使用httpd_enable_homedirs作为例子:
1 2 3 4 5 6 7 8 9 10 |
[normal@study ~]$ sesearch -b httpd_enable_homedirs -AC Found 74 semantic av rules: DT allow httpd_sys_script_t cifs_t : dir { getattr search open } ; [ httpd_enable_homedirs use_samba_home_dirs && ] DT allow httpd_sys_script_t cifs_t : dir { ioctl read getattr lock search open } ; [ httpd_enable_homedirs use_samba_home_dirs && ] DT allow httpd_sys_script_t cifs_t : dir { getattr search open } ; [ httpd_enable_homedirs use_samba_home_dirs && ] DT allow httpd_sys_script_t cifs_t : dir { ioctl read getattr lock search open } ; [ httpd_enable_homedirs use_samba_home_dirs && ] DT allow httpd_suexec_t home_root_t : dir { ioctl read getattr lock search open } ; [ httpd_enable_homedirs ] DT allow httpd_sys_script_t cifs_t : file { ioctl read getattr lock open } ; [ httpd_enable_homedirs use_samba_home_dirs && ] DT allow httpd_t user_home_type : dir { getattr search open } ; [ httpd_enable_homedirs ] DT allow httpd_suexec_t autofs_t : dir { getattr search open } ; [ httpd_enable_homedirs use_nfs_home_dirs && ] |
当运行命令之后,会出现一大堆的规则,上面只摘录其中的一部分,规则开头的两个字母例如DT,表示当前规则的状态DT表示Disable True,这是在httpd_enable_homedirs在off状态下的情况。
getsebool和setsebool命令
如果想查看一个条件化策略的布尔值,可以使用getsebool命令,而设置它的值,使用setsebool。
1 2 3 4 5 |
[root@study normal]# getsebool httpd_enable_homedirs #获取当前状态 httpd_enable_homedirs --> off [root@study normal]# setsebool httpd_enable_homedirs on #设置布尔值为真 [root@study normal]# getsebool httpd_enable_homedirs httpd_enable_homedirs --> on |
那么,我们更改了它的值,当它在on状态下,受影响的规则是哪些呢,依然可以使用和上面同样的命令查看:
1 2 3 4 5 6 7 |
[root@study normal]# sesearch -b httpd_enable_homedirs -AC | grep 'ET' ET allow httpd_suexec_t home_root_t : dir { ioctl read getattr lock search open } ; [ httpd_enable_homedirs ] ET allow httpd_t user_home_type : dir { getattr search open } ; [ httpd_enable_homedirs ] ET allow httpd_user_script_t user_home_dir_t : dir { getattr search open } ; [ httpd_enable_homedirs ] ET allow httpd_t user_home_dir_t : lnk_file { read getattr } ; [ httpd_enable_homedirs ] ET allow httpd_user_script_t home_root_t : lnk_file { read getattr } ; [ httpd_enable_homedirs ] ET allow httpd_suexec_t user_home_type : lnk_file { read getattr } ; [ httpd_enable_homedirs ] |
会发现,一部分规则开头的DT(Disable True)变成了ET(Enable True),改变的部分就是条件化策略影响的规则。
相关命令
通过这些命令,可以方便地操作SELinux,它们是seinfo、sestatu、semanage、sesearch、chon、restorecon、getsebool和setsebool。上面的文字中已经涉及到相关命令的简单使用,详细的可以参照man page。
seinfo
没有参数的seinfo命令列出SELinux中的相关信息的数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[root@study conf.d]# seinfo Statistics for policy file: /sys/fs/selinux/policy Policy Version & Type: v.28 (binary, mls) Classes: 94 Permissions: 262 Sensitivities: 1 Categories: 1024 Types: 4747 Attributes: 251 Users: 8 Roles: 14 Booleans: 307 Cond. Expr.: 356 Allow: 101734 Neverallow: 0 Auditallow: 155 Dontaudit: 8846 Type_trans: 17759 Type_change: 74 Type_member: 35 Role allow: 39 Role_trans: 416 Range_trans: 5697 Constraints: 109 Validatetrans: 0 Initial SIDs: 27 Fs_use: 29 Genfscon: 105 Portcon: 602 Netifcon: 0 Nodecon: 0 Permissives: 6 Polcap: 2 |
通过相关参数,可以单独查询上表中的指定信息,例如:seinfo -c,列出系统中所有的classes数量,即上面的94。
sestatus
不带参数的sestatus用于显示当前SELinux的状态信息
1 2 3 4 5 6 7 8 9 10 |
[root@study conf.d]# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 28 |
也可以查询指定信息,例如条件策略的状态:sestatus -b
什么时候使用SELinux
前面我们说过,SELinux一般用于控制提供网络服务或者系统服务的进程的权限,从而使运行linux系统的服务器更加安全。当SELinux正常运行的时候,这些进程就已经受SELinux的约束,因此我们多数情况下是被动使用SELinux,也可以说,认识和学习SELinux,多数情况下都是为了在解决问题的时候提供多一个参考。
一个完整的例子
假设有一台提供web服务的服务器,使用的程序是nginx,该服务器原本就运行了一个网站程序,存放于/usr/share/nginx/html下面,现在希望新增一个网站。我是通过下面的步骤创建这个新网站的:
- 在/usr/share/nginx 目录下创建一个新文件夹名为newsite,用于存放新网站
1 |
[root@study ~]# mkdir -p /usr/share/nginx/newsite |
- 在用户的家目录创建一个新的网站配置文件名为newsite.conf
1 |
[root@study ~]# touch newsite.conf |
- 把上一步创建好的newsite.conf配置文件复制到/etc/nginx/conf.d目录中,并配置好相关信息
1 |
[root@study ~]# mv newsite.conf /etc/nginx/conf.d/ |
准备好后,通过systemctl restart nginx重启服务,发现nginx重启失败,只留下一段错误信息:
1 2 |
[root@study conf.d]# systemctl restart nginx Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details. |
意思是nginx重启失败,通过journalctl -xe命令可以查看相关错误信息,于是我运行journalctl -xe命令,发现了用红色高亮的错误信息:
1 2 |
Jan 12 15:07:57 study.centos.konrida setroubleshoot[4573]: failed to retrieve rpm info for /etc/nginx/conf.d/newsite.conf Jan 12 15:07:58 study.centos.konrida setroubleshoot[4573]: SELinux is preventing /usr/sbin/nginx from read access on the file /etc/nginx/conf.d/newsite.conf. |
很明显是SELinux限制了nginx进程访问该配置文件,从而造成了这样的错误。
追踪问题
首先要知道,SELinux禁止了nginx访问这个文件,说明文件的Security Context肯定不正常,那么就应该用ls -lZ命令查看文件的Security Context信息
1 2 3 |
[root@study conf.d]# ls -lZ -rw-r--r--. root root system_u:object_r:httpd_config_t:s0 default.conf -rw-r--r--. root root unconfined_u:object_r:admin_home_t:s0 newsite.conf |
default.conf是默认的配置文件,newsite.conf是我们添加的,通过对比发现,newsite.conf的type类型是admin_home_t,而default.conf是httpd_config_t,我们猜测nginx要求的type类型应该是httpd_config_t才对,但得到的却是admin_home_t。
问题的产生原因
那么为什么newsite.conf的type类型是admin_home_t呢?实际上,在上面的3个步骤中,从第2个开始就已经会造成这个问题。原因是文件的type类型具有继承性,我们在什么文件夹中创建文件,该文件的type就会继承自父文件夹。
还记得我们是在root的家目录下创建的newsite.conf吗?所以它的类型是admin_home_t。
解决问题
知道问题的原因,就可以着手解决问题了,只要把newsite.conf文件的type改为httpd_config_t就可以了。可以使用chcon(Change Context)命令
1 2 3 4 |
[root@study conf.d]# chcon -t httpd_config_t newsite.conf [root@study conf.d]# ls -Z -rw-r--r--. root root system_u:object_r:httpd_config_t:s0 default.conf -rw-r--r--. root root unconfined_u:object_r:httpd_config_t:s0 newsite.conf |
除了chon命令外,还可以使用restorecon命名,这个命令更加简单,而且可以递归处理文件夹下的所有文件。
1 |
[root@study conf.d]# restorecon -Rv . |
-Rv参数后面有一个点(.)的,即当前文件夹。
延伸阅读
http://linux.vbird.org/linux_basic/0440processcontrol.php#selinux
https://selinuxproject.org/page/NewUsers
https://wiki.gentoo.org/wiki/SELinux
https://wiki.gentoo.org/wiki/SELinux/Tutorials
转载请注明:Pure nonsense » SELinux 详解