[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] Re: Patch to python-virtinst to allow it to choose svirt labels



Daniel J Walsh wrote:

The patch didn't apply to latest upstream (there has been a lot of code
movement recently). I rediffed the patch to apply against current tip,
and made a few minor changes that don't change the overall result
(mentioned below).

> Also found at least one big bug in python-virtinst, VirtualDisk.py was
> dropping the "/" between dirname and basename of installation object,
> when you told it to create the object.
> 

This is already fixed upstream. You also had a minor bug fix in the
Installer class that is fixed as well, so I dropped both pieces.

> I think we want to have a big switch stored in libvirt somewhere saying
> whether or not we want isolated virtual machines.
> 

I think this should really be at the management tool level (i.e,
virt-manager). libvirt should be dumb in this respect, being passed a
label via the xml and doing with it what it's told.

I figure, virt-manager can have an option in Edit->Preferences,
something like "Isolate virtual machines with SELinux". Defaults to on.
If selinux isn't running, we disable the option with a tooltip
explaining why (or maybe hide it altogether). If the option is enabled,
virt-manager will assign labels to VMs at install time, and check all
active connections to avoid label collisions. More advanced behavior can
come later (assigning specific labels, some sort of collision
resolution with VMs on new connections, etc.)

Updated patch attached, I'll reply with patch specific comments later.

Thanks,
Cole
diff -r 026a37ccd417 virtinst/Guest.py
--- a/virtinst/Guest.py	Sat Feb 21 14:36:07 2009 -0500
+++ b/virtinst/Guest.py	Sat Feb 21 15:09:49 2009 -0500
@@ -27,6 +27,8 @@
 import libvirt
 import CapabilitiesParser
 import VirtualGraphics
+import selinux
+import random
 
 import osdict
 from virtinst import _virtinst as _
@@ -71,6 +73,8 @@
         self._cpuset = None
         self._graphics_dev = None
         self._consolechild = None
+        self._seclabel = None
+        self._imagelabel = None
 
         self._os_type = None
         self._os_variant = None
@@ -101,6 +105,16 @@
 
         self._caps = CapabilitiesParser.parse(self.conn.getCapabilities())
 
+        (self.default_seclabel,
+         self.default_imagelabel) =  self._default_seclabels()
+
+        while self._seclabel == None:
+            seclabel, imagelabel = self.gen_seclabels()
+            if self.is_conflict_seclabel(self.conn, seclabel):
+                continue
+            self.set_seclabel(seclabel)
+            self.set_imagelabel(imagelabel)
+
 
     def get_installer(self):
         return self._installer
@@ -110,6 +124,20 @@
         self._installer = val
     installer = property(get_installer, set_installer)
 
+    # Security context used to secure guest image 
+    def get_imagelabel(self):
+        return self._imagelabel
+    def set_imagelabel(self, val):
+        self._imagelabel = val
+    imagelabel = property(get_imagelabel, set_imagelabel)
+
+    # Security context used to secure guest process
+    def get_seclabel(self):
+        return self._seclabel
+    def set_seclabel(self, val):
+        self._seclabel = val
+    seclabel = property(get_seclabel, set_seclabel)
+
     # Domain name of the guest
     def get_name(self):
         return self._name
@@ -407,6 +435,19 @@
             xml = _util.xml_append(xml, sound_dev.get_xml_config())
         return xml
 
+    def _get_seclabel_xml(self):
+        xml = ""
+        if self._seclabel != None:
+            xml = """
+  <seclabel model='selinux'>
+    <label>%s</label>
+    <image>%s</image>
+  </seclabel>
+""" % ( self._seclabel, self._imagelabel)
+            print xml
+        return xml
+
+
     def _get_device_xml(self, install=True):
         xml = ""
 
@@ -494,6 +535,7 @@
   <devices>
 %(devices)s
   </devices>
+  %(secxml)s
 </domain>
 """ % { "type": self.type,
         "name": self.name, \
@@ -504,7 +546,8 @@
         "maxramkb": self.maxmemory * 1024, \
         "devices": self._get_device_xml(install), \
         "osblob": osblob, \
-        "action": action }
+        "action": action, 
+        "secxml": self._get_seclabel_xml()}
 
 
     def start_install(self, consolecb=None, meter=None, removeOld=False,
@@ -537,6 +580,8 @@
         """Ensure that devices are setup"""
         for disk in self._install_disks:
             disk.setup(progresscb)
+            # Not sure of this, might want to put this in VirtualDisk class
+            selinux.setfilecon(disk.path, self._imagelabel)
         for nic in self._install_nics:
             nic.setup(self.conn)
 
@@ -631,6 +676,80 @@
         if self.domain is not None:
             raise RuntimeError, _("Domain has already been started!")
 
+    def _default_seclabels(self):
+        try:
+            fd = open(selinux.selinux_virtual_domain_context_path(), 'r')
+        except OSError, (err_no, msg):
+            raise RuntimeError(_("Failed to find SELinux virtual domains "
+                                "context: %s: %s %s" %
+                                (selinux.selinux_virtual_domain_context_path(),
+                                 err_no, msg)))
+
+        label = fd.read()
+        fd.close()
+        try:
+            fd = open(selinux.selinux_virtual_image_context_path(), 'r')
+        except OSError, (err_no, msg):
+            raise RuntimeError(_("Failed to find SELinux virtual image "
+                               "context: %s: %s %s" %
+                               (selinux.selinux_virtual_domain_context_path(),
+                                err_no, msg)))
+
+        image = fd.read()
+        fd.close()
+
+        return (label, image)
+
+    def is_conflict_seclabel(self, conn, seclabel):
+        """
+        check if security label is in use by any other VMs on passed
+        connection.
+
+        @param conn: connection to check for collisions on
+        @type conn: libvirt.virConnect
+
+        @param seclabel: Security Label 
+        @type str: Security label 
+
+        @return: True if a collision, False otherwise
+        @rtype: C{bool}
+        """
+        if not seclabel:
+            return False
+
+        active, inactive = _util.fetch_all_guests(conn)
+        vms = active + inactive
+
+        count = 0
+        for vm in vms:
+            xml = vm.XMLDesc(0)
+            if _util.get_xml_path(xml, path="/domain/seclabel/label/"):
+                count += 1
+
+        if count > 0:
+            return True
+        else:
+            return False
+
+    def _get_random_mcs(self):
+        f1 = random.randrange(1024)
+        f2 = random.randrange(1024)
+        if f1 < f2:
+            return "s0:c%s,c%s" % (f1, f2)
+        else:
+            if f1 == f2:
+                return "s0:c%s" % f1
+            else:
+                return "s0:c%s,c%s" % (f2, f1)
+
+    def gen_seclabels(self):
+        mcs = self._get_random_mcs()
+        con = self.default_seclabel.split(':')
+        seclabel = "%s:%s:%s:%s" %  (con[0], con[1], con[2], mcs)
+        con = self.default_imagelabel.split(':')
+        imagelabel = "%s:%s:%s:%s" %  (con[0], con[1], con[2], mcs)
+        return (seclabel, imagelabel)
+
     def _set_defaults(self):
         if self.uuid is None:
             while 1:

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]