qemu-passthrough-security.rst 6.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
=============================
QEMU command-line passthrough
=============================

.. contents::

Libvirt aims to provide explicit modelling of virtualization features in
the domain XML document schema. QEMU has a very broad range of features
and not all of these can be mapped to elements in the domain XML. Libvirt
would like to reduce the gap to QEMU, however, with finite resources there
will always be cases which aren't covered by the domain XML schema.


XML document additions
======================

To deal with the problem, libvirt introduced support for command-line
passthrough of QEMU arguments. This is achieved by supporting a custom
XML namespace, under which some QEMU driver specific elements are defined.

The canonical place to declare the namespace is on the top level ``<domain>``
element. At the very end of the document, arbitrary command-line arguments
can now be added, using the namespace prefix ``qemu:``

::

   <domain type='kvm' xmlns:qemu='http://libvirt.org/schemas/domain/qemu/1.0'>
     <name>QEMUGuest1</name>
     <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
     ...
     <qemu:commandline>
       <qemu:arg value='-newarg'/>
       <qemu:arg value='parameter'/>
       <qemu:env name='ID' value='wibble'/>
       <qemu:env name='BAR'/>
     </qemu:commandline>
   </domain>

Note that when an argument takes a value eg ``-newarg parameter``, the argument
and the value must be passed as separate ``<qemu:arg>`` entries.

Instead of declaring the XML namespace on the top level ``<domain>`` it is also
possible to declare it at time of use, which is more convenient for humans
writing the XML documents manually. So the following example is functionally
identical:

::

   <domain type='kvm'>
     <name>QEMUGuest1</name>
     <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
     ...
     <commandline xmlns="http://libvirt.org/schemas/domain/qemu/1.0">
       <arg value='-newarg'/>
       <arg value='parameter'/>
       <env name='ID' value='wibble'/>
       <env name='BAR'/>
     </commandline>
   </domain>

Note that when querying the XML from libvirt, it will have been translated into
the canonical syntax once more with the namespace on the top level element.

Security confinement / sandboxing
=================================

When libvirt launches a QEMU process it makes use of a number of security
technologies to confine QEMU and thus protect the host from malicious VM
breakouts.

When configuring security protection, however, libvirt generally needs to know
exactly which host resources the VM is permitted to access. It gets this
information from the domain XML document. This only works for elements in the
regular schema, the arguments used with command-line passthrough are completely
opaque to libvirt.

As a result, if command-line passthrough is used to expose a file on the host
to QEMU, the security protections will activate and either kill QEMU or deny it
access.

There are two strategies for dealing with this problem, either figure out what
steps are needed to grant QEMU access to the device, or disable the security
protections.  The former is harder, but more secure, while the latter is simple.

Granting access per VM
----------------------

* SELinux - the file on the host needs an SELinux label that will grant access
  to QEMU's ``svirt_t`` policy.

  - Read-only access - use the ``virt_content_t`` label
  - Shared, write access - use the ``svirt_image_t:s0`` label (ie no Multi-
    Category Security (MCS) value appended)
  - Exclusive, write access - use the ``svirt_image_t:s0:MCS`` label for the VM.
    The MCS is auto-generatd at boot time, so this may require re-configuring
    the VM to have a fixed MCS label

* Discretionary Access Control (DAC) - the file on the host needs to be
  readable/writable to the ``qemu`` user or ``qemu`` group. This can be done
  by changing the file ownership to ``qemu``, or relaxing the permissions to
  allow world read, or adding file ACLs to allow access to ``qemu``.

* Namespaces - a private ``mount`` namespace is used for QEMU by default
  which populates a new ``/dev`` with only the device nodes needed by QEMU.
  There is no way to augment the set of device nodes ahead of time.

* Seccomp - libvirt launches QEMU with its built-in seccomp policy enabled with
  ``obsolete=deny``, ``elevateprivileges=deny``, ``spawn=deny`` and
  ``resourcecontrol=deny`` settings active. There is no way to change this
  policy on a per VM basis.

* Cgroups - a custom cgroup is created per VM and this will either use the
  ``devices`` controller or an ``BPF`` rule to whitelist a set of device nodes.
  There is no way to change this policy on a per VM basis.

Disabling security protection per VM
------------------------------------

Some of the security protections can be disabled per-VM:

* SELinux - in the domain XML the ``<seclabel>`` model can be changed to
  ``none`` instead of ``selinux``, which will make the VM run unconfined.

* DAC - in the domain XML an ``<seclabel>`` element with the ``dac`` model can
  be added, configured with a user / group account of ``root`` to make QEMU run
  with full privileges.

* Namespaces - there is no way to disable this per VM.

* Seccomp - there is no way to disable this per VM.

* Cgroups - there is no way to disable this per VM.

Disabling security protection host-wide
---------------------------------------

As a last resort it is possible to disable security protection host wide which
will affect all virtual machines. These settings are all made in
``/etc/libvirt/qemu.conf``

* SELinux - set ``security_default_confied = 0`` to make QEMU run unconfined by
  default, while still allowing explicit opt-in to SELinux for VMs.

* DAC - set ``user = root`` and ``group = root`` to make QEMU run as the root
  account.

* SELinux, DAC - set ``security_driver = []`` to entirely disable both the
  SELinux and DAC security drivers.

* Namespaces - set ``namespaces = []`` to disable use of the ``mount``
  namespaces, causing QEMU to see the normal fully popualated ``dev``.

* Seccomp - set ``seccomp_sandbox = 0`` to disable use of the Seccomp sandboxing
  in QEMU.

* Cgroups - set ``cgroup_device_acl`` to include the desired device node, or
  ``cgroup_controllers = [...]`` to exclude the ``devices`` controller.