runsisi's

technical notes

SELinux 导致 Glance 无法访问 ceph.conf

2019-01-14 runsisi#debug#ceph#openstack#selinux

故障现象

openstack-glance-api 服务能启动,但上传镜像失败,通过 systemctl status openstack-glance-api 命令查看服务状态可能会看到访问 /etc/ceph/ceph.conf 没有权限的错误(不一定能看到这个错误,但正是因为这个关键的错误消息,引领了我们后续的分析方向)。

故障分析

显然,以普通的 rwx 权限的角度来看,glance-api 进程是有 /etc/ceph/ceph.conf 读权限的,通常来说在这种情况下没有权限只有两种情况:

  1. 其它进程锁定了这个文件;
  2. SELinux 给进程附加了权限限制。但在我们的场景下,/etc/ceph/ceph.conf 不大可能被其它进程锁定,因此重点关注后一种情况。

首先查看 SELinux 状态:

~# getenforce
Enforcing

显然已经开启了 SELinux,因此继续往这个方向查,搜索 SELinux 的审计日志,发现有大量的类似如下的日志:

~# vi /var/log/audit/audit.log
...
type=AVC msg=audit(1543226207.699:1013995): avc:  denied  { open } for  pid=40959 comm="glance-api" path="/etc/ceph/ceph.conf" dev="dm-0" ino=6030316 scontext=system_u:system_r:glance_api_t:s0 tcontext=system_u:object_r:tmp_t:s0 tclass=file
...

因此,毫无疑问,是 SELinux 的原因导致 glance-api 进程无法访问 /etc/ceph/ceph.conf。

记录文件 /etc/ceph/ceph.conf 与进程 glance-api 的 SELinux 属性如下:

~# ls -lZ /etc/ceph/ceph.conf
-rw-r--r--. root root system_u:object_r:tmp_t:s0       /etc/ceph/ceph.conf
~# ps aux -Z | grep glance-api
system_u:system_r:glance_api_t:s0 glance  1667  0.5  0.1 592720 102200 ?       Ss   18:06   0:14 /usr/bin/python2 /usr/bin/glance-api
system_u:system_r:glance_api_t:s0 glance  3224  0.0  0.1 597804 94656 ?        S    18:06   0:02 /usr/bin/python2 /usr/bin/glance-api
system_u:system_r:glance_api_t:s0 glance  3225  0.1  0.1 597804 94652 ?        S    18:06   0:02 /usr/bin/python2 /usr/bin/glance-api
system_u:system_r:glance_api_t:s0 glance  3226  0.1  0.1 597804 94656 ?        S    18:06   0:03 /usr/bin/python2 /usr/bin/glance-api
system_u:system_r:glance_api_t:s0 glance  3227  0.1  0.1 597804 94648 ?        S    18:06   0:02 /usr/bin/python2 /usr/bin/glance-api

然后下面分析为何 glance-api 进程无法访问 /etc/ceph/ceph.conf 文件。

在进行后面的分析之前,一定要理解 SELinux 是强加在进程之上的一种约束,默认情况下进程是无所顾忌能够访问系统
所有资源的(当然是 Linux rwx 权限系统允许的情况下),但是 SELinux 类似于一个进程访问系统资源的防火墙,
这个进程防火墙默认禁止进程对系统资源的所有访问权限,然后通过配置访问规则(在 SELinux 中就是 policy)一
点点的放开权限,但与普通防火墙有一个非常大的区别是:只有加载了进程的访问规则,该进程才受 SELinux 的限制,
即进程访问规则虽然是用来放开权限的,但是如果 SELinux 没有加载进程访问规则,则不会对进程的访问做任何限制。

因此规避 SELinux 的限制存在两个方法:

1. 修改进程的程序文件名;
2. 不安装程序对应的 selinux 按照包(里面包含进程访问规则,通常程序会依赖这个 selinux 包,因此这种安装
方式需要类似 --nodeps 之类的强制安装方式选项)。

查询 SELinux 是否加载了 glance 对应的访问规则:

~# semodule -l | grep glance
glance  1.1.0
os-glance       0.1

通过 rpm -qa | grep selinux 大致能猜到规则文件所在的安装包:

~# rpm -ql selinux-policy-targeted | grep glance
/etc/selinux/targeted/modules/active/modules/glance.pp
~# ls /etc/selinux/targeted/modules/active/modules/ | grep glance
glance.pp
os-glance.pp

分析文件数据,并得到最终的 pp 文件(访问规则文件):

~# file /etc/selinux/targeted/modules/active/modules/glance.pp
/etc/selinux/targeted/modules/active/modules/glance.pp: bzip2 compressed data, block size = 900k
~# bzcat glance.pp > glance.d
~# file glance.d
glance.d: SE Linux modular policy version 1, 2 sections, mod version 17, MLS, module name glance\005

用 SELinux 提供的工具查看规则的具体信息:

~# sedismod glance.d
Reading policy...
libsepol.policydb_index_others: security: 0 users, 2 roles, 94 types, 4 bools
libsepol.policydb_index_others: security: 1 sens, 1024 cats
libsepol.policydb_index_others: security: 52 classes, 0 rules, 0 cond rules
libsepol.policydb_index_others: security: 0 users, 2 roles, 94 types, 4 bools
libsepol.policydb_index_others: security: 1 sens, 1024 cats
libsepol.policydb_index_others: security: 52 classes, 0 rules, 0 cond rules
Binary policy module file loaded.
Module name: glance
Module version: 1.1.0
...
Command ('m' for menu): 6
...
glance_domain [1]: attribute for types glance_registry_t, glance_api_t, glance_scrubber_t flags:0
...
Command ('m' for menu): 1
...
allow glance_domain [etc_t] : [dir] { getattr search open };
...

理解上面的信息需要理解 SELinux type 和 attribute 的关系,简单点说就是既可以给 type 单独定义规则,也可以给 attribute 定义规则,然后将 attribute 附加到多个 type 上。在这里 glance_domainglance_api_t 等多个 type 的 attribute,而 glance_domain 是允许访问 etc_t 的,因此 glance_api_t 能够访问 etc_t,同样从上面的输出中,我们同样可以看到 glance_api_t 是没有 tmp_t 访问权限的。而根据前面记录的 glance-api 进程和 /etc/ceph/ceph.conf 文件的 SELinux 属性,显然 glance-api 进程无法访问具有 tmp_t SELinux 属性的 /etc/ceph/ceph.conf 文件。

通过同样的方法,可以继续分析 os-glance.pp 文件定义的规则,能看到这个规则还是有些用处的。

解决方案

恢复 /etc/ceph/ceph.conf 文件应有的 SELinux 属性,然后重启 openstack-glance-api 服务即可:

~# restorecon -v /etc/ceph/ceph.conf
restorecon reset /etc/ceph/ceph.conf context system_u:object_r:tmp_t:s0->system_u:object_r:etc_t:s0
~# ls -lZ /etc/ceph/ceph.conf
-rw-r--r--. root root system_u:object_r:etc_t:s0       /etc/ceph/ceph.conf

当然更根本的原因需要追究为何本应该是 etc_t 的 /etc/ceph/ceph.conf 文件变成了 tmp_t(文件夹下的文件默认继承文件夹的 SELinux 属性,在这里就是 etc_t),这个需要阅读[1]。

延伸分析

为何 Cinder、Nova 没有这个问题?

通过上面类似的分析手段,我们把 Glance、Cinder、Nova 的 SELinux 规则列出如下:

Glance:
    Line 65:   allow glance_log_t [tmp_t] : [filesystem] { associate };
    Line 152:   allow glance_registry_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 153:   type_transition glance_registry_t [tmp_t] : { [dir] [file] }  glance_registry_tmp_t;
    Line 171:   allow glance_api_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 172:   type_transition glance_api_t [tmp_t] : { [dir] [file] }  glance_tmp_t;

    Line 140:   allow glance_domain [etc_t] : [dir] { getattr search open };

Cinder:
    Line 49:   allow cinder_api_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 50:   type_transition cinder_api_t [tmp_t] : { [dir] [file] }  cinder_api_tmp_t;
    Line 59:   allow cinder_backup_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 60:   type_transition cinder_backup_t [tmp_t] : { [dir] [file] }  cinder_backup_tmp_t;
    Line 69:   allow cinder_scheduler_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 70:   type_transition cinder_scheduler_t [tmp_t] : { [dir] [file] }  cinder_scheduler_tmp_t;
    Line 79:   allow cinder_volume_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 80:   type_transition cinder_volume_t [tmp_t] : { [dir] [file] }  cinder_volume_tmp_t;
    Line 82:   allow cinder_log_t [tmp_t] : [filesystem] { associate };

    Line 203:   allow cinder_domain [etc_t] : [dir] { getattr search open };
    Line 293:   allow cinder_api_t [etc_t] : [dir] { getattr search open };
    Line 365:   allow cinder_backup_t [etc_t] : [dir] { getattr search open };
    Line 437:   allow cinder_scheduler_t [etc_t] : [dir] { getattr search open };
    Line 521:   allow cinder_volume_t [etc_t] : [dir] { getattr search open };

Nova:
    Line 51:   allow nova_t [tmp_t] : [dir] { ioctl read write getattr lock add_name remove_name search open };
    Line 52:   type_transition nova_t [tmp_t] : { [lnk_file] [dir] [file] }  nova_tmp_t;
    Line 57:   allow nova_log_t [tmp_t] : [filesystem] { associate };
    Line 187:   allow nova_domain [tmp_t] : [dir] { getattr search open };
    Line 188:   allow nova_domain [tmp_t] : [lnk_file] { read getattr };
    Line 189:   allow nova_domain [tmp_t] : [dir] { getattr search open };

    Line 195:   allow nova_domain [etc_t] : [dir] { getattr search open };

显然,原因一目了然。

此外,如果机器上没有 sedismod 等工具的话(SELinux 相关的工具都在 checkpolicy, policycoreutils, policycoreutils-python, setools-console 等 4 个主要的安装包中),可以直接访问 https://github.com/SELinuxProject/refpolicy/tree/master/policy (这实际上就是 selinux-policy-targeted 的上游源代码),或者也可以访问 http://vault.centos.org/ 获取 src.rpm 源代码包查找原始的规则定义。

[1] SELinux with cp/mv

https://danwalsh.livejournal.com/56534.html