故障现象
openstack-glance-api 服务能启动,但上传镜像失败,通过 systemctl status openstack-glance-api 命令查看服务状态可能会看到访问 /etc/ceph/ceph.conf 没有权限的错误(不一定能看到这个错误,但正是因为这个关键的错误消息,引领了我们后续的分析方向)。
故障分析
显然,以普通的 rwx 权限的角度来看,glance-api 进程是有 /etc/ceph/ceph.conf 读权限的,通常来说在这种情况下没有权限只有如下几种情况:
- 其它进程锁定了这个文件;
- 通过 setfacl 给文件加上了特殊的权限(注意
ls -l
输出中权限一栏中的+
); - 文件夹没有
x
权限; - SELinux 给进程附加了权限限制;
但在我们的场景下,排除了前面三种原因,因此重点关注最后一种情况。
首先查看 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_domain
是 glance_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
[2] Difference between chmod vs ACL
https://unix.stackexchange.com/questions/364517/difference-between-chmod-vs-acl
最后修改于 2019-01-14