NB VM

From SELinux Wiki
Revision as of 14:30, 18 May 2010 by RichardHaines (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

SELinux Virtual Machine Support

SELinux support is available in the KVM/QEMU and Xen virtual machine (VM) technologies[1] that are discussed in the sections that follow, however the package documentation should be read for how these products actually work and how they are configured.

Currently the main SELinux support for virtualisation is via libvirt that is an open-source virtualisation API that can be used to dynamically load guest VMs on behalf of the virtualisation product. Security extensions were added as a part of the Svirt project and the SELinux implementation for the KVM/QEMU package (qemu-kvm and libvirt rpms) is discussed using some examples. The Xen product has Flask/TE services that can be builtas an optional service, although it can also use the security enhanced libvirt services as well.

The sections that follow give an introduction to KVM/QEMU and then the libvirt support with some examples that make use of the Virtual Machine Manager (virt-manager rpm) to configure VMs, an overview of the Xen implementation then follows.

KVM / QEMU Support

KVM is a kernel loadable module that uses the Linux kernel as a hypervisorand makes use of a modified QEMU emulator to support the hardware I/O emulation. The "Kernel-based Virtual Machine" document gives a good overview of how KVM and QEMU are implemented. It also provides an introduction the virtualisation in general.

The SELinux support for VMs is implemented by the libvirt sub-system that is used to manage the VM images using a Virtual Machine Manager, and as KVM is based on Linux it has SELinux support by default. There are also Reference Policy modules to support the overall infrastructure (KVM support is in various kernel and system modules with a virt module supporting the libvirt services). The KVM Environment diagram shows a high level overview with two VMs running in their own domains. The libsvirt Support section below shows how to configure these and their VM image files.


libvirt Support

The Svirt project added security hooks into the libvirt library that is used by the libvirtd daemon. This daemon is used by a number of VM products (such as KVM/QEMU and Xen) to start their VMs running as guest operating systems.

The security hooks can be utilised by any security mechanism by the VM supplier providing a product specific libvirt driver that loads and manages the images. The SELinux implementation (when SELinux is enabled on the host system) supports four methods of labeling VM images, processes and their resources with support from the Reference Policy modules/services/virt.* loadable module[2]. To support this labeling, libvirt requires an MCS or MLS enabled policy as the level entry of the security context is used (user:role:type:level) .

Default Mode

The default mode is where each VM is run under its own dynamically configured domain and image file therefore isolating the VMs from each other (i.e. every time the system is rebooted a different and unique MCS label will be generated to confine each VM to its own domain). This mode is implemented as follows:

  • An initial context for the process is obtained from the /etc/selinux/<policy_name>/contexts/virtual_domain_context file (the default is system_u:system_r:svirt_t:s0).
  • An initial context for the image file label is obtained from the /etc/selinux/<policy_name>/contexts/virtual_image_context file. The default is system_u:system_r:svirt_image_t:s0 that allows read/write of image files.
  • When the image is used to start the VM a random MCS level is generated and added to the process context and the image object context. The process and image objects are then transitioned to the context by the libselinux API calls setfilecon and setexeccon respectively (see security_selinux.c in the libvirt source). The following example shows two VM sessions having different label as they were launched during the same boot session:
VM Object Dynamically assigned security context
VM1 Process system_u:system_r:svirt_t:s0:c585,c813
VM1 File system_u:system_r:svirt_image_t:s0:c585,c813
VM2 Process system_u:system_r:svirt_t:s0:c535,c601
VM2 File system_u:system_r:svirt_image_t:s0:c535,c601


The running image ls -Z and ps -eZ are as follows and for completeness an ls -Z is shown when both VMs have been stopped:

# Both VMs running:
ls -Z /var/lib/libvirt/images
system_u:object_r:svirt_image_t:s0:c585,c813 Test_VM1.img
system_u:object_r:svirt_image_t:s0:c535,c601 Test_VM2.img

ps -eZ | grep qemu
system_u:system_r:svirt_t:s0:c585,c813 8707 ? 00:00:44 qemu
system_u:system_r:svirt_t:s0:c1022,c535 8796 ? 00:00:37 qemu

# Both VMs stopped (note that the categories are now missing AND the type has changed from svirt_image_t to virt_image_t):
ls -Z /var/lib/libvirt/images
system_u:object_r:virt_image_t:s0 Test_VM1.img
system_u:object_r:virt_image_t:s0 Test_VM2.img


Shared Image Mode

If the disk image has been set to shared, then a dynamically allocated level will be generated for each VM process instance, however there will be a single instance of the disk image.

The Virtual Machine Manager can be used to set the image as shareable by checking the Shareable box as shown in the Setting the image as Shareable or Readonly screen.

This will set the image (Test_VM1.xml) resource XML configuration file located in the /etc/libvirt/qemu directory <disk> contents as follows:

# /etc/libvirt/qemu/Test_VM1.xml:
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/images/Test_VM1.img'/>
<target dev='hda' bus='ide'/>
<shareable/>
</disk>

As the two VMs will share the same image, the Test_VM1 image needs to be cloned and its XML file <disk> contents are as follows (note that it has the same shared image source file name):

# /etc/libvirt/qemu/Test_VM1-clone.xml:
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/images/Test_VM1.img'/>
<target dev='hda' bus='ide'/>
<shareable/>
</disk>

Now that the image has been configured as shareable, the following initialisation process will take place:

  • An initial context for the process is obtained from the /etc/selinux/<policy_name>/contexts/virtual_domain_context file (the default is system_u:system_r:svirt_t:s0).
  • An initial context for the image file label is obtained from the /etc/selinux/<policy_name>/contexts/virtual_image_context file. The default is system_u:system_r:svirt_image_t:s0 that allows read/write of image files.
  • When the image is used to start the VM a random MCS level is generated and added to the process context (but not the image object). The process and image objects are then transitioned to the appropriate context by the libselinux API calls setfilecon and setexeccon respectively. The following example shows each VM having the same file label but different process labels:
VM Object Default security context
VM1 Process system_u:system_r:svirt_t:s0:c231,c245
VM1 File system_u:system_r:svirt_image_t:s0
VM2 Process system_u:system_r:svirt_t:s0:c695,c894
VM1 File system_u:system_r:svirt_image_t:s0


The running image ls -Z and ps -eZ are as follows and for completeness an ls -Z is shown when both VMs have been stopped:

# Both VMs running and sharing same image as Test_VM1 was cloned:
ls -Z /var/lib/libvirt/images
system_u:object_r:svirt_image_t:s0 Test_VM1.img

ps -eZ | grep qemu
system_u:system_r:svirt_t:s0:c231,c254 6748 ? 00:01:17 qemu
system_u:system_r:svirt_t:s0:c695,c894 7664 ? 00:00:03 qemu

# Both VMs stopped (note that the type has remained as svirt_image_t)
ls -Z /var/lib/libvirt/images
system_u:object_r:svirt_image_t:s0 Test_VM1.img


Readonly Image Mode

The readonly configuration sequence is similar to the shared option shown above with a dynamically allocated level generated for each VM process instance and the disk image can be shared. The major differences are that the disk image will be read only by setting the image context to virt_content_t (that enforces read only - see the virt.if module interface file - read_blk_files_pattern) instead of svirt_image_t (that allows read/write - rw_blk_files_pattern).

The Virtual Machine Manager can be used to set the image as read only by checking the Readonly box as shown in the Setting the image as Shareable or Readonly screen. This will set the image (Test_VM1.xml) resource XML configuration file located in the /etc/libvirt/qemu directory <disk> contents as follows:

# /etc/libvirt/qemu/Test_VM1.xml:
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/images/Test_VM1.img'/>
<target dev='hda' bus='ide'/>
<readonly/>
</disk>

As the two VMs will share the same image the Test_VM1 image needs to be cloned and its XML file <disk> contents will be as follows:

# /etc/libvirt/qemu/Test_VM1-clone.xml:
<disk type='file' device='disk'>
<driver name='qemu' type='raw'/>
<source file='/var/lib/libvirt/images/Test_VM1.img'/>
<target dev='hda' bus='ide'/>
<readonly/>
</disk>

Now that the image has been configured as readonly, the following initialisation process will take place:

  • An initial context for the process is obtained from the /etc/selinux/<policy_name>/contexts/virtual_domain_context file (the default is system_u:system_r:svirt_t:s0).
  • An initial context for the image file label is obtained from the /etc/selinux/<policy_name>/contexts/virtual_image_context file. The default for read only images is system_u:system_r:virt_content_t:s0 as discussed above.
  • When the image is used to start the VM a random MCS level is generated and added to the process context (but not the image object). The process and image objects are then transitioned to the appropriate context by the libselinux API calls setfilecon and setexeccon respectively. The following example shows each VM having the same file label but different process labels:


VM Object Default security context
VM1 Process system_u:system_r:svirt_t:s0:c103,c950
VM1 File system_u:system_r:virt_content_t:s0
VM2 Process system_u:system_r:svirt_t:s0:c312,c820
VM1 File system_u:system_r:virt_content_t:s0


The running image ls -Z and ps -eZ are as follows and for completeness an ls -Z is shown when both VMs have been stopped:

# Both VMs running and sharing same image as Test_VM1 was cloned:
ls -Z /var/lib/libvirt/images
system_u:object_r:virt_content_t:s0 Test_VM1.img

ps -eZ | grep qemu
system_u:system_r:svirt_t:s0:c103,c950 8756 ? 00:01:08 qemu
system_u:system_r:svirt_t:s0:c312,c820 9246 ? 00:01:03 qemu

# Both VMs stopped (note that the type remained as virt_content_t),
# however if the disk type was reset to <shared/>, then it would be
# reset back to virt_image_t:s0 once the VM was running again.
ls -Z /var/lib/libvirt/images
system_u:object_r:virt_content_t:s0 Test_VM1.img


Static Labeling

It is possible to set static labels on each image file, however a consequence of this is that the image cannot be cloned therefore an image for each VM will be required. This is the method used to configure VMs on MLS systems as there is a known label that would define the security level. With this method it is also possible to configure two or more VMs with the same security context so that they can share resources.

If using the Virtual Machine Manager GUI, then by default it will start each VM running as they are built, therefore they need to be stopped and then configured for static labels and the image file will also need to be relabeled. An example VM configuration follows where the VM has been created as Static_VM1 using the F-12 targeted policy in enforcing mode (just so all errors are flagged during the build):

  • Once the VM has been built, it will need to be stopped from the Static_VM1 Virtual Machine screen. Display the Security menu and select selinux as the Model and check the Static check box. The required security context can then be set - for this example svirt_t has been chosen as it is a valid context as shown in the Static Configuration screen.

This context will be written to the Static_VM1.xml file in the /etc/libvirt/qemu directory as follows:

<seclabel type='static' model='selinux'>
<label>system_u:system_r:svirt_t:s0:c1022.c1023</label>
</seclabel>
  • If the VM is now started an error will be shown as follows as shown in the Image Start Error screen.
This is because the image file label is incorrect as by default it is labeled virt_image_t when the VM image is built (and svirt_t does not have read/write permission for this label):
# The default label of the image at build time:
system_u:object_r:virt_image_t:s0 Static_VM1.img

There are a number of ways to fix this, such as adding an allow rule or changing the image file label. In this example the image file label will be changed using chcon as follows:

# This command is executed from /var/lib/libvirt/images
#
# This sets the correct type:
chcon -t svirt_image_t Static_VM1.img

If required, the image can also be relabeled so that the [level] is the same as the process using chcon as follows:

# This command is executed from /var/lib/libvirt/images
#
# Set the MCS label to match the process (optional step):
chcon -l s0:c1022,c1023 Static_VM1.img
  • Now that the image has been relabeled, the VM can now be started.

The following example shows two VMs (the unconfined_t configuration is discussed below):


VM Object Static security context
VM1 Process system_u:system_r:svirt_t:s0:c1022,c1023
VM1 File system_u:system_r:svirt_image_t:s0:c1022,c1023
VM2 Process system_u:system_r:unconfined_t:s0:c11,c22
VM2 File system_u:system_r:virt_image_t:s0


The running image ls -Z and ps -eZ are as follows, and for completeness an ls -Z is shown when both VMs have been stopped:

# Both VMs running:
ls -Z /var/lib/libvirt/images
system_u:object_r:svirt_image_t:s0:c1022,c1023 Static_VM1.img
system_u:object_r:virt_image_t:s0:c11,c22 Static_VM2.img

ps -eZ | grep qemu
system_u:system_r:svirt_t:s0:c585,c813 6707 ? 00:00:45 qemu
system_u:system_r:unconfined_t:s0:c11,c22 6796 ? 00:00:26 qemu

# Both VMs stopped (note that Static_VM1.img was relabeled svirt_image_t to enable it to run, however Static_VM2.img is still labeled
# virt_image_t and runs okay. This is because the process is run as unconfined_t that is allowed to use virt_image_t):

system_u:object_r:svirt_image_t:s0:c1022,c1023 Static_VM1.img
system_u:object_r:virt_image_t:s0 Static_VM2.img

Configuring the unconfined_t image

The objective of this section is to configure a VM domain that the targeted policy does not currently support. The domain chosen is unconfined_t as that is the default for general users and requires a minimal additional policy module. The steps required to enable the VM are:

  • Using the Virtual Machine Manager, generate a VM (this has been called Static_VM2).
  • Stop the VM and set a static context of system_u:system_r:unconfined_t:s0:c11,c22. This context will be written to the Static_VM2.xml file in the /etc/libvirt/qemu directory as follows:
<seclabel type='static' model='selinux'>
<label>system_u:system_r:unconfined_t:s0:c11,c22</label>
</seclabel>
  • Before attempting to start the VM clear the audit log first so that a module can be generated with audit2allow to allow the VM to start:
> /var/log/audit/audit.log

This is because the libvirt daemon does not have permission to transition the VM process to the unconfined_t domain. The audit log AVC entry would be:

type=AVC msg=audit(1271080140.988:30): avc: denied { transition } for pid=2000 comm="libvirtd" path="/usr/bin/qemu" dev=dm-0 ino=71778 scontext=system_u:system_r:virtd_t:s0-s0:c0.c1023 tcontext=system_u:system_r:unconfined_t:s0:c11,c22 tclass=process

type=SYSCALL msg=audit(1271080140.988:30): arch=40000003 syscall=11 success=no exit=-13 a0=b425c470 a1=b427f610 a2=b42a4a68 a3=0 items=0 ppid=1999 pid=2000 auid=4294967295 uid=107 gid=107 euid=107 suid=107 fsuid=107 egid=107 sgid=107 fsgid=107 tty=(none) ses=4294967295 comm="libvirtd" exe="/usr/sbin/libvirtd" subj=system_u:system_r:virtd_t:s0-s0:c0.c1023 key=(null)
  • To generate a loadable module that will allow the transition use the following commands:
# These cmds will generate an unconfinedvm.pp module package:

cat /var/log/audit/audit.log | audit2allow -M unconfinedvm > unconfinedvm.te

# Once the package has been generated, it needs to be activated:
semodule -i unconfinedvm.pp
  • Once the module has been loaded and the policy rebuilt, the VM can now be started. For reference the module file generated by audit2allow consists of the following:
module unconfinedvm 1.0;

require {
type unconfined_t;
type virtd_t;
class process transition;
}

#============= virtd_t ==============
allow virtd_t unconfined_t:process transition;


Xen Support

This is not supported by SELinux in the usual way as it is built into the actual Xen software as a 'Flask/TE' extension[3] for the XSM (Xen Security Module). Also the Xen implementation has its own built-in policy (xen.te) and supporting definitions for access vectors, security classes and initial SIDs for the policy. These Flask/TE components run in Domain 0 as part of the domain management and control supporting the Virtual Machine Monitor (VMM) as shown in the Xen Hypervisor diagram.

The "How Does Xen Work" document describes the basic operation of Xen, the "Xen Security Modules" document describes the XSM/Flask implementation, and the xsm-flask.txt file in the Xen source package describes how SELinux and its supporting policy is implemented.

However (just to confuse the issue), there is another Xen policy module (also called xen.te) in the Reference Policy to support the management of images etc. via the Xen console.




  1. KVM (Kernel-based Virtual Machine) and Xen are classed as 'bare metal' hypervisors and they rely on other services to manage the overall VM environment. QEMU (Quick Emulator) is an emulator that emulates the BIOS and I/O device functionality and can be used standalone or with KVM and Xen.
  2. The various images would have been labeled by the virt module installation process (see the virt.fc module file or the policy file_contexts file libvirt entries). If not, then need to ensure it is relabeled by the most appropriate SELinux tool.
  3. This is a version of the SELinux security server, avc etc. that has been specifically ported for the Xen implementation.