需求比较简单,就是编写独立的规则允许 OpenStack 的 qemu 进程能够访问(完全控制) ceph 的客户端 admin socket,之所以需要独立的规则(或者说模块),而不是直接修改 ceph-selinux 中定义的规则,是因为 ceph-selinux 依赖 ceph-base,我们在客户端节点通常来说只会安装 python-rbd 之类的客户端必须的包,以及 ceph-common 等基础包,而 ceph-base 实际上是服务端所需的包,而且它自身又有其它的依赖,因此不适合在客户端进行安装。
在分离部署和融合部署两种情况下,/var/run/ceph 目录的 SELinux 标签(label)是不同的,在分离部署下是 var_run_t
,即默认的标签,而在融合部署情况下,ceph-selinux 会执行 fixfiles/restorecon 将 /var/run/ceph 打上新的标签 ceph_var_run_t
,因此在写规则时我们要考虑到这两种情况:
~# vi librados.te
policy_module(librados, 0.0.1)
require {
type virtd_t;
type var_run_t;
type ceph_var_run_t;
}
manage_sock_files_pattern(virtd_t, var_run_t, var_run_t)
manage_sock_files_pattern(virtd_t, ceph_var_run_t, ceph_var_run_t)
然后进行编译(注意不要使用 checkmodule 等网上场景的方式(见第一个参考链接),因为 checkmodule 不会使用到预定义的宏和接口,需要完全手写 allow 规则):
~# rpm -qa | grep selinux-policy-devel
selinux-policy-devel-3.13.1-60.el7.noarch
~# rpm -ql selinux-policy-devel-3.13.1-60.el7.noarch | grep Makefile
/usr/share/selinux/devel/Makefile
/usr/share/selinux/devel/include/Makefile
# 注意这里的 .pp 后缀,makefile 文件会在当前目录寻找 librados.te 的 te 文件,并编译成二进制的 pp 文件
# 另外这里的 librados 文件名并不需要与 te 文件中的模块名一致
~# make -f /usr/share/selinux/devel/Makefile librados.pp
然后可以用 semodule 对编译后得到的二进制规则文件进行测试、验证。
但是,上面测试是我在安装了 ceph-selinux 包的测试结果,如果在没有安装 ceph-selinux 包的机器进行测试,会得到如下的错误:
# semodule -i librados.pp
libsepol.print_missing_requirements: librados's global requirements were not met: type/attribute ceph_var_run_t (No such file or directory).
libsemanage.semanage_link_sandbox: Link packages failed (No such file or directory).
semodule: Failed!
显然,ceph_var_run_t
是在 ceph-selinux 中的 ceph.pp 定义的,我们没有安装,自然 librados.te 中 require 的 ceph_var_run_t
找不到定义。
解决方法就是 SELinux 的 optional 规则:
~# vi librados2.te
policy_module(librados2, 0.0.2)
optional {
require {
type virtd_t;
type var_run_t;
type ceph_var_run_t;
}
manage_sock_files_pattern(virtd_t, var_run_t, var_run_t)
manage_sock_files_pattern(virtd_t, ceph_var_run_t, ceph_var_run_t)
}
然后同样的可以用 semodule 对编译后得到的二进制规则文件进行测试、验证。
显然上面的情况仍然可能会出现所需的依赖 type 未定义的情况,而像下面的组合就能考虑到各种未定义的情况:
~# vi librados3.te
policy_module(os-librados3, 0.0.3)
optional {
require {
type virtd_t;
type var_run_t;
}
manage_sock_files_pattern(virtd_t, var_run_t, var_run_t)
}
optional {
require {
type virtd_t;
type ceph_var_run_t;
}
manage_sock_files_pattern(virtd_t, ceph_var_run_t, ceph_var_run_t)
}
optional {
require {
type glance_api_t;
type var_run_t;
}
manage_sock_files_pattern(glance_api_t, var_run_t, var_run_t)
}
optional {
require {
type glance_api_t;
type ceph_var_run_t;
}
manage_sock_files_pattern(glance_api_t, ceph_var_run_t, ceph_var_run_t)
}
上面使用 optional 规则确实可以解决依赖的问题,而且可以同时解决在分离部署和融合部署两种场景下 /var/run/ceph(/.*)?
存在两种不同标签(var_run_t
和 ceph_var_run_t
),但是是不是不需要这么复杂呢,我们可以换另外一种思路,为客户端的 .asok 文件独立定义一种 file context 类型,并且根据这种 file context 定义 type enforcement,然后让 librados 或者 ceph-common 包在安装时安装这个规则模块。
定义 file context 类型(注意 .fc 文件中的文件名匹配是标准的正则表达式,而不是普通的 glob 匹配规则):
~# vi os-librados3.fc
/var/run/ceph/.*\.casok gen_context(system_u:object_r:librados_var_run_t,s0)
定义 type enforcement(为了简化,去掉了 glance_api_t
):
~# vi os-librados3.te
policy_module(os-librados3, 0.0.3)
type librados_var_run_t;
files_pid_file(librados_var_run_t)
optional {
require {
type virtd_t;
}
manage_sock_files_pattern(virtd_t, librados_var_run_t, librados_var_run_t)
}
编译:
~# make -f /usr/share/selinux/devel/Makefile os-librados3.pp
Compiling targeted os-librados3 module
/usr/bin/checkmodule: loading policy configuration from tmp/os-librados3.tmp
/usr/bin/checkmodule: policy configuration loaded
/usr/bin/checkmodule: writing binary representation (version 17) to tmp/os-librados3.mod
Creating targeted os-librados3.pp policy package
rm tmp/os-librados3.mod tmp/os-librados3.mod.fc
安装:
~# semodule -i os-librados3.pp
~# grep librados /etc/selinux/targeted/modules/active/file_contexts
/var/run/ceph/.*\.casok system_u:object_r:librados_var_run_t:s0
~# grep librados /etc/selinux/targeted/contexts/files/file_contexts
/var/run/ceph/.*\.casok system_u:object_r:librados_var_run_t:s0
测试:
# 此时还没有安装 ceph.pp,因此只会更新 *.casok 文件的 label,而 *.asok 文件仍然保持系统默认的 var_run_t label
~# restorecon -FR /var/run/ceph/
~# ll /var/run/ceph/ -Zd
drwxr-xr-x. root root system_u:object_r:var_run_t:s0 /var/run/ceph/
~# ll /var/run/ceph/ -Z
-rw-r--r--. root root system_u:object_r:librados_var_run_t:s0 1.casok
-rw-r--r--. root root system_u:object_r:var_run_t:s0 2.asok
# 安装 ceph.pp,但是并不会自动修改已有文件的 label
~# semodule -i ceph.pp
# restorecon 根据新的 file context 定义更新文件 label
~# restorecon -FR /var/run/ceph/
~# ll /var/run/ceph/ -Zd
drwxr-xr-x. root root system_u:object_r:ceph_var_run_t:s0 /var/run/ceph/
~# ll /var/run/ceph/ -Z
-rw-r--r--. root root system_u:object_r:librados_var_run_t:s0 1.casok
-rw-r--r--. root root system_u:object_r:ceph_var_run_t:s0 2.asok
虽然我们通过前面的 os-librados3.fc 将 /var/run/ceph/*.casok
文件定义成了 librados_var_run_t
type,且 restorecon 能正确的更新 label,但是我们忽视了一个非常大的问题,新创建的文件默认继承了父目录的 label,除非显式的使用 filetrans_pattern
接口定义转换规则,但是 filetrans_pattern
接口需要传递父目录的 type,而在我们分离和融合部署两种场景下,父目录的 type 可能为 var_run_t
也可能为 ceph_var_run_t
,因此,我们并不能使用上面这种方法,最终我们还是只能使用 os-librados3.te 定义的方法,但是注意到由于 sVirt 的引入,os-librados3.te 需要做如下的修改(还是考虑不支持 glance_api_t
,即只支持 virtd_t
和 svirt_t
两种 process domain):
~# vi librados.te
policy_module(librados, 0.0.4)
optional {
require {
type virtd_t;
type var_run_t;
}
manage_sock_files_pattern(virtd_t, var_run_t, var_run_t)
}
optional {
require {
type virtd_t;
type ceph_var_run_t;
}
manage_sock_files_pattern(virtd_t, ceph_var_run_t, ceph_var_run_t)
}
optional {
require {
type svirt_t;
type var_run_t;
}
manage_sock_files_pattern(svirt_t, var_run_t, var_run_t)
}
optional {
require {
type svirt_t;
type ceph_var_run_t;
}
manage_sock_files_pattern(svirt_t, ceph_var_run_t, ceph_var_run_t)
}
参考资料
[1] How to compile a SELinux policy package
https://relativkreativ.at/articles/how-to-compile-a-selinux-policy-package
[2] Policy development: optional policy
http://selinux-mac.blogspot.com/2009/07/policy-development-optional-policy.html
[3] SELinux - canonical way of automatically applying a context on file creation
[4] SELinuxFileNameTransition
https://fedoraproject.org/wiki/Features/SELinuxFileNameTransition
[5] What is sVirt?
https://rwmj.wordpress.com/2011/05/24/what-is-svirt/
最后修改于 2019-01-19