Mctrain's Blog

What I learned in IT, as well as thought about life

XSM-FLASK学习笔记

| Comments

XSM-FLASK全称为:Xen Security Modules - FLux Advanced Security Kernel。这篇博文对其进行一个简单的介绍,资料主要翻译自这里

XSM是Xen提供的一个安全框架,允许管理者对整个系统进行细粒度的控制,换句话说,即运行管理者定义一套规则来管理虚拟机之间,虚拟机与Xen之间的交互,以及对系统资源(memory,device)的访问。

FLASK是XSM中的一个模块实现,当然,之后可能还有其他的模块,那就是后话了。下面是一些例子,列举了XSM-FLASK可以做的几件事:

  • 禁止两台虚拟机之间通过event channel和grant table进行通信;
  • 将一些需要特权级别的操作安全地grant给某些非特权级虚拟机;
  • 控制哪些虚拟机能使用device passthrough;
  • 限制或者审计特权虚拟机中进行的某些特定操作;
  • 限制特权虚拟机对其他虚拟机的任意内存映射;
  • 将hypervisor中的不同模块(比如qemu和xenstore)进行隔离,防止它们之间互相影响。

以上是一些基本说明,下面会具体介绍如何使用FLASK,以及FLASK中规则的一些语法定义。


FLASK及其policy的编译流程

在Xen 4.3之后的版本都对FLASK进行了比较全面的支持,如果要开启FLASK,需要在编译Xen之前修改Config.mk文件,将XSM_ENABLEFLASK_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。


启动Xen(with FLASK)

在重启机器之前,我们需要在grub的配置中加上flask的选项,修改/etc/default/grub文件:

GRUB_CMDLINE_XEN_DEFAULT="flask=<OPTION>"

其中,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

利用XSM security label创建虚拟机

当通过上面所提到的方式启动Xen之后,FLASK也就开启了,那么我们在创建虚拟机的时候就需要在其配置中增加一个security label(安全标签),否则,该虚拟机会被标记为“unlabeled”,例子如下所示:

seclabel='system_u:system_r:domU_t'

安全标签有user,roletype表示,这些会在之后进行介绍。另外我们需要在policy中对相应的主体进行正确的权限设置。对于“unlabeled”的虚拟机,如果FLASK并没进入enforcing模式,或者进入了enforcing模式但定义了相关标签的权限,则不会有问题,否则,在其调用某些操作的时候会被禁止。

通过xl list -Z命令可以查看当前虚拟机所具有的安全标签。

通过xl dmesg | grep avc命令可以查看FLASK相关的log记录。


FLASK policies

增加一个security module

我们可以把security module(安全模块)当做将一系列规则进行封装所产生的集合,如果要增加一个自定义的安全模块,我们需要在$XEN/tools/flask/policy/policy/modules.conf文件中加一行:

<module_name> = on

同时在$XEN/tools/flask/policy/policy/modules/<module_name>目录下增加两个文件:

<module_name>.te
<module_name>.if

其中.te文件定义了相应规则的的描述,而.if文件定义了一系列在.te文件中会被用到的宏(macros)。

在XSM-FLASK中有一个默认的模块:xen。如果存在多个模块(比如用户自己定义了多个模块),那么这些模块中不能有重复的typerole的定义。当模块定义好之后,可以参照’FLASK policy的编译流程’进行编译,并将其放在目标目录中(如/boot/flask),这样在xen启动的时候就会加载,或者通过xl loadpolicy进行手动加载。

定义Types, roles, users和attributes

在安全模块中会定义许多规则,对于一个特定的规则,说白了就是规定了某个主体(source subject)对另一个主体(target subject)进行的一系列访问和操作(如hypercall)的权限(deny or allow),比如规定:

某个集合中的虚拟机(source)不能向(deny)虚拟机监控器Xen(target)调用某个hypercall(operation)

那么这些集合就需要通过一系列的层级进行定义,也就引入了接下来需要讨论的type, role, usersattributes。可以结合$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 <attributes>;

比如在示例文件中,定义了一个type

type xen_t, xen_type, mls_priv;

其中xen_t即为type的标示符,而后面的xen_typemls_priv则是相应的attribute,也就是说每个type可能会带有多个不同的attributes

当我们需要定义一个规则的时候,可以通过type来指定对应的源和目标主体。比如需要定义某个hypercall的调用是被允许的,可以这么写:

allow <source type> <target type>:<security class> <hypercall>;

其中,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 };

可以看到,roletypes是采用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。

增加Policy constraints

FLASK可以通过在$XEN/tools/flask/policy/policy/constraints文件中定义规则来限制某些操作。在默认的情况下,FLASK规则定义了两条constraints(限制规则)来防止不同用户之间event channel和grant table的交互。一条限制规则语法如下:

constrain <security class> { <hypercall> } ( expression );

比如例子中的:

constrain grant { map_read map_write copy } (
  u1 == system_u or
  u2 == system_u or
  u1 == u2
);

这条限制规则表示如果属于grant class的这些hypercall可以被执行,当且仅当expression中的条件成立。其中,expression可以包含之前定义好的所有user, roletype主体,其语法规则如下:

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 classes

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最基本的介绍,我也还正处于学习阶段,其它更细节的部分和更直观的实例会在以后的博文中进行说明。

Comments