KVM 安全策略配置与维护实战

如今,计算机信息安全越来越受到人们的重视,在计算机相关的各种学术会议、论文、期刊中“安全”(security)一词被经常提及。

 因为KVM是Linux系统中的一个虚拟化相关的模块,qemu-kvm是Linux系统上一个普通的用户空间进程,所以Linux系统上的各种安全技术、策略对QEMU/KVM都是适用的。根据经验选择了其中的一些技术来介绍一下KVM的安全技术。

1、SMEP

有一些安全渗透(exploitation)攻击,会诱导系统在最高执行级别(ring0)上访问在用户空间(ring3)的数据和执行用户空间的某段恶意代码,从而获得系统的控制权或使系统崩溃。SMEP(Supervision Mode Execution Protection,监督模式执行保护),是Intel在2012年发布的代号为"Ivy Bridge"的新一代CPU上提供的一个安全特性。当控制寄存器CR4寄存器的20位(第21位)被设置为1时,表示SMEP特性是打开状态。SMEP特性让处于管理模式(supervisor mode,当前特权级别CPL<3)的程序不能获取用户模式(user mode,CPL=3)可以访问的地址空间上的指令,如果管理模式的程序试图获取并执行用户模式的内存上的指令,则会发生一个错误(fault),不能正常执行。

在SMEP特性的支持下,在运行管理模式的CPU不能执行用户模式的内存页,可以较好地阻止前面提到的那种渗透攻击,而且由于SMEP是CPU的一个硬件特性,这种安全保护非常方便和高效,其带来的性能开销几乎可以忽略不计。

目前,Linux 3.0之后的linux系统和Windows 8系统都已经支持SMEP的特性,而且一些Linux发行版(如RHEL 6.2/6.3)尽管使用的是Linux 2.6.32为基础的内核,但是也都向后移植(backport)了SMEP的特性。而较新的KVM(如Linux 3.4)和qemu-kvm(如1.2.0)让KVM客户机中也支持了SMEP特性。下面介绍一下如何让KVM客户机也可以使用SMEP的步骤。

检查宿主机中的CPU对SMEP特性支持,命令行如下所示:

[root@jay-linux kvm_demo]# cat /proc/cpuinfo  | grep smep
flags:     fpu vme de pse tsc msr pae mce cx8 api
pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht 
tm pbe syscall nx rdtscp lm constant tsc arch perfmon pebs bts 
rep_good nopl xtopology nonstop_tsc aperfmperf pni pclmulqdq 
dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 
sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c 
rdrand lahf lm ida arat epb xsaveopt pln pts dtherm tpr_shadow 
vnmi flexpriority ept vpid fsgsbase smep erms 
<!-- 省略其余逻辑 CPU的输出信息 -->

加上"-cpu host"参数来启动客户机,命令行如下: 

[root@jay-linux kvm_demo]# qemu-system-x86_64 rhel6u3.img -m 1024 -net nic -net tap -cpu host
VNC server running on ':1:5900'

在客户机中检查其CPU是否有SMEP特性的支持,命令行如下: 

[root@kvm-guest ~]# cat /proc/cpuinfo | grep smep 
flags:     fpu vme de pse tsc msr pae mce cx8 apic mtrr pge
mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx rdtscp 
lm constant_tsc up arch_perfmon rep_good unfair_spinlock pni 
pclmulqdq ssse3 cx16 sse4_1 sse4_2 x2apic popcnt aes xsave avx 
f16c rdrand hypervisor lahf lm xsaveopt fsgsbase smep erms

由上面的输出信息可知,客户机中已经能够检测到SMEP特性了,从而使客户机成为一个有Intel CPU的SMEP特性支持的较为安全的环境了。 

2、控制客户机的资源使用——cgroups

在KVM虚拟化环境中,每个客户机操作系统使用系统的一部分物理资源(包括处理器、内存、磁盘、网络带宽等)。当一个客户机对资源的消耗过大时(特别是qemu-kvm启动客户机时没有能够控制磁盘或网络I/O的选项),它可能会占用该系统的大部分资源,此时,其他的客户机对相同资源的请求就会受到严重影响,可能会导致其他客户机响应速度过慢甚至失去响应。

为了让所有客户机都能够按照预先的比例来占用物理资源,我们需要对客户机能使用的物理资源做控制。通过qmue-kvm命令行启动客户机时,可以用"-smp num"参数来控制客户机的CPU个数,使用"-m size"参数来控制客户机的内存大小,不过,这些都是比较粗粒度的控制,例如,不能控制客户机仅使用1个CPU的50%的资源,而且对磁盘I/O、网络I/O等并没有直接的参数来控制。由于每个客户机就是宿主机Linux系统上的一个普通qemu-kvm进程,所以可以通过控制qemu-kvm进程使用的资源来达到控制客户机的目的。

1. cgroups简介

cgroups(即control groups,控制群组)是Linux内核中的一个特性,用于限制、记录和隔离进程组(process groups)对系统物理资源的使用。cgroups最初是由Google的一些工程师(Paul Menage、Rohit Seth等人)在2006年以“进程容器”(process container)的名字实现的,在2007年被重命名为“控制群组”(Control Groups),然后被合并到Linux内核的2.6.24版本之中。

在加入到Linux内核的主干之后,cgroups越来越成熟,有很多新功能和控制器(controller)被键入进去,其功能也越来越强大了。cgroups为不同的用户场景提供了一个统一的接口,这些用场景包括对单一进程的控制,也包括像OpenVZ、LXC(Linux Containers)等操作系统级别的虚拟化技术。一些较新的比较流行的Linux发行版,如RHEL 6.0、RHEL 6.3、Fedora 17、Ubuntu 12.10等,都提供了对cgroups的支持。

cgroups提供了如下的一些功能:

  1. 资源限制(Resource limiting),让进程组被设置为使用不能超过某个界限的资源数量。如内存子系统可以为进程组设定一个内存使用的上限,一旦进程组使用的内存达到限额后,如果再申请内存就会发生缺乏内存的错误(即:OOM,out of memory)。
  2. 优先级控制(Prioritization),让不同的进程组有不同的优先级。可以让一些进程组占用较大的CPU或磁盘I/O吞吐量的百分比,另一些进程组占用较小的百分比。
  3. 记录(Accounting),衡量每个进程组(包括KVM客户机)实际占用的资源数量,可以用于对客户机用户进行收费等目的,如使用cpuacct子系统记录某个进程组使用的CPU时间。
  4. 隔离(Isolation),对不同的进程组使用不同的命名空间(namespace),不同的进程组之间不能看到相互的进程、网络连接、文件访问等信息,如使用ns子系统就可以使不同的进程组使用不同的命名空间。
  5. 控制(Control),控制进程组的暂停、添加检查点、重启等,如使用freezer子系统可以将进程组挂起和恢复。

cgroups中有如下几个重要的概念,理解它们是了解cgroups的前提条件。

  1. 任务(task):在cgroups中,一个任务就是Linux系统上的一个进程或线程,可以将任务添加到一个或多个控制群组中。
  2. 控制群组(control group):一个控制群组就是按照某种标准划分的一组任务(进程)。在cgroups中,资源控制都是以控制群组为基本单位来实现的。一个任务可以被添加到某个控制群组,也从一个控制群组转移到另一个控制群组。一个控制群组中的进程可以使用以控制群组为单位分配的资源,同时也受到以控制群组为单位而设定的资源限制。
  3. 层级体系(hierarchy):简称“层级”,控制群组被组织成有层级关系的一棵控制群组树。控制群组树上的子节点控制群组是父节点控制群组的孩子,继承父节点控制群组的一些特定属性。每个层级需要被添加到一个或多个子系统中,受到子系统的控制。
  4. 子系统(subsystem):一个子系统就是一个资源控制器(resource controller),如blkio子系统就是控制对物理块设备的I/O访问的控制器。子系统必须附加到一个层级上才能起作用,一个子系统附加到某个层级以后,该子系统会控制这个层级上的所有控制群组。

目前cgroups中主要有如下10个子系统可供使用。

  • blkio:这个子系统为块设备(如磁盘、固态硬盘、U盘等)设定读写I/O的访问设置限制。
  • cpu:这个子系统通过使用进程调度器提供了对控制群组中的任务在CPU上执行的控制。
  • cpuacct:这个子系统为控制群组中的任务所实际使用的CPU资源自动生成报告。
  • cpuset:这个子系统为控制群组中的任务分配独立CPU核心(在多核系统)和内存节点。
  • devices:这个子系统可以控制一些设备允许或拒绝来自某个控制群组中的任务的访问。
  • freezer:这个子系统用于挂起或恢复控制群组中的任务。
  • memory:这个子系统为控制群组中任务能使用的内存设置限制,并能自动生成那些任务所使用的内存资源的报告。
  • net_cls:这个子系统使用类别识别符(classid)标记网络数据包,允许Linux流量控制程序(traffic controller)识别来自某个控制群组中任务的数据包。
  • ns:名称空间子系统。用于命名空间的隔离,使不同命令空间中控制群组的任务不能“看到”其他命名空间中的任务的信息。
  • perf_event:这个子系统主要用于对系统中进程运行的性能监控、采样和分析等。

在Redhat系列的系统中,如RHEL 6.3系统的libcgroup这个RPM包提供了lssubsys工具在命令包中查看当前系统支持那些子系统,命令行操作如下:

[root@jay-linux ~]# uname -a
Linux jay-linux 3.5.0 #8 SMP Sun Nov 11 22:51:54 CST 2012 x86_64 x86_64 x86_64 GNU/Linux
[root@jay-linux ~]# lssubsys -am 
cpuset 
cpu cpuacct 
memory 
devices 
freezer 
net_cls 
blkio 
perf_event
net_prio

cgroups中层级体系的概念,与Linux系统中的进程模型有相似之处。在Linux系统中,所有进程也是组成树状的形式(可以用"pstree"命令查看),除init外的所有进程都是一个公共父进程,即init进程的子进程。init进程是由Linux内核在启动时执行的,它会启动其他进程(当然普通进程也可以启动自己的子进程)。除init进程外的其他进程从其父进程那里继承环境变量(如$PATH变量)和其他一些属性(打开的文件描述符)。

与Linux进程模型类似,cgroups的层级体系也是树状分层结构的,子节点控制群组继承父节点控制群组的一些属性。尽管有类似的概念和结构,但它们之间也一些区别。最主要的区别是,在Linux系统中可以同时存在cgroups的一个或多个相互独立的层级,而且此时Linux系统中只有一个进程树模型(因为它们有一个相同的父进程init进程)。多个独立的层级的存在也是有其必然性的,因为每个层级都会给添加到一个或多个子系统下。

下图展示了cgroups模型的一个示例,"cpu"、"memory"、"blkio"、"net_cls"是4个子系统,"Cgroup Hierarchy A"~"Cgroup Hierarchy C"是3个相互独立的层级,含有"cg"两字的就是控制群组(包括cpu_mem_cg、blk_cg、cg1、cg4等),"qemu-kvm"是一个任务(其PID为8201)。

"cpu"和"memory"子系统同时附加到了"Cgroup Hierarchy A"这个层级上,"blkio"、"net_cls"子系统分别附加到了"Cgroup Hierarchy B"和"Cgroup Hierarchy C"这两个层级上。qemu-kvm任务被添加到这三个层级之中,它分别在cg1、cg2、cg3这3个控制群组中。

 

cgroups中的子系统、层级、控制群组、任务之间的关系,至少有如下几条规则需要遵循。

1)每个层级可以有一个或多个子系统附加上去。

"Cgroup Hierarchy A"就有cpu、memory两个子系统,而"Cgroup Hierarchy B"只有blkio一个子系统。

2)只要其中的一个层级已经有一个子系统被附加上了,任何一个子系统都不能被添加到两个或多个层级上。

memory子系统就不能同时添加到层级A和层级B之上。

3)一个任务不能同时是同一个层级中的两个或多个控制群组中的成员。一个任务一旦要成为同一个层级中的第二个控制群组中的成员,它必须先从第一个控制群组中移除。

qemu-kvm任务就不能同时是层级A中的cg1和cg2这两个控制群组的共同成员。

4)派生(fork)出来的一个任务会完全继承它父进程的cgroups群组关系。当然,也可以再次调整派生任务的群组关系使其与它的父进程不同。

如果PID为8201的qemu-kvm进程派生出一个子进程,则该子进程也默认是cg1、cg3、cg4这三个控制群组的成员。

5)在每次初始化一个新的层级之时,该系统中所有的任务(进程或线程)都被默认添加该层级默认的控制群组中成为它的成员。该群组被称为根控制群组(root cgroup),在创建层级时自动创建,之后在该层级中创建的所有其他群组都是根控制群组的后代。

2. cgroups操作示例

在了解了cgroups的基本功能和原理后,将介绍一下如何实际操作cgroups来达到控制KVM客户机的资源使用。Linux内核提供了一个统一的cgroups接口来访问多个子系统(如cpu、memory、blkio等)。

在编译Linux内核时,需要对cgroups相关的项目进行配置,如下是RHEL 6.3内核中与cgroups相关的一些重要配置。

CONFIG CGROUP_SCHED=y 
CONFIG_CGROUPS=y
# CONFIG CGROUP DEBUG is not set 
CONFIG_CGROUP_NS=y 
CONFIG_CGROUP_FREEZER=y 
CONFIG_CGROUP_DEVICE=y 
CONFIG_CPUSETS=y
CONFIG_PROC_PID_CPUSET=y 
CONFIG CGROUP_CPUACCT=y 
CONFIG RESOURCE_COUNTERS=y 
CONFIG CGROUP_MEM_RES_CTLR=y 
CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y 
CONFIG BLK_CGROUP=y
# CONFIG_DEBUG_BLK CGROUP is not set 
CONFIG_CGROUP_PERF=y 
CONFIG_SCHED_AUTOGROUP=y 
CONFIG_MM_OWNER=y
# CONFIG_SYSFS_DEPRECATED_V2 is not set 
CONFIG_RELAY=Y 
CONFIG_NAMESPACES=Y 
CONFIG_UTS_NS=y 
CONFIG_IPC_NS=y 
CONFIG_USER_NS=y 
CONFIG_PID_NS=y 
CONFIG_NET_NS=y
CONFIG_NET_CLS_CGROUP=y 
CONFIG_NETPRIO_CGROUP=y

在实际操作过程中,可以通过如下几个方式来使用cgroups:

  1. 手动地访问cgroups的虚拟文件系统。
  2. 使用libcgroup软件包中的cgcreate、cgexec、cgclassify等工具来创建和管理cgroups控制群组。
  3. 通过一些使用cgroups的规则引擎,通常是系统的一个守护进程来读取cgroups相关的配置文件,然后对任务进程相应的设置。例如,在RHEL 6.3系统中,如果安装了"libcgroup" RPM包,就会有cgconfig这个服务可以使用("service cgconfig start"命令可以启动它),其配置文件默认为/etc/cgconfig.conf文件。
  4. 通过其他一些软件来间接使用cgroups,如使用操作系统虚拟化技术(LXC等)、libvirt工具等。

下面举个KVM虚拟化实际应用中的例子:一个系统中运行着两个客户机(它们的磁盘镜像文件都在宿主机的本地存储上),每个客户机中都运行着MySQL数据库服务,其中一个数据库的优先级很高(需要尽可能快地响应),另一个优先级不高(慢一点也无所谓)。

我们知道数据库服务是磁盘I/O密集型服务,所以这里通过cgroups的blkio子系统来设置两个客户机对磁盘I/O读写的优先级,从而使优先级高的客户机能够占用更多的宿主机中的I/O资源。

采用手动读写cgroups虚拟文件系统的方式,实现这个需求的操作步骤如下:

1)启动这两个客户机和其中的MySQL服务器,让其他应用开始使用MySQL服务。这里不再写出启动过程。

假设需要优先级高的客户机在qemu-kvm命令行启动时加上了"-name high_prio"的参数来指定其名称,而优先级低的客户机有"-name low_prio"参数。在它们启动时为它们取不同的名称,仅仅是为了在后面操作中方便区别出两个客户机。

2)添加blkio子系统到/cgroup/blkio这个控制群组上,并创建高优先级和低优先级两个群组,命令行操作如下:

[root@jay-linux ~]# mkdir -p /cgroup/blkio
[root@jay-linux ~]# mount-tcgroup-oblkioblkio/cgroup/blkio 
[root@jay-linux ~]# mkdir /cgroup/blkio/high_prio 
[root@jay-linux ~]# mkdir /cgroup/blkio/low_prio 
[root@jay-linux ~]# 1s /cgroup/blkio/high_prio/
blkio.reset_stats     blkio.io_merged
blkio.throttle.write_bps_device    cgroup.event_control 
blkio.io_queued      blkio.sectors
blkio.throttle.write_iops_device     cgroup.procs
blkio.io_service_bytes     blkio.throttle.io_service_bytes
blkio.time                 notify_on_release 
blkio.throttle.io_serviced blkio.io_serviced
blkio.weight               tasks
blkio.io_service_time      blkio.throttle.read_bps_device
blkio.throttle.read_iops_device    blkio.weight_device
blkio.io_wait_time         cgroup.clone_children 

其中"mount-t cgroup-o blkio blkio/cgroup/blkio"命令创建了名为blkio的层级,该层级被挂载到"/cgroup/blkio"目录上,并且将blkio子系统附加到该层级上。

如果需要附加多个子系统到一个层级上,在创建时选用多个子系统即可,如执行"mount-t cgroup-o cpu,cpuset,memorycpu_mem/cgroup/cpu_mem"命令就创建了名为cpu_mem的层级,并将cpu、cpuset、memory三个子系统附加到该层级上。

3)分别将高优先级和低优先级的客户机的qemu-kvm进程(及其子进程、子线程)移动到相应的控制群组下面去。可以使用下面的脚本执行,使高优先级客户机(用"high_prio"来标识)进程作为high_prio控制器群组的成员;同样修改一下该脚本也可以处理低优先级客户机。

#!/bin/bash
pid_list=$(ps-eLf | grep qemu | grep "high_prio" | awk '(print $4)')
for pid in $pid_list do
echo Spid >> /cgroup/blkio/high_prio/tasks
done

将控制群组正常分组后,分别查看high_prio和low_prio两个控制群组中的任务,命令行如下,其中7372是高优先级客户机qemu-kvm的进程ID,7374、7375、7377都是ID为7372进程的子线程的ID。低优先级客户机对应的low_prio群组中的信息也与此类似。 

[root@jay-linux ~]# cat /cgroup/blkio/high_prio/tasks 
7372 
7374 
7375 
7377
[root@jay-linux ~]# cat /cgroup/blkio/low_prio/tasks 
7398
7400 
7401 
7403

4)分别设置高低优先级的控制群组中块设备I/O访问的权重,这里假设高低优先级的比例为10:1,设置权重的命令行如下: 

[root@jay-linux ~]# echo 1000 > /cgroup/blkio/high_prio/blkio.weight 
[root@jay-linux ~]# echo 100 > /cgroup/blkio/low_prio/blkio.weight 

顺便提一下,"blkio.weight"是在一个控制群组中设置块设备I/O访问相对比例(即权重)的参数,其取值范围是100~1000的整数。

5)块设备I/O访问控制的效果分析。假设宿主机系统中磁盘I/O访问的最大值是每秒写入66 MB,除客户机的qemu-kvm进程之外的其他所有进程的磁盘I/O访问可以忽略不计,那么在未设置两个客户机的块设备I/O访问权重之前。

在满负载的情况下,高优先级和低优先级的客户机对实际磁盘的写入速度会同时达到约33 MB/s,而在通过10:1的比例设置了它们的权重之后,高优先级客户机对磁盘的写入速度可以达到约60 MB/s,而低优先级的客户机可达到约6 MB/s。

在KVM虚拟化环境中,通过使用cgroups,系统管理员可以对客户机进行细粒度的控制,包括资源分配、优先级调整、拒绝某个资源的访问、监控系统资源利用率。硬件资源可以根据不同的客户机进行“智能”的分组,从整体上提高了资源利用效率,从某种角度来说,也可以提高各个客户机之间的资源隔离性,提升了KVM虚拟机的安全性。

3、 SELinux和sVirt

1. SELinux简介

SELinux(Security-Enhanced Linux)是一个Linux内核的一个安全特性,是通过使用Linux内核中的Linux安全模块(Linux Security Modules,LSM)提供了一种机制来支持访问控制的安全策略,包括美国国防部风格的强制访问控制(Mandatory Access Controls,MAC)。

简单地说,SELinux提供了一种安全访问机制的体系,在该体系中进程只能访问那些其任务中所需要的文件。SELinux中的许多概念是来自美国国家安全局(National Security Agency,NSA)的一些早期项目。SELinux特性是从2003年发布Linux内核2.6版本开始进入到内核主干开发树中的。在一些Linux发型版中,可以通过使用SELinux配置的内核和在用户空间的管理工具来使用SELinux,目前RHEL 6.x、Fedora、CentOS、OpenSuse、SLES、Ubuntu等发行版中都提供对SELinux的支持。

使用SELinux可以减轻恶意攻击、恶意软件等带来的灾难损失,并提供对机密性、完整性有较高要求的信息的安全保障。普通的Linux和传统的UNIX操作系统一样,在资源访问控制上采用自由访问控制(Discretionary Access Controls,DAC)策略,只要符合规定的权限(如文件的所有者和文件属性等),就可以访问资源。在这种传统的安全机制下,一些通过setuid或setgid的程序就可能产生安全隐患,甚至错误的配置可能引发很大的安全漏洞,让系统处于脆弱的容易被攻击的状态。而SELinux是基于强制访问控制(MAC)策略的,应用程序或用户必须同时满足传统的DAC和SELinux的MAC访问控制才能进行资源的访问操作,否则都会被拒绝或返回失败,但这个过程不影响其他的应用程序,保持了系统的安全性。

强制访问控制(MAC)模式为每一个应用程序都提供了一个虚拟“沙箱”,只允许应用程序执行它设计需要的且在安全策略中明确允许的任务,对每个应用程序只分配它正常工作所需的对应特权。例如,Web服务器可能只能够读取网站发布目录中的文件,并监听指定的网络端口。即使攻击者将其攻破,他们也无法执行在安全策略中没有明确允许的任何活动,即使这个进程在超级用户(root)下运行。

传统Linux的权限控制仍然会在系统中存在,并且当文件被访问时,此权限控制将先于SELinux安全策略生效。如果传统的权限控制拒绝本次访问,则该访问直接被拒绝,SELinux在整个过程中没有参与。但是,如果传统Linux权限允许访问,SELinux此时将进一步对其进行访问控制检查,并根据其源进程和目标对象的安全上下文来判断允本次许访问还是拒绝访问。

2. sVirt简介

在非虚拟化环境中,每台服务器是物理硬件上隔离的,一般通过网络来进行相互的通信,如果一台服务器被攻击了,它一般只会让该服务器不能正常工作,当然针对网络的攻击也可能影响到其他服务器。在KVM这样的虚拟化环境中,有多个客户机服务器运行在一个宿主机上,它们使用相同的物理硬件,如果一个客户机被攻击了,攻击者可能会利用被攻陷的服务器发起对宿主机的攻击,如果Hypervisor有bug,则可能会让攻击很容易从一个客户机蔓延到其他客户机上去。

在传统的文件权限管理中,一般是将用户分为所有者(owner)、与所有者相同的用户组(group)和其他用户(others)。在虚拟化应用中,如果使用一个用户账号启动了多个客户机,那么当其中一个客户机qemu-kvm进程有异常行为时,它可能会对其他客户机的镜像文件有读写权限,可能会让其他客户机也暴露在不安全的环境中。

当然可以使一个客户机对应一个用户账号,使用多个账号来分别启动多个不同的客户机,以便使客户机镜像访问权限隔离,而当一个宿主机中有多达数十个客户机时,就需要对应数十个用户账号,管理和使用起来都会非常不方便。

sVirt是Redhat公司开发的针对虚拟化环境的一种安全技术,它主要集成了SELinux和虚拟化。sVirt通过对虚拟化客户机使用强制访问控制来提高安全性,它可以阻止因为Hypervisor的bug而导致的从一台客户机向宿主机或其他客户机的攻击。sVirt让客户机之间相互隔离得比较彻底,而且即使某个客户机被攻陷了,也能够限制它发起进一步攻击的能力。

sVirt还是通过SELinux来起作用的,下图展示了sVirt阻止来自一个客户机的攻击。

sVirt框架允许将客户机及其资源都打上唯一的标签,如qemu-kvm进程有一个标签(tag1),其对应的客户机镜像也使用这个标签(tag1),而其他客户机的标签不能与这个标签(tag1)重复。一旦被打上标签,就可以很方便地应用规则来拒绝不同客户机之间的访问,SELinux可以控制仅允许标签相同的qemu-kvm进程可以访问某个客户机镜像。

3. SELinux和sVirt的配置和操作示例

由于Redhat公司是SELinux的主要开发者之一,更是sVirt最主要的开发者和支持者,因此SELinux和sVirt在Redhat公司支持的系统中使用得最为广泛,RHEL、Fedora的较新的版本都有对sVirt的支持。因为sVirt是以SELinux为基础,并通过SELinux来真正实现访问控制的,以RHEL 6.3系统为例介绍sVirt的配置和使用也完全和SELinux相关。

1)SELinux相关的内核配置

查看RHEL 6.3系统内核配中SELinux相关的内容,命令行如下:

[root@rhel6u3-ga ~]# grep -i selinux /boot/config-2.6.32-279.e16.x86_64 CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_BOOTPARAM=y 
CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1 
CONFIG_SECURITY_SELINUX_DISABLE=y 
CONFIG_SECURITY_SELINUX_DEVELOP=y 
CONFIG SECURITY SELINUX_AVC_STATS=y
CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set

这里的"CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1"表示在内核启动时默认开启SELinux,也可以在Grub引导程序的内核启动行中添加"selinux="参数来修改内核启动时的SELinux状态:"selinux=0"参数表示在内核启动时关闭SELinux,"selinux=1"参数表示在内核启动时开启SELinux。

2)SELinux和sVirt相关的软件包

RHEL 6.3中用rpm命令查询与SELinux、sVirt相关的主要软件包,命令行如下:

[root@rhel6u3-ga ~]# rpm-qa | grep selinux 
libseIinux-deve1-2.0.94-5.3.e16.x86_64
libseIinux-Python-2.0.94-5,3.e16.x86_64 
1ibseIinux-uti18-2.0.94-5.3.e16.x86_64  
1ibseIinux-2.0.94-5.3.e16.x86_64
seIinux-policy-tageted-3.7.19-154.e16.noarch 
se1inux-policy-3.7.19-154.e16.noarch
[root@rhel6u3-ga ~]# rpm -q po1icycoreutils
po1icycoteuti18-2.0.83-19.24.e16.x8664

其中,最重要的就是libselinux、selinux-policy、selinux-policy-targeted这3个RPM包,它们提供了SELinux在用户空间的库文件和安全策略配置文件。而libselinux-utils RPM包则提供了设置和管理SELinux的一些工具,如getenforce、setenforce等命令行工具;policycoreutils RPM包提供了一些查询和设置SELinux策略的工具,如setfiles、fixfiles、sestatus、setsebool等命令行工具。

另外,因为RHEL中SELinux和sVirt的安全策略是根据libvirt来配置的,所以一般也需要安装libvirt软件包,这样才能更好的发挥sVirt对虚拟化安全的保护作用。

3)SELinux和sVirt的配置文件

SELinux和sVirt的配置文件一般都在/etc/selinux目录之中,如下:

[root@rhel6u3-ga ~]# 1s /etc/selinux/
config restorecond.conf restorecond user.conf semanage.conf targeted 
[root@rhel6u3-ga ~]# 1s -1 /etc/selinux/config
-rw-r--r--.1 root root 457 Nov 19 11:14 /etc/selinux/config

其中最重要的配置文件是"/etc/selinux/config"文件,在该配置文件中修改设置之后需要重启系统才能生效。该配置文件的一个示例如下: 

# This file controls the state of SELinux on the system. 
# SELINUX= can take one of these three values:
enforcing- SELinux security policy is enforced. 
# permissive- SELinux prints warnings instead of enforcing. 
# disabled - No SELinux policy is loaded. #
SELINUX=enforcing
# SELINUXTYPE= can take one of these two values:
# targeted - Targeted processes are protected, 
# mls - Multi Level Security protection. 
SELINUXTYPE=targeted

其中"SELINUX="项是用于设置SELinux的状态的,有三个值可选:"enforcing"是生效状态,它会禁止违反规则策略的行为;"permissive"是较为宽松的状态,它会在发现违反SELinux策略的行为时发出警告(而不是阻止该行为),管理员在可以看到警告后可以考虑是否修改该策略来满足目前的安全需求;"disabled"是关闭状态,任何SELinux策略都不会生效。

"SELINUXTYPE="项是用于设置SELinux的策略,有两个值可选:一个是目标进程保护策略(targeted),另一个是多级安全保护策略(mls)。目标进程保护策略仅针对部分已经定义为目标的系统服务和进程执行SELinux策略,会执行"/etc/selinux/targeted"目录中各个具体策略;而多级安全保护策略是严格分层级定义的安全策略。一般来说,选择其默认的"targeted"目标进程保护策略即可。

SELinux的状态和策略的类型决定了SELinux工作的行为和方式,而具体策略决定具体的进程去访问文件时的安全细节。在目标进程工作模式(targeted)下,具体的策略配置在"/etc/selinux/targeted"目录中详细定义,执行如下命令行可查找和sVirt直接相关的配置文件:

[root@rhel6u3-ga targeted]# pwd 
/etc/selinux/targeted
[root@rhel6u3-ga targeted]# grep svirt * -r
contexts/virtual domain context:system_u:system_r:svirt_t:s0 contexts/virtual_image_context:system_u:object_r:svirt_image_t:s0 contexts/customizable_types:svirt_image_t 
Binary file modules/active/policy.kern matches
Binary file policy/policy.24 matches

"virtual_domain_context"文件配置了客户机的标签,而"virtual_image_context"文件配置了客户机镜像的标签,"customizable_types"中增加了"svirt_image_t"这个自定义类型。

4)SELinux相关的命令行工具

在RHEL 6.3中,命令行查询和管理SELinux的工具主要是由"libselinux-utils"和"policycoreutils"这两个RPM包提供的。其中部分命令行工具的使用示例如下:

[root@rhel6u3-ga ~]# setenforce 1 
[root@rhel6u3-ga ~]# getenforce 
Enforcing
[root@rhel6u3-ga ~]# sestatus
SELinux status:             enabled
SELinuxfs mount:            /selinux 
Current mode:               enforcing
Mode from config file:      enforcing
Policy version:             24
Policy from config file:    targeted
[root@rhel6u3-ga~]# getsebool -a | grep httpd 
allow_httpd_anon_write--> off
allow_httpd_mod_auth_ntlm_winbind --> off 
allow_httpd_mod_auth_pam --> off 
httpd_enable_homedirs --> off 
<!--省略其他输出信息-->
[root@rhel6u3-ga ~]# setsebool httpd_enable_homedirs on 
[root@rhel6u3-ga ~]# 1l -Z
drwxr-xr-x. root root unconfined_u:object_r:admin_home_t:s0 html 
[root@rhel6u3-ga ~]# chcon -R -t httpd_sys_content_t /root/html 
[root@rhel6u3-ga ~]# ll -Z
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_type

下面对这几个命令进行简单介绍:

  • setenforce:修改SELinux的运行模式,其语法为setenforce[Enforcing|Permissive|1|0],设置为1或Enforcing就是让其处于生效状态,设置为0或Permissive是让其处于宽松状态(不阻止违反SELinux策略的行为,只是给一个警告提示)。
  • getenforce:查询获得SELinux当前的状态,可能会是生效(Enforcing)、宽松(Permissive)和关闭(Disabled)三个状态。
  • sestatus:获取运行SELinux系统的状态,通过此命令获取的SELinux信息更加详细。
  • getsebool:获取当前SELinux策略中各个配置项的布尔值,每个布尔值有开启(on)和关闭(off)两个状态。
  • setsebool:设置SELinux策略中配置项的布尔值。
  • chcon:改变文件或目录的SELinux安全上下文,"-t[type]"参数是设置目标安全上下文的类型(TYPE),"-R"参数表示递归地更改目录中所有子目录和文件的安全上下文。另外,"-u[user]"参数更改安全上下文中的用户(User),"-r[role]"参数更改安全上下文的角色(Role)。

5)查看SELinux的安全上下文

在SELinux启动后,所有文件和目录都有各自的安全上下文,进程的安全上下文是域(domain)。关于安全上下文,一般遵守如下几个规则。

  1. 系统根据Linux-PAM(可插拔认证模块,Pluggable Authentication Modules)子系统中的pam_selinux.so模块来设定登录者运行程序的安全上下文。
  2. RPM包安装时会根据RPM包内现有的记录来生成安全上下文。
  3. 手动创建的一个文件或者目录,会根据安全策略中的规则来设置其安全上下文。
  4. 如果复制文件或目录(用cp命令),会重新生成安全上下文。
  5. 如果移动文件或目录(用mv命令),其安全上下文保持不变。

安全上下文由用户(User)ID、角色(Role)、类型(Type)三部分组成,这里的用户和角色和普通系统的概念中的用户名和用户分组是没有直接关系的。

用户ID:与Linux系统中的UID类似,提供身份识别的功能,例如:user_u表示普通用户;system_u表示开机过程中系统进程的预设值;root表示超级用户root登录后的预设;unconfined_u为不限制的用户。在默认的目标(targeted)工作模式下,用户ID的设置并不重要。

角色:普通文件和目录的角色通常是object_r;用户可以具有多个角色,但是在同一时间内只能使用一角色;用户的角色,类似于普通系统中的GID(用户组ID),不同的角色具备不同的权限。在默认的目标工作模式下,用户角色的设置也并不重要。

类型:将主体(程序)与客体(程序访问的文件)划分为不同的组,一个组内的各个主体和系统中的客体都定义了一个类型。类型是安全上下文中最重要的值,一般来说,一个主体程序能不能读取到这个文件资源,与类型这一栏的值有关。类型有时会有两个名称:在文件资源(Object)上面称为类型(Type);而在主体程序(Subject)上称为域(Domain)。在域与类型匹配时,一个程序才能正常访问一个文件资源。

一般来说,有三种安全上下文:1)账号的安全上下文;2)进程的安全上下文;3)文件或目录的安全上下文。可以分别用"id-Z"、"ps-Z"、"ls-Z"等命令进行查看,示例如下:

[root@rhel6u3-ga ~]# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 
[root@rhel6u3-ga ~]# ps -eZ | grep httpd
unconfined_u:system_r:httpd_t:s0 10999 ?    00:00:00 httpd
unconfined u:system r:httpd t:s0 11002 ?    00:00:00 httpd
[root@rhel6u3-ga ~]# ls -Z /var/www/
drwxr-xr-x. root root system_u:object_r:httpd_sys_script_exec_t:s0 cgi-bin 
drwxr-xr-x,root root system_u:object_r:httpd_sys_content_t:sO error 
drwxr-xr-x,root root system_u:object_r:httpd_sys_content_t:s0 html

另外,chcon命令行工具可以改变文件或目录的SELinux安全上下文内容。

6)sVirt提供的与虚拟化相关的标签

sVirt使用基于进程的机制和约束为虚拟化客户机提供了一个额外的安全保护层。在一般情况下,只要不违反约束的策略规则,用户是不会感知到sVirt在后台运行的。

sVirt和SELinux将客户机用到的资源打上标签,也对相应的qemu-kvm进程打上标签,然后保证只允许具有与被访问资源相对应的类型(type)和相同分类标签的qemu-kvm进程可以访问某个磁盘镜像文件或其他存储资源。如果有违反策略规则的进程试图非法访问资源,SELinux会直接拒绝它的访问操作,返回一个权限不足的错误提示(通常为"Permission denied")。

sVirt将SELinux与QEMU/KVM虚拟化进行了结合。sVirt一般的工作方式是:在RHEL系统中使用libvirt的守护进程(libvirtd)在后台管理客户机,在启动客户机之前,libvirt动态选择一个带有两个分类标志的随机的MCS标签(如s0:c1,c2),将该客户机使用到的所有存储资源(包括磁盘镜像、光盘等)都打上相应的标签(svirt_image_t:s0:c1,c2),然后用该相应的标签(svirt_t:c0:c1,c2)执行qemu-kvm进程,从而启动了客户机。

在客户机启动之前,查看一些关键的程序和磁盘镜像的安全上下文,示例如下:

# 依次查看libvirtd、virsh、qemu-kvm的安全上下文
[root@rhel6u3-ga ~]#1s-Z/usr/sbin/libvirtd
-rwxr-xr-x, root root system_u:object_r:virtd_exec_t:s0/usr/sbin/libvirtd 
[root@rhel6u3-ga ~]# 1s -Z /usr/bin/virsh
-rwxr-xr-x. root root system_utobject_r:xm_exec_t:s0  /usr/bin/virsh 
[root@rhel6u3-ga ~]# 1s -Z /usr/libexec/qemu-kvm
-rwxr-xr-x.root root system_u:object_r:qemu_exec_t:s0/usr/libexec/qemu-kvm
# 查看默认存放客户机镜像的目录中镜像文件的安全上下文
[rooterhel6u3-ga ~]# 1s -Z /var/lib/libvirt/images/
-rw-r------, qemu qemu system_u:object_r:virt_content_t:sO RHEL6.3.iso 
-xw-------, root root system_u:object_r:virt_image_t:s0 rhel6u3.img

然后分别使用上面看到的rhel6u3.img和rhel6u3-new.img两个磁盘镜像,用libvirt的命令行或者virt-manager图形界面工具启动两个客户机。

在客户机启动后,查看磁盘镜像和qemu-kvm进程的安全上下文,命令行如下: 

[root@rhel6u3-ga ~]# 1s -Z /var/lib/libvirt/images/
-rw-r--r--,qemu qemu system_u:object_r:virt_content_t:s0 RHEL6.3.iso
-rw---------qemu qemu system_u:object_r:svirt_image_t:=0:c259,c710 rhel6u3.img 
-rw---------qemu qemu system_u:object_r:#virt_image_t:s0:c782,c824 rhel6u3-new.img [root@rhel6u3-ga~]# ps-eZ | grep qemu
system_u:system_r:svirt_t:s0:c259,c710 23271 ? 00:00:18 gemu-kvm
system_u:system_r:svirt_t:s0:c782,c824 23364 ? 00:00:06 gemu-kvm

由上面的输出信息可知,两个磁盘镜像已经被动态地标记上了不同的标签,相应的两个qemu-kvm进程也被标记上了对应的标签。

其中,PID为23271的qemu-kvm进程对应的是rhel6u3.img这个磁盘镜像,由于SELinux和sVirt的配合使用,即使23271 qemu-kvm进程对应的客户机被黑客入侵,其qemu-kvm进程(PID 23172)也无法访问非它自己所有的rhel6u3-new.img这个镜像文件,也就不会攻击到其他客户机(当然,通过网络发起的攻击除外)。

7)sVirt根据标签阻止不安全访问的示例

前面已经提及,如果进程与资源的类型和标签不匹配,那么进程对该资源的访问将会被拒绝。真的这么有效吗?下面,通过示例来证明它的有效性:模拟一些不安全的访问,然后查看sVirt和SELinux是否将其阻止。

首先,将bash这个Shell程序复制一份,并将其重命名为svirt-bash,以"svirt_t"类型和"s0:c1,c2"标签执行svirt-bash程序,进入这个Shell环境,用"id-Z"命令查看当前用户的安全上下文。然后创建/tmp/svirt-test.txt这个文件,并写入一些文字,可以看到该文件的安全上下文中类型和标签分别是:svirt_tmp_t和s0:c1,c2。该过程的命令行操作如下(包括部分命令行的注释):

#复制一个Shell,重命名为/bin/svirt-bash
[root@rhe16u3-ga ~]# cp /bin/bash/bin/svirt-bash
# 默认策略规定,使用svirt_t类型的入口程序必须标记为"qemu_exec_t"
#类型,与/usr/libexec/gemu-kym文件的安全上下文类似
[root@rhe16u3-ga ~]# chcon -t qemu_exec_t /bin/svirt-bash
# runcon命令是以某个特定的SELinux安全上下文来执行某个命令
[root@rhel6u3-ga ~]# runcon -t svirt_t -l s0:c1,c2 /bin/svirt-bash 
svirt-bash:/root/.bashrc:Permission denied
#由于类型改变后,无法读取.bashrc配置文件,对本次实验无影响,忽略该错误提示
# 已经进入到使用svirt_t:s0:c1,c2 这样的安全上下文的Shell环境中了
svirt-bash-4.1# id -Z
unconfined_u:unconfined_r:svirt_t:s0:c1,c2
# 可能会遇到“svirt:child setpgid (6962 to 6962): Permission denied”
# 这样的错误提示,She11试图为每个命令都设置进程的组ID(setpgid),对本次实验
# 无影响,在本次实验中忽略setpid来带的错误提示
svirt-bash-4.1# echo "just for testing" >> /tmp/svirt-test,txt 
svirt-bash-4.1# 1s -Z /tmp/svirt-test.txt 
-rw-r--r--. root
root unconfined_u:object_r:svirt_tmp_t:s0:c1,c2/tmp/svirt-test.txt
svirt-bash-4.1# cat /tmp/svirt-test.txt 
just for testing

使用"svirt_t"类型和"s0:c1,c3"(与前面s0:c1,c2不同)标签启动svirt-bash这个Shell程序,进到该Shell环境,用"id-Z"命令查看当前用户的安全上下文,用"ls-Z"查看上一步创建的临时测试文件的安全上下文,试着用"cat"命令去读取该测试文件(svirt-test.txt)。该过程的命令行操作如下: 

[root@rhel6u3-ga ~]# runcon -t svirt_t -1 s0:c1,c3 /bin/svirt-bash 
svirt-bash:/root/.bashrc: Permission denied
svirt-bash-4.1# id -Z
unconfined_u:unconfined_r:svirt_t:s0:cl,c3
svirt-bash-4.1# 1s -Z /tmp/svirt-test.txt 
-rw-r-r-----root
root unconfined_u:object_r:svirt_tmp_t:s0:c1,c2/tmp/svirt-test,txt
# 在s0:c1,c3的标签下,无法读取s0:c1,c2标签的测试文件
svirt-bash-4.1# cat /tmp/svirt-test.txt
cat:/tmp/svirt-test.txt: Permission denied

可知,在标签为"s0:c1,c3"的Shell环境中,不能访问具有"s0:c1,c2"标签的一个临时测试文件,sVirt拒绝本次非法访问,故sVirt基于标签的安全访问控制策略是生效的。

在使用Redhat相关系统(如RHEL、Fedora等)时,如果对KVM虚拟化安全性要求较高,可以选择安装SELinux和sVirt相关的软件包,使用sVirt来加强虚拟化中的安全性。不过,由于SELinux配置还是比较复杂,其默认策略对其他各种服务的资源访问限制还是较多的(为了安全性),也受到一些系统管理员的排斥,一些系统管理员经常会关闭SELinux。总之,一切从实际出发,根据实际项目中对安全性的需求对SELinux和sVirt进行相应的配置。

4、 可信任启动——Tboot

1. TXT和Tboot简介

现在越来越多的公司开始使用虚拟化技术来提高物理资源的利用率和系统可管理性,客户机都运行在Hypervisor之上,所以通常会把Hypervisor作为安全可信的基础,这就要求Hypervisor本身是可信任的。Hhypervisor的可信启动是一个可信Hypervisor的基础,也是整个虚拟化环境的安全可信的基础。由于Hypervisor是和系统物理硬件接触最紧密的软件层,它直接依赖于系统的固件(如BIOS)和硬件(如CPU、内存),所以可以利用一些硬件技术来保证hypervisor的可信启动。

1)TXT简介

TXT是Intel公司开发的Trusted Execution Technology(可信执行技术)的简称,是可信计算(Trusted Computing)在Intel平台上的实现,它是在PC或服务器系统启动时对系统的关键部件进行验证的硬件解决方案。TXT的主要目标在于:证明一个平台和运行于它之上的操作系统的可靠性;确保一个可靠的操作系统是启动在可信任的环境中的,进而成为一个可信的操作系统;提供给可信任操作系统比被证实的系统更多的安全特性。

TXT提供了动态可信根(dynamic root of trust)的机制以增强平台的安全性,为建立可信计算环境和可信链提供必要的支持。动态可信根,依靠一些已知正确的序列来检查系统启动时的配置和行为的一致性。使用这样的基准测试,系统可以很快地评估当前这次启动过程中是否有改变或篡改已设置的启动环境的意图。

恶意软件是对IT基础架构的一个重要威胁,而且也是日益严重的威胁。尽管恶意软件的原理不尽相同,但是它们都试图去污染系统、扰乱正常商业活动、盗取数据、夺取平台的控制权,等等。由于现在的公司都越来越多地使用虚拟的、共享的、多租户的IT基础架构模型,因此传统的网络基础架构就看起来更加的分散,暴露了更多的安全漏洞。同样,传统的安全技术(反病毒和反蠕虫软件)采用的主要方式是对已知的病毒或蠕虫的攻击行为来进行防护和阻止,今天随着各种安全公司的规模扩大和复杂性的增强,传统安全技术也只能是部分的有效,效果并不尽如人意。Intel的TXT技术提供了一种不同的方法——使用已知是正确的数据和流程来检查系统或普通软件的启动。

Intel的TXT技术主要通过三个方面来提供安全和信任:首先将数字指纹软件保存在一个称为“可信用平台模块”(Trusted Platform Module,TMP)的受保护区域内。每次软件启动时,都会检测并确保数字指纹吻合,从而判断系统是否存在风险;其次,它能阻止其他应用程序、操作系统或硬件修改某个应用程序的专用内存区;再次,如果某个应用程序崩溃,TXT将清除它在内存中的数据和芯片缓存区,避免攻击软件嗅探其残余数据。

基于Intel TXT技术建立的可信平台,主要由三个部分组成:安全模式指令扩展(Safe Mode Externtions,SMX)、认证代码模块(Authenticated Code Module,AC模块)、度量过的安全启动环境(Measured Launched Environment,MLE)。安全模式指令扩展,是对现有指令集进行扩展,引入了一些与安全技术密切相关的指令,通过执行这些指令,系统能够进入和退出安全隔离环境。认证代码模块,是由芯片厂商提供的,经过数字签名认证的,与芯片完全绑定的一段认证代码。

当系统接入安全隔离环境时,最先被执行的就是这段认证代码,它被认为是可信的,它的作用是检测后续安全启动环境的可信性。安全启动环境是用于检测内核(或Hypervisor)以及启动内核(或Hypervisor)的软件,它不仅需要对即将启动的内核(或Hypervisor)进行检测,同时还需要保证这种检测过程是受保护的,以及它自身所运行的代码不会被篡改。

2)Tboot简介

Tboot即“可信启动”(Trusted Boot),是使用TXT技术的在内核或Hypervisor启动之前的一个软件模块,用于度量和验证操作系统或Hypervisor的启动过程。Tboot利用TXT技术能够对“动态可信根”在进入安全隔离环境时进行一系列可信检测,与保存在可信平台模块(TPM)的存储空间中的目标检测值进行比较。可信平台模块(通常是一个硬件芯片)利用自身的安全性使这些值不会被恶意代码获取和篡改。Tboot能够利用可信平台模块中的平台配置寄存器(PCR)来保护和控制启动顺序,使非法应用不能跳过这些检测步骤直接进入到可信链的后续环节。

同时,Tboot还能在系统关闭或休眠前清除敏感信息以防泄漏,使可信环境在安全可控制的过程中退出。除此之外,Tboot还利用支持TXT技术的芯片中自带的DMA受保护区域(DMA Protected Range,PTR)和VT-d的受保护内存区域(Protected Memory Region,PMR)来防止恶意代码通过DMA跳过检测过程而直接访问内存中的敏感区域。

关于TXT和Tboot的实现细节,这里并不做详细的介绍,可参考阅读中提到的Intel关于TXT技术规范的手册。在使用Tboot的情况下,一个可信的内核或Hypervisor的启动过程大致如图所示。

Tboot目前也是由Intel的开源软件工程师发起的一个开源的项目,其项目主页是:http://sourceforge.net/projects/tboot/和http://tboot.sourceforge.net/

目前的一些开源操作系统内核和Hypervisor都对Tboot有较好支持,Xen从3.4版本开始支持Tboot,而Linux内核从2.6.35版本开始支持Tboot(在3.0版本中加入了一部分和VT-d相关的修复)。一些主流的Linux发行版(如RHEL、Fedora、Ubuntu等)也支持Tboot,并提供了Tboot的软件安装包。

2. 使用Tboot的示例

在介绍了Intel TXT和Tboot技术之后,将介绍如何配置系统的软硬件来让Tboot真正地工作起来。关于配置和使用Tboot,根据在一台使用Intel Westmere-EP处理器的Xeon(R)X5670服务器的实际操作来介绍如下几个操作步骤。

1)硬件配置及BIOS设置

并非Intel平台的任何一个机器都支持TXT,在Intel中,有vPro标识的桌面级硬件平台一般都支持TXT,另外也有部分服务器平台支持TXT技术。因为Intel TXT技术依赖于Intel VT和VT-d技术,所以在BIOS中不仅需要打开TXT技术的支持,还需要打开Intel VT和VT-d的支持。在BIOS中设置如下项目(不同BIOS在选项命令和设置位置上有些差别)。

打开TXT技术的支持,BIOS选项位于:Advanced→Processor Configuration→Intel(R)TXT或Intel(R)Trusted Excution Technology,需要将其设置为"[Enabled]"状态。

打开Intel VT和VT-d技术的支持,BIOS选项位于:Advanced→Processor Configuration→Intel(R)Virtualization Technology和Intel(R)VT-for Direct I/O,将这两者都设置为"[Enabled]"状态。

可信平台模块(TPM)需要在主板上由一个TPM模块芯片支持,打开TPM支持的BIOS选项位于:Security→TPM Administrative Control。有几个选项,分别是:No Operation、Turn ON、Turn OFF、Clear Ownership,应该选择"Turn ON"(打开)。重启系统后,可以看到BIOS设置中的:Security→TPM State的值为"Enabled&Activated"。当然,如果TPM状态一开始就是"Enabled&Activated",那么说明TPM处于打开状态,不需要重复打开了。

2)编译支持Tboot的Linux内核

要支持Tboot,需要在宿主机的Linux内核中配置TXT和TCG[15](Trusted Computing Group)相关的配置。由于Tboot还需要Intel VT和VT-d技术的支持,在内核中也要有支持VT(配置关键字为VT)和VT-d(配置文件中关键字为IOMMU)的配置,查看这些配置命令行如下:

[root@jay-linux kvm_demo]# grep TXT /boot/config-3.5.0 
CONFIG_HAVE_INTEL_TXT=y 
CONFIG_INTEL_TXT=y
[root@jay-linux kvm_demo]# grep TCG /boot/config-3.5.0 
CONFIG_TCG_TPM=m 
CONFIG_TCG_TIS=m 
CONFIG_TCG_NSC=m 
CONFIG_TCG_ATMEL=m 
CONFIG_TCG_INFINEON=m
[root@jay-linux kvm_demo]# grep -i IOMMU /boot/config-3.5.0 
CONFIG_GART_IOMMU=y 
CONFIG_CALGARY_IOMMU=y
# CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT is not set 
CONFIG_IOMMU_HELPER=y 
CONFIG_IOMMU_API=y 
CONFIG_IOMMU_SUPPORT=y 
# CONFIG_AMD_IOMMU is not set CONFIG_INTEL_IOMMU=y
CONFIG_INTEL_IOMMU_DEFAULT_ON=y
[root@jay-linux ~]# grep -i virtualization /boot/config-3.5.0 
CONFIG_VIRTUALIZATION=y
[root@jay-linux ~]# grep -i config_vt /boot/config-3.5.0 
CONFIG_VT=y 
CONFIG_VT_CONSOLE=y
CONFIG_VT_CONSOL_SLEEP=y
CONFIG_VT_HW_CONSOLE_BINDING=y

如果缺少这些配置,则需要添加上这些配置,然后重新编译内核。

3)编译和安装Tboot

在一些主流的Linux发行版中,Tboot已经作为一个单独的软件包发布了,这些发行版包括:Fedora 14、RHEL 6.1、SELS 11 SP2、Ubuntu 12.04等,以及比它们更新的版本。在已经有Tboot支持的系统上,只需要安装tboot这个软件包即可,如在RHEL 6.3中可以用"yum install tboot"来安装Tboot,一般也会同时安装trousers软件包(可提供tcsd这个用于管理可信计算资源的守护程序)。

如果没有现成的软件包可用,可到sourceforge上tboot的项目页面下载其源代码的tar.gz包进行编译。Tboot项目主页是:http://sourceforge.net/projects/tboot/和http://tboot.sourceforge.net/,同时也可以在线查看该项目的修改记录和代码,见http://tboot.hg.sourceforge.net/hgweb/tboot/tboot。

tboot的编译和运行会依赖openssl-devel、zlib-devel、trousers-devel、trousers等软件包,在编译tboot之前需要将它们安装好(如果有的依赖软件包不存在,编译时会有报错提示,安装上相应的软件包即可)。获取最新的tboot开发源代码仓库(目前是使用Mercurial[16]工具管理的),并进行编译,然后查看编译后生成的所需要的二进制文件,命令行操作如下:

[root@jay-linux kvm_demo]# hg clone
http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot thoot 
requesting all changes 
adding changesets 
adding manifests 
adding file changes
added 318 changesets with 1194 changes to 197 files 
updating to branch default
169 files updated,O files merged,O files removed,O files unresolved
[root@jay-linux kvm_demo]# cd tboot 
[root@jay-linux tboot]# make 
<!-- 此处省略编译过程的输出信息-->
[root@jay-linux tboot]# make install 
<!-- 此处省略安装过程的输出信息 -->
[root@jay-linux tboot]# ls -1 /boot/tboot.gz
-rw-r------1 root root 70834 Nov 24 23:39 /boot/tboot.gz 
[root@jay-linux tboot]# ls -1 /usr/sbin/txt-stat
-rwxr-xr-x. 1 root root 18424 Nov 24 23:40 /usr/sbin/txt-stat

由上面的输出信息可知,通过源代码编译和安装Tboot及其用户空间管理工具的过程是比较简单的,默认会将Tboot的内核文件tboot.gz安装到/boot/目录下,将Tboot相关的用户空间管理工具安装到/usr/sbin/目录下。

4)修改GRUB,让tboot.gz先于Linux内核启动

在修改GRUB之前,还需要获取当前系统的SINIT AC模块(它是芯片厂商提供的经过数字签名的一段认证代码),并将其配置到GRUB中使其对系统后续启动过程进行可信的认证。可以从硬件供应商那里去索取SINIT AC模块,也可以到Intel公司官方网站中关于TXT技术的网页[17]去查看哪些平台支持TXT和下载对应的SINIT二进制代码模块。这里已经将SINIT模块下载放到/boot/WSM_SINIT_100407_rel.bin位置。

在使用Tboot之时,在GRUB等引导程序中,需要将tboot.gz文件设置为内核(kernel)以便最先启动,而原本的Linux内核(或Xen等Hypervisor)、初始化内存镜像(initramfs、initrd)和SINIT模块都设置为模块启动。一个配置有Tboot的GRUB入口条目示例如下:

title KVM host with Tboot
    root (hd0,0)
    kernel /boot/tboot.gz logging=vga,serial,memory 
    module /boot/vmlinuz-3.5.0 ro
root=UUID=la65b4bb-cd9b-4bbf-97ff-7e1f7698d3db intel_iommu=on
    module /boot/initramfs-3.5.0.img 
    module /boot/wSM_SINIT_100407_rel.bin

其中,tboot.gz行的"logging=vga,serial,memory"配置表示将Tboot内核启动的信息同时记录在显示器、串口和内存中;WSM_SINIT_100407_rel.bin就是用于支持使用的Westmere-EP平台的SINIT AC模块。

5)获取可信平台模块(TPM)的所有权

通过上一步修改好的Tboot启动的GRUB入口条目重新启动系统,在系统中获取可信平台模块(TPM)的所有权,步骤如下:

(1)加载tpm_tis内核模块。

tpm_tis模块是可信平台模块(TPM)硬件的驱动程序,加载和检查tpm_tis的命令行操作如下:

[root@jay-linux ~]# modprobe tpm_tis 
[root@jay-linux ~]# lsmod | grep tpm
tpm_tis     9580     0
tpm         17031    1     tpm_tis 
tpm_bios    10339    1     tpm

由上面的输出信息可知,tpm_tis模块依赖于tpm模块,而tpm模块又依赖于tpm_bios模块。如果加载tpm_tis时不成功,可以添加一些参数,通过用如下命令来加载tpm_tis模块: 

[root@jay-linux ~]# modprobe tpm_tis force=1 interrupts=0

(2)启动tcsd守护进程。

tcsd是trousers软件包中的一个命令行工具,是一个管理可信平台模块(TPM)资源的守护程序,它也负责处理来自本地和远程的可信服务提供者(TSP)的请求。tcsd应该是唯一的到达TPM设备驱动程序的一个用户空间守护进程。启动和查询tcsd守护程序的命令行操作如下:

[root@jay-linux ~]# tcsd
[root@jay-linux ~]# ps -ef | grep tcsd | grep -v grep
tss      8440     1    0  14:24  ?     00:00:00 tcsd

(3)设置系统的可信平台模块(TPM)的所有者。

tpm_takeownership也是trousers软件包中的一个命令行工具,它通过TPM_TakeOwnership应用程序接口在当前系统的可信平台模块(TPM)上建立一个所有者(owner)。该命令在执行时会要求设置所有者的密码和存储根密钥(Storage Root Key,SRK)的密码和确认。

[root@jay-linux ~]#tpm_takeownership 
Enter owner password:
Confirm password:
Enter SRK password:
Confirm password:

另外,可以添加-y参数将所有者的密码设置为20个“0”而不需要交互式的输出,也可以用-z参数将存储根密钥(SRK)的密码设置为20个“0”,示例如下: 

[root@jay-linux ~]# tpm_takeownership -y -z 

6)启动系统,检查Tboot的启动情况

从前面修改的Grub的Tboot入口条目启动系统,在显示器或串口信息上就可以看到Tboot启动时的信息了。

在系统启动之后,通过dmesg命令可以找到Tboot正在使用的一些信息,如下:

[root@jay-linux ~]# dmesg | grep -i tboot 
tboot: found shared page at phys addr 0x826000:
tboot: Forcing Intel-IOMMU to enabled

7)使用txt-stat工具检查本次启动的可信状态

对于Tboot是否正常工作,本次操作系统启动是否可信,可以使用Tboot源码中自带的txt-stat命令行工具来查看TXT当前的工作状态。如果在txt-stat中看到如下的输出信息,则表明本次是一次安全可信的启动。

TXT measured launch: TRUE
secrets flag set: TRUE 

使用txt-stat工具查看TXT工作状态:

[root@jay-linux ~]# txt-stat
<!-- 省略txt-stat 命令的其余部分输出信息-->
[root@jay-linux~]# txt-stat | grep -i "secrets flag"
    secrets flag set:TRUE
TBOOT: set TXT.CMD.SECRETS flag
[root@jay-linux ~]#txt-stat | grep -i "measured launch"
    TXT measured launch: TRUE
TBOOT: measured launch succeeded

如果看到在Tboot的保护下正常启动操作系统,也查询到当前系统的TXT是正常工作的,那么就可以证明本次启动是一次可信任的启动。

5、其他安全策略

1. 镜像文件加密

随着网络与计算机技术的发展,数据的一致性和数据的完整性在信息安全中变得越来越重要,对数据进行加密处理对数据的一致性和完整性都有较好的保障。有一种类型的攻击叫做“离线攻击”,如果攻击者在系统关机状态下可以物理接触到磁盘或其他存储介质,就属于“离线攻击”的一种表现形式。

另外,在一个公司内部,不同职位的人有不同的职责和权限,系统处于启动状态时的使用者是工作人员A,而系统关机后,会存放在另外的位置(或不同部门),而工作人员B可以获得该系统的物理硬件。如果没有保护措施,那么B就可以轻易地越权获得系统中的内容。如果有了良好的加密保护,就可以防止这样的攻击或内部数据泄露事件的发生。

在KVM虚拟化环境中,存放客户机镜像的存储设备(如磁盘、U盘等)可以对整个设备进行加密,如果其分区是LVM,也可以对某个分区进行加密。而对于客户机镜像文件本身,也可以进行加密的处理,已经介绍过qcow2镜像文件格式支持加密,这里再简单介绍一下。

"qemu-img convert"命令在"-o encryption"参数的支持下,可以将未加密或已经密的镜像文件转化为加密的qcow2的文件格式。先创建一个8 GB大小的qcow2格式镜像文件,然后用"qemu-img convert"命令将其加密,命令行操作如下:

[root@jay-linux kvm_demo]# qemu-img create-f qcow2 -o size=8G guest.qcow2 Formatting guest.qcow2', fmt=qcow2 size=8589934592 encryption=off cluster_size=65536
[root@jay-linux kvm_demo]# qemu-img convert -o encryption -O qcow2 guest.qcow2 encrypted.qcow2
Disk image encrypted.qcow2' is encrypted.
password:    ******    #此处输入你需要设置的密码,然后按回车键确认

生成的encryped.qcow2文件就是已经加密的文件,查看其信息如下所示,可以看到"encrypted:yes"的标志。 

[root@jay-linux kvm_demo]# qemu-img info encrypted.qcow2 
Disk image 'encrypted.qcow2' is encrypted. 
password:
image: encrypted.qcow2 
file format: qcow2
virtual size:8.0G(8589934592 bytes)
disk size:8.0G 
encrypted: yes 
cluster_size: 65536

在使用加密的qcow2格式的镜像文件启动客户机时,客户机会先不启动而暂停,需要在QEMU monitor中输入"cont"或"c"命令以便继续执行,然后会出现输入已加密qcow2镜像文件的密码,只有密码正确才可以正常启动客户机。

在QEMU monitor中的命令行示例如下: 

(qemu) c
ide0-hd0 (encryped.qcow2) is encrypted.

Password: ******  #此处输入先前设置过的密码

(qemu)

当然,在执行"qemu-img create"创建镜像文件时就可以将其创建为加密的qcow2文件格式,但是不能交互式地指定密码,命令行如下: 

[root@jay-linux kvm_demo]# qemu-img create -f qcow2 -o backing_file=rhel6u3.img,encryption encrypted.qcow2

这样创建的qcow2文件处于加密状态,但是其密码为空,在使用过程中提示输入密码时,直接按回车键即可。对于在创建时已设置为加密状态的qcow2文件,仍然需要用上面介绍过的"qemu-img convert"命令来转换一次,这样才能设置为自己所需的非空密码。

当使用qcow2镜像文件的加密功能时,可能会觉得每次都要输入密码,可能会给大规模地自动化部署KVM客户机带来一定的障碍。其实,输入密码验证的问题也是很容易解决的,例如Linux上的expect工具就很容易解决需要输入密码这样的交互式会话。

在使用expect进行ssh远程登录过程中输入密码交互的一段Bash脚本如下:

SSH_Time_Out() {
local host=192.168.111.111 
local time out=250 
local command="ifconfig"
local exp_cmd=$(cat<< EOF
spawn ssh root@$host $command
set timeout $time_out 
expect {
    "*assword:"  { send "123456\r"; exp_continue}
    "*(yes/no)?" { send "yes\r"; exp_continue }
    eof  { exit 0 }
} 
EOF)
??? expect -c "$exp_cmd"
??? return $?
}

上面的脚本示例实现了用ssh远程登录到一台主机上,并执行了一个ifconfig命令。其中远程主机的root用户的密码的输入是通过expect的脚本来实现自动输入的,expect的超时时间在这里被设置为250秒。

2. 虚拟网络的安全

在KVM宿主机中,为了网络安全的目的,可以使用Linux防火墙——iptables工具。使用iptables工具(为IPv4协议)或ip6tables(为IPv6协议)可以创建、维护和检查Linux内核中IP数据报的过滤规则。

而对于客户机的网络,QEMU/KVM提供了多种网络配置方式。例如:使用NAT方式让客户机获取网络,就可以对外界隐藏客户机内部网络的细节,对客户机网络的安全起到了保护作用。不过,在默认情况下,NAT方式的网络让客户机可以访问外部网络,而外部网络不能直接访问客户机。如果客户机中的服务需要被外部网络直接访问,就需要在宿主机中配置好iptables的端口映射规则,通过宿主机的某个端口映射到客户机的一个对应端口。

如果物理网卡设备比较充足,而且CPU、芯片组、主板等都支持设备的直接分配技术(如Intel VT-d技术),那么选择使用设备直接分配技术(VT-d)为每个客户机分配一个物理网卡也是一个非常不错的选择。因为在使用设备直接分配使用网卡时,网卡工作效率非常高,而且各个客户机中的网卡是物理上完全隔离的,提高了客户间的隔离性和安全性,即使一个客户机中网络流量很大(导致了阻塞)也不会影响到其他客户机中网络的质量。

3. 远程管理的安全

在KVM虚拟化环境中,可以通过VNC的方式远程访问客户机,那么为了虚拟化管理的安全性,可以为VNC连接设置密码,并且可以设置VNC连接的TLS、X.509等安全认证方式。

如果使用libvirt的应用程序接口来管理虚拟机,包括使用virsh、virt-manager、virt-viewer等工具,为了远程管理的安全性考虑,最好只允许管理工具使用SSH连接或者带有TLS加密验证的TCP套接字来连接到宿主机的libvirt。关于libvirt API的简介及其配置、连接的方式,可以阅读"6.1 libvirt"中的内容。

4. 普通Linux系统的安全准则

KVM宿主机及运行在KVM上Linux客户机都是普通的Linux操作系统。普通Linux系统的一些安全策略和准则都可以用于KVM环境的安全性的提高。

美国国家安全局(National Security Agency,NSA)的一份公开文档[18]中谈及了Redhat Linux系统的安全设置应遵循的几个通用原则,对于任何Linux系统(甚至Windows系统)的安全配置都有一定的借鉴意义。

(1)对传送的数据尽可能进行加密处理

无论是在有线网络还是无线网络上传输的数据都很容易被监听,所以只要对这些数据的加密方案存在,都应该使用加密方案。即使是预期仅在局域网中传输的数据,也应该做加密处理。对于一些身份认证相关的数据(如密码)等的加密有其非常重要。RHEL 5.x或6.x版本的Linux系统组成的网络中,可以配置为它们之间传输的全部都经过加密处理。

(2)安装尽可能少的软件,从而使软件漏洞数量尽可能少

避免一个软件漏洞最简单的方法就是避免安装那个软件。在RHEL系统中,有RPM软件包管理工具可以比较方便地管理系统中已经安装的软件包。随意安装的软件可能会以多种方式暴露出系统的漏洞:1)包含有setuid程序的软件包可能为本地的攻击者提供一种特权扩大的潜在途径。2)包含有网络服务的软件包可能为基于网络的攻击者提供攻击的机会。3)包含有被本地用户有预期地执行的程序(如图形界面登录后必须执行的程序)的软件可能会给特洛伊木马者其他隐藏执行的攻击代码提供机会。通常,一个系统上安装的软件包数量可以被大量删减,直到只包含环境或者操作所真正必需的软件。

(3)不同的网络服务尽量运行在不同的系统上

一个服务器(当然也可以是虚拟客户机)应该尽可能地专注只提供一个网络服务。如果这样做,即使一个攻击者成功地渗透了一个网络服务上的软件漏洞,也不会危害到其他服务的正常运行。

(4)配置一些安全工具去提高系统的鲁棒性

现存的几个工具可以有效地提高系统的抵抗能力和检测未知攻击的能力。这些工具可以利用较低的配置成本去提高系统面对攻击时的鲁棒性。有一些实际的工具就具有这样的能力,如基于内核的防火墙——iptables,基于内核的安全保护软件——SELinux,以及其他一些日志记录和进程审计的软件基础设施。

(5)使用尽可能少的权限

只授予用户账号和运行的软件最少的必需的权限。例如,仅给真正需要用sudo管理员权限的用户开放sudo的功能(在Ubuntu中默认开放过多的sudo权限可能并不太安全)。限制系统的登录,仅向需要管理系统的用户开放登录的权限。另外,还可以使用SELinux来限制一个软件对系统上其他资源的访问权限,合适的SELinux策略可以让某个进程仅有权限做应该做的事情。

6、 KVM维护迁移

如何才能从其他虚拟化方案迁移到KVM虚拟化中呢?或者如何能将物理原生系统直接迁移到KVM虚拟化环境中去呢?

1. virt-v2v工具介绍

V2V是“从一种虚拟化迁移到另一种虚拟化”(Virtual to Virtual)过程的简称。不借助于其他的工具,使用纯QEMU/KVM能实现KVM到KVM的迁移,而不能实现从Xen等其他Hypervisor迁移到KVM上去。virt-v2v工具可用于将虚拟客户机从一些Hypervisor(也包括KVM自身)迁移到KVM环境中。它要求目的宿主机中的KVM是由libvirt管理的或者是由RHEV(Red Hat Enterprise Virtualization)管理的。

virt-v2v是由Redhat的工程师Matthew Booth开发的命令行工具,它也是一个完全开源的项目,除了Matthew自己,也有一些其他开发者为该项目贡献过代码。可以用下面的命令克隆最新的virt-v2v开发源代码:

git clone git://git.fedorahosted.org/virt-v2v.git

virt-v2v默认会尽可能由转换过来的虚拟客户机使用半虚拟化的驱动(virtio)。根据Redhat官方对virt-v2v工具的的描述[20],RHEL 6.x系统中的virt-v2v工具支持从KVM、Xen、VMware ESX等迁移到KVM上去(最新的virt-v2v还支持VirtualBox的转化)。当然从KVM到KVM的迁移,使用前面动态迁移中的方法也是可以完成的。

与动态迁移中介绍的不同,virt-v2v工具的迁移不是动态迁移,在执行迁移操作之前,必须要在源宿主机(Xen、VMware等)上关闭待迁移的客户机,所以,实际上,可以说virt-v2v工具实现的是一种转化,将Xen、VMware等Hypervisor的客户机转化为KVM客户机。一般来说,virt-v2v要依赖于libvirt,让libvirt为不同的Hypervisor提供一个公共的适配层,为向KVM转化提供了必要功能。它要求源宿主机中运行着libvirtd(当然VMware除外),迁移到的目标宿主机上也要运行着libvirtd。

根据Redhat官方文档的介绍,virt-v2v支持多种Linux和Windows客户机的迁移和转换,包括:RHEL 4、RHEL 5、RHEL 6、Windows XP、Windows Vista、Windows 7、Windows Server 2003、Windows Server 2008等。当然,尽管没有官方的声明和支持,通过实际测试还是可以发现,使用virt-v2v也能够使其他一些类型客户机迁移到KVM(如Fedora、Ubuntu等)。

virt-v2v的可执行程序已经在一些Linux发行版中发布了。Fedora 11和之后的Fedora都已经包含了virt-v2v工具,RHEL 5.x(x≥6)和RHEL 6.x也已经发布了virt-v2v软件包,可以使用"yum install virt-v2v"来安装。

与V2V的概念相对应,还有一个P2V的概念,P2V是“物理机迁移到虚拟化环境”(Physical to Virtual)的缩写。virt-p2v的代码也在virt-v2v的代码库中,在使用时,还需要一个用于P2V迁移的可启动的ISO(Redhat的注册用户可以通过RHN通道下载)。从Fedora 14和RHEL 6.3开始的Fedora和RHEL版本开始提供对virt-p2v的支持。

2. 从Xen迁移到KVM

在Linux开源虚拟化领域中,Xen和QEMU/KVM是两个比较广泛使用的Hypervisor,Xen的部分用户可能会有将Hypervisor从Xen迁移到KVM的需求。virt-v2v工具支持在libvirt管理下在Xen客户机迁移到KVM虚拟化环境中,既支持本地转化,也支持远程迁移。

将Xen上的一个客户机迁移到KVM的过程,需要将客户机的配置转化为对应的KVM客户机配置,也需要将客户机镜像文件传输到目的宿主机上。磁盘镜像的传输需要SSH会话的支持,所以需要保证Xen宿主机支持SSH访问,另外,在每个磁盘进行传输时都会提示输入SSH用户名和密码,可以考虑使用SSH密钥认证来避免多次的SSH用户名和密码交互。

使用virt-v2v工具将Xen客户机迁移到KVM中的命令示例如下:

virt-v2v-ic xen+ssh://[email protected] -os pool-b brname vm-name

-ic URI表示连接远程Xen宿主机中libvirt的URI。-os pool表示迁移过来后,用于存放镜像文件的本地存储池。-b brname(即:--bridge brname)表示本地网桥的名称,用于建立与客户机的网络连接。如果本地使用虚拟网络,则使用-n network(或--network network)参数来指定虚拟网络。vm-name表示的是在Xen的源宿主机中将要被迁移的客户机的名称。

Xen中的全虚拟化类型客户机(HVM)和半虚拟化客户机(PV guest,XenU),都可以使用上面介绍的命令迁移到KVM。由于KVM是仅支持全虚拟化客户机(HVM)的,所以,支持对Xen半虚拟化客户机的迁移,virt-v2v还会涉及客户机内核修改、转化的过程。

下面通过一个示例来介绍一下使用virt-v2v工具将Xen上的HVM客户机迁移到KVM上的过程。其中,源宿主机(Xen的Dom0)使用的是经过升级的Fedora 17系统,Xen的版本是4.1.3,Dom0内核版本是3.6.1,libvirt版本是0.9.11;KVM宿主机使用的是RHEL 6.3原生系统的内核、qemu-kvm和libvirt(0.9.10版本),并且用"yum install virt-v2v"安装了0.8.7版本的virt-v2v工具。

1)在源宿主机系统(IP地址为192.168.127.163)中,启动libvirtd服务,并且定义好一个名为xen-hvm1的客户机用于本次迁移,该客户机在Xen环境中是可以正常工作的,但需要在迁移之前将该客户及关闭。通过virsh工具查看Xen中libvirt管理的所有客户机的状态,命令行如下:

[root@F17-Xen ~]# virsh list --all 
Id        Name            State
0        Domain-0        running
-        xen-hvml        shut off

由上面信息可知,xen-hvm1客户机处于关机状态。

2)在KVM宿主机(目的宿主机)中,启动libvirtd服务,然后使用virt-v2v命令行工具进行Xen到KVM的迁移,命令行操作如下:

[root@rhel6u3-ga ~]# service libvirtd start
[root@rhel6u3-ga ~]# virt-v2v -ic
xen+ssh://[email protected] default--bridge br0 xen-hym1 
[email protected]'s password:
[email protected]'s password:

由以上信息可知,经过用户命名/密码的验证之后,virt-v2v将客户机的镜像文件传输到KVM宿主机中,磁盘镜像的传输过程可能需要一定的时间,所需的时间长度与磁盘镜像的大小及当前网络带宽有很大的关系。尽管最后有一些警告提示信息,该迁移过程还是正常完成了。

已经提及过,virt-v2v的转换需要先关闭客户机,如果Xen宿主机(Dom0)上的该客户机处于运行中状态,运行上面的virt-v2v命令会得到如下的错误提示:

virt-v2v: Guest XX is currently blocked. It must be shut down

3)在KVM宿主机中,查看迁移过来的客户机镜像和启动该客户机,命令行操作如下: 

[root@rhel6u3-ga ~]# 1s /var/lib/libvirt/images/ia32e rhel6u3.img /var/lib/libvirt/images/ia32e_rhel6u3.img
[root@rhel6u3-ga ~]# virsh list --all
Id        Name         State
-        rhel6u3-1    shut off
-        rhel6u3-2    shut off
-        xen-hvml     shut off

[root@rhel6u3-ga ~]# virsh create /etc/libvirt/gemu/xen-hvml.xml 
Domain xen-hvml created from /etc/libvirt/qemu/xen-hvml.xml
[root@rhel6u3-ga ~]# virsh list
Id      Name           State
2     xen-hvml        running

由以上信息可知,从Xen上迁移过来的客户机,其镜像文件默认在/var/lib/libvirt/images/目录下,其XML配置文件默认在/etc/libvirt/qemu/目录下。从第一个"virsh list--all"命令可知,迁移过来的客户机默认处于关闭状态。在使用"virsh create"命令启动该客户后,该客户机就处于运行中(Running)状态了。

一般来说,从Xen到KVM迁移成功后,迁移过来的客户机就可以完全正常使用了。不过,由于一些命令和配置的不同,也可能会导致迁移后的客户机网络不通的情况,这就需要自行修改该客户机的XML配置文件;也可能出现磁盘不能识别或启动的问题(Windows客户机迁移时易出现该问题),这一方面需要检查和修改XML配置文件(可以直接改为模拟IDE磁盘设备而不是virtio),另一方面可以在该客户机中安装virtio-blk相关的磁盘驱动。

由于Xen中也是使用QEMU来作为设备模型,因此Xen中的客户机一般使用raw、qcow2等格式的客户机镜像文件,这与QEMU/KVM中的磁盘镜像格式是完全一致(或兼容)的,不需要进行任何的格式化转换(除非它们的QEMU版本差异太大)。

除了使用virt-v2v工具来实现Xen到KVM的迁移,也可以直接将Xen中客户机的磁盘镜像远程复制到KVM宿主机的存储池中,然后根据Xen上面客户机的需求,手动地使用相应的qemu-kvm命令行参数来启动该客户机即可,或者,将libvirt管理的Xen中该客户机的XML文件复制到KVM宿主机中,对该XML配置文件进行相应的修改后,通过libvirt启动该客户机即可。

3. 从VMware迁移到KVM

VMware作为系统虚拟化领域的开拓者和市场领导者之一,其虚拟化产品功能比较强大、易用性也比较良好,所以被很多人了解并在生产环境中使用。不过,美中不足的是,其企业级虚拟化产品的许可证授权费用还是比较昂贵的,特别是有大批量的服务器需要部署WMware ESX/ESXi虚拟化产品时,许可证授权费用就真的不是一笔小数目了,不管是从KVM可以完全免费的角度,还是从KVM基于Linux内核且完全开源的角度来看。

从VMware迁移到KVM的方法,与从Xen迁移到KVM的完全类似。可以通过virt-v2v工具来做迁移,实现将VMware ESX中的一个客户机迁移到KVM上。利用virt-v2v迁移VMware客户机的命令行示例如下:

virt-v2v -ic esx://esx.demo.com/ -os pool --bridge brname vm-name

上面命令行中的命令和参数基本类似,只是这里使用了esx://esx.demo.com来表示连接到VMware ESX服务器,将命令vm-name的客户机迁移过来。在连接到VMware的ESX服务器时,一般需要认证和授权。virt-v2v工具支持连接ESX时使用密码认证,它默认读取$HOME/.netrc文件中的机器名、用户名、密码等信息,这与FTP客户端命令"ftp"类似。这个.netrc文件中的格式如下:

machine esx.demo.com login root password 123456

除了通过virt-v2v工具可以将VMware中运行着的客户机迁移到KVM,也可以采用直接复制VMware客户机镜像到KVM中的方法:先关闭VMware客户机,然后直接将VMware的客户机镜像(一般是.vmdk为后缀的文件)复制到KVM的客户机镜像存储系统系统上(可能是宿主机本地也可能是网络共享存储),接着通过qemu-kvm命令行工具启动该镜像文件即可。

qemu-kvm从0.12版本开始就支持直接使用VMware的vmdk镜像文件启动客户机,一个简单的命令行示例如下: 

[root@jay-linux ~]# qemu-system-x86_64 -m 1024 win7.wmdk

如果qemu-kvm版本较旧,不支持直接使用vmkd镜像文件格式,那么可以将VMware的镜像格式转化为raw或qcow2等在QEMU/KVM中最常用的格式。convert命令可以让不同格式的镜像文件进行相互转换。将vmdk格式的镜像文件转化为qcow2格式,然后用qemu-kvm命令行启动,命令行操作过程如下:

[root@jay-linux ~]# qemu-img convert win7.vmdk -O gcow2 win7.qcow2
[root@jay-linux ~]# qemu-system-x86_64 -m 1024 win7.qcow2

4. 从VirtualBox迁移到KVM

virt-v2v工具从0.8.8版本开始也支持将VirtualBox客户机迁移到KVM上,其方法与从Xen迁移到KVM的方法完全类似,其转化命令示例如下:

virt-v2v -ic vbox+ssh://[email protected] -os pool -b brname vm-name

在该命令中,仅仅在连接到VirtualBox时使用的URI是vbox+ssh这样的连接方式,而不是用xen+ssh的方式。

除了使用virt-v2v工具来转换,也可以直接将VirtualBox中的客户机镜像文件(一般是以.vdi为后缀的文件)复制到KVM宿主机中使用。较新的qemu-kvm(如1.2.0版本)都支持直接用.vdi格式的镜像文件作为客户机镜像直接启动,命令行示例如下:

[root@jay-linux ~]# qemu-system-x86_64 -m 1024  ubuntu.vdi

也可以将VirtualBox客户机镜像文件转化为QEMU/KVM中最常用的qcow2或raw格式的镜像文件,然后在qemu-kvm命令行启动转化后的镜像文件,命令行操作如下:

[root@jay-linux ~]# gemu-img convert ubuntu.vdi -O gcow2 ubuntu.gcow2
[root@jay-linux ~]# qemu-system-x86_64 -m 1024 ubuntu.gcow2

5. 从物理机迁移到KVM虚拟化环境(P2V)

virt-p2v由两部分组成:包含在virt-v2v软件包中的服务器端,可启动的ISO作为virt-p2v的客户端。使用virt-p2v工具的方法将物理机迁移到KVM虚拟化环境中,需要经过如下几个步骤:

1)在服务器端(一个KVM宿主机)安装virt-v2v、libvirt等软件,打开该宿主机的root用户SSH登录权限。

2)在服务器端,修改/etc/virt-v2v.conf配置文件,让其有类似如下的示例配置:

<virt-v2v>
    <!-- Target profiles -->
    <profile name="libvirt">
        <method>libvirt</method>
        <storage>default</storage>
        <network type="default">
            <network type="network" name="default"/>
        </network>
    </profile>
</virt-v2v>

3)制作virt-p2v可以启动的设备。如果是Redhat的客户,可以到RHN中去下载virt-p2v的ISO镜像文件(如rhel-6.3-p2v.iso),然后将其烧录到一个光盘或U盘,使其作为物理机的启动设备。当然,可以下载virt-v2v的源代码,然后编译、制作ISO镜像文件。

4)在待迁移的物理机上,选择前一步中制作的virt-p2v启动介质(光盘或U盘)来启动系统。

5)在virt-p2v客户端启动后,根据其界面上的提示,填写前面准备的服务器端的IP或主机名、登录用户等信息,在连接上服务器端后,可以进一步填写转移后的虚拟客户机的名称、vCPU数量、内存大小等信息,最后点击"convert"(转化)按钮即可。virt-p2v客户端会将物理机中的磁盘信息通过网络传输到服务器端,待传输完成后,选择关闭该物理机即可。

6)在virt-p2v服务器端(KVM宿主机)通过libvirt或qemu-kvm命令行启动前面转移过来的客户机即可。

因为使用virt-p2v工具进行转换的步骤还比较复杂,获得可启动的ISO文件可能还需要Redhat的授权,且virt-p2v并不太成熟可能导致迁移不成功,所以使用KVM的普通用户在实际环境中使用virt-p2v工具的情况还不多。

将物理机转化为KVM客户机,是一个公司或个人实施KVM虚拟化的基本过程。可以很简单地完成这个过程:安装一个和物理机上面相同系统的客户机,然后将物理机磁盘中的内容完全复制到对应客户机中即可。或者,更简单地,将物理机的磁盘物理上放到KVM宿主机中,直接使用该磁盘作为客户机磁盘启动即可(QEMU/KVM支持客户机使用一个完整的物理磁盘)。只是需要注意根据自己的需求来使用相应的qemu-kvm命令行参数来启动客户机,或者配置libvirt使用的相应客户机的XML配置文件。

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/128693036
KvM