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:
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list