XSM-FLASK全称为:Xen Security Modules - FLux Advanced Security Kernel。这篇博文对其进行一个简单的介绍,资料主要翻译自这里。
XSM是Xen提供的一个安全框架,允许管理者对整个系统进行细粒度的控制,换句话说,即运行管理者定义一套规则来管理虚拟机之间,虚拟机与Xen之间的交互,以及对系统资源(memory,device)的访问。
FLASK是XSM中的一个模块实现,当然,之后可能还有其他的模块,那就是后话了。下面是一些例子,列举了XSM-FLASK可以做的几件事:
以上是一些基本说明,下面会具体介绍如何使用FLASK,以及FLASK中规则的一些语法定义。
在Xen 4.3之后的版本都对FLASK进行了比较全面的支持,如果要开启FLASK,需要在编译Xen之前修改Config.mk
文件,将XSM_ENABLE
和FLASK_ENABLE
设成y
,然后再开始编译。
编译完Xen之后,需要编译FLASK的policy,在这之前需要先安装checkpolicy:
$ aptitude install checkpolicy
然后编译:
$ cd $XEN
$ make -C tools/flask/policy
之后会在$XEN/tools/flask/policy
目录下生成一个叫做xenpolicy-$XEN_FULLVESION
的文件,这个就是生成的flask policy。
在重启机器之前,我们需要在grub的配置中加上flask的选项,修改/etc/default/grub
文件:
GRUB_CMDLINE_XEN_DEFAULT="flask=
其中,OPTIONs包括:
permissive
表示:如果在bootloader阶段找到了一个policy,则会被加载;如果没有,或者发生错误,错误报告会被写到一个buffer,但是不会阻止系统启动。该模式可以通过xl setenforce
改为enforcing
模式;enforcing
表示:在创建domain0之前会强制要求提供一个policy,否则无法启动系统;late
表示:在bootloader阶段不会load相关的policy,可以在系统启动之后通过xl loadpolicy
加载相应的policy,一旦policy被加载则进入enforcing模式;disabled
表示:XSM会被设成dummy module,该模块和没有编译XSM所产生的效果是一样的,另外,一旦采用这个模式之后,FLASK是无法被重新加载的。需要注意的一点是,FLASK的policy需要被放在一个grub可以访问的目录下,如/boot/flask/
,否则FLASK不会开启。另外,该policy选项需要写入grub的配置中,放在multiboot下面,如下所示:
multiboot /boot/xen-VERSION.gz dom0_mem=1024M,max:1024M flask=enforcing
module /boot/vmlinuz-X.Y-amd64 root=/dev/mapper/vg_system-root ro quiet
module /boot/initrd.img-X.Y
module /boot/flask/xenpolicy-VERSION
当通过上面所提到的方式启动Xen之后,FLASK也就开启了,那么我们在创建虚拟机的时候就需要在其配置中增加一个security label(安全标签),否则,该虚拟机会被标记为“unlabeled”,例子如下所示:
seclabel='system_u:system_r:domU_t'
安全标签有user
,role
和type
表示,这些会在之后进行介绍。另外我们需要在policy中对相应的主体进行正确的权限设置。对于“unlabeled”的虚拟机,如果FLASK并没进入enforcing模式,或者进入了enforcing模式但定义了相关标签的权限,则不会有问题,否则,在其调用某些操作的时候会被禁止。
通过xl list -Z
命令可以查看当前虚拟机所具有的安全标签。
通过xl dmesg | grep avc
命令可以查看FLASK相关的log记录。
我们可以把security module(安全模块)当做将一系列规则进行封装所产生的集合,如果要增加一个自定义的安全模块,我们需要在$XEN/tools/flask/policy/policy/modules.conf
文件中加一行:
= on
同时在$XEN/tools/flask/policy/policy/modules/
目录下增加两个文件:
.te
.if
其中.te
文件定义了相应规则的的描述,而.if
文件定义了一系列在.te
文件中会被用到的宏(macros)。
在XSM-FLASK中有一个默认的模块:xen。如果存在多个模块(比如用户自己定义了多个模块),那么这些模块中不能有重复的type
和role
的定义。当模块定义好之后,可以参照’FLASK policy的编译流程’进行编译,并将其放在目标目录中(如/boot/flask
),这样在xen启动的时候就会加载,或者通过xl loadpolicy
进行手动加载。
在安全模块中会定义许多规则,对于一个特定的规则,说白了就是规定了某个主体(source subject)对另一个主体(target subject)进行的一系列访问和操作(如hypercall)的权限(deny or allow),比如规定:
某个集合中的虚拟机(source)不能向(deny)虚拟机监控器Xen(target)调用某个hypercall(operation)
那么这些集合就需要通过一系列的层级进行定义,也就引入了接下来需要讨论的type
, role
, users
和attributes
。可以结合$XEN/tools/flask/policy/policy/modules/xen.te
文件中的例子进行更具体的了解。
Policy Attribute
attribute
定义了一个抽象的属性,它可以被附属在接下来要介绍的type
主体上,即表示某个type
具备哪些attributes
。
Policy Type
type
是整个policy定义规范中最低的一个主体级别,它可以被用来在某个规则中指定source和target的类型。定义type
的方式是:
type new_type_t ;
比如在示例文件中,定义了一个type
:
type xen_t, xen_type, mls_priv;
其中xen_t
即为type
的标示符,而后面的xen_type
和mls_priv
则是相应的attribute
,也就是说每个type
可能会带有多个不同的attributes
。
当我们需要定义一个规则的时候,可以通过type
来指定对应的源和目标主体。比如需要定义某个hypercall的调用是被允许的,可以这么写:
allow
其中,security class
会在之后介绍,简单来说,它定义了一系列具有相关性的hypercall的集合。一个具体的例子:
allow dom0_t security_t:security check_context;
定义了dom0_t
type的主体可以向security_t
type主体调用security class
中的check_context
hypercall。
另外,如果同时定义多个同一个class中的hypercall,可以用{}
将其括起来,例如:
allow dom0_t dom0_t:resource { add remove };
除了用type
表示的主体,我们也可以直接用attribute
来表示主体,如:
allow domain_type xen_t:xen tmem_op;
即表示所有具有domain_type属性的type主体都可以向xen_t
type主体调用xen class
中的tmem_op
hypercall。
Policy Role
role
是处于type
上一层级的主体级别,用户可以定义某个role
由多个types
组成,比如:
role system_r
role system_r types { xen_type domain_type };
可以看到,role
的types
是采用attribute
的方式定义的,即定义具有某个attribute
的所有types
都属于这个role
。如果要定义具有某个attribute
的除掉某个type
的所有types
,则通过在该type
之前加上一个-
进行标示,如:
role vm_r
role vm_r types {domain_type -dom0_t };
Policy Users
user
作为policy中的最高层级的主体级别,它并不被定义在.te
文件中,它们是被定义在$XEN/tools/flask/policy/policy/users
文件中。因此我们可以跨安全模块共用同一个user
。在默认情况下,FLASK定义了三个users:system_u, customer_t和customer_2。
FLASK可以通过在$XEN/tools/flask/policy/policy/constraints
文件中定义规则来限制某些操作。在默认的情况下,FLASK规则定义了两条constraints(限制规则)来防止不同用户之间event channel和grant table的交互。一条限制规则语法如下:
constrain { } ( expression );
比如例子中的:
constrain grant { map_read map_write copy } (
u1 == system_u or
u2 == system_u or
u1 == u2
);
这条限制规则表示如果属于grant class
的这些hypercall可以被执行,当且仅当expression中的条件成立。其中,expression可以包含之前定义好的所有user
, role
和type
主体,其语法规则如下:
expression : (expresion)
| not expression
| expression and expression
| expression or expression
| u1 op u2
| r1 role_op r2
| t1 op t2
| u1 op names
| u2 op names
| r1 op names
| r2 op names
| t1 op names
| t2 op names
op : == | !=
role_op : == | != | eq | dom | domby | incomp
names : name | { name_list }
name_list : name | name_list name
security class
被定义在$XEN/xen/xsm/flask/policy/access_vectors
文件中,每个hypercall被分配在了其中一个class中,需要注意的是,每一个class最多只能有32个hypercalls。下面简单介绍下默认的几个classes,相关的hypercall的描述可以直接看这里。
class xen
包含了所有在hypervisor中进行的操作,其source为执行hypercall的domain,target为xen (xen_t
type);class domain & class domain2
包含了某个domain调用另一个domain或者调用自己的操作,source为执行hypercall的domain,target为被调用的domain(包括_self
和_target
的type);class hvm
类似于domain
,除了它是针对HVM domain;class event
用于描述event channels;class grant
用于描述grant mapping;class mmu
用于描述不是采用grant机制映射的内存页;class shadow
(这个不清楚是干嘛的);class resource
用于描述硬件设备passthrough所使用的资源,包括IRQ, MMIO regions, I/O ports, PCI device等;class security
用于描述和FLASK相关的操作。以上即为XSM-FLASK最基本的介绍,我也还正处于学习阶段,其它更细节的部分和更直观的实例会在以后的博文中进行说明。