def __init__(self, **kwargs): super(QubesNetVm, self).__init__(**kwargs) self.connected_vms = QubesVmCollection() self.__network = "10.137.{0}.0".format(self.netid) self.__netmask = defaults["vm_default_netmask"] self.__gateway = self.netprefix + "1" self.__secondary_dns = self.netprefix + "254" self.__external_ip_allowed_xids = set() self.log.debug('network={} netmask={} gateway={} secondary_dns={}'.format( self.network, self.netmask, self.gateway, self.secondary_dns))
def main(): global qubes_host qubes_host = QubesHost() global app app = QApplication(sys.argv) app.setOrganizationName("The Qubes Project") app.setOrganizationDomain("http://qubes-os.org") app.setApplicationName("Qubes VM Settings") sys.excepthook = handle_exception qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() vm = None tab = "basic" if len(sys.argv) > 1: vm = qvm_collection.get_vm_by_name(sys.argv[1]) if vm is None or vm.qid not in qvm_collection: QMessageBox.critical(None, "Qubes VM Settings Error", "A VM with the name '{0}' does not exist in the system.".format(sys.argv[1])) sys.exit(1) if len(sys.argv) > 2: tab_arg = sys.argv[2] print tab_arg if tab_arg in VMSettingsWindow.tabs_indices: tab = tab_arg else: QMessageBox.warning(None, "Qubes VM Settings Error", "There is no such tab as '{0}'. Opening default tab instead.".format(tab_arg)) else: vms_list = [vm.name for vm in qvm_collection.values() if (vm.is_appvm() or vm.is_template())] vmname = QInputDialog.getItem(None, "Select VM", "Select VM:", vms_list, editable = False) if not vmname[1]: sys.exit(1) vm = qvm_collection.get_vm_by_name(vmname[0]) global settings_window settings_window = VMSettingsWindow(vm, app, qvm_collection, tab) settings_window.show() app.exec_() app.exit()
def ext_pillar(minion_id, pillar, *args, **kwargs): qc = QubesVmCollection() qc.lock_db_for_reading() qc.load() qc.unlock_db() vm = qc.get_vm_by_name(minion_id) if vm is None: return {} qvm_pillar = {} if vm.qid == 0: qvm_pillar['type'] = 'admin' elif vm.is_template(): qvm_pillar['type'] = 'template' elif vm.template is None: qvm_pillar['type'] = 'standalone' else: qvm_pillar['type'] = 'app' if vm.template: qvm_pillar['template'] = vm.template.name if vm.netvm: qvm_pillar['netvm'] = vm.netvm.name # TODO: consider other properties; target VM will learn them! return {'qubes': qvm_pillar}
def main(): global qubes_host qubes_host = QubesHost() global app app = QApplication(sys.argv) app.setOrganizationName("The Qubes Project") app.setOrganizationDomain("http://qubes-os.org") app.setApplicationName("Qubes Restore VMs") sys.excepthook = handle_exception qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() global restore_window restore_window = RestoreVMsWindow() restore_window.show() app.exec_() app.exit()
def vm(self, value): # pylint: disable=C0103 ''' Get Qubes VM object from qvm.collection and set it here. ''' if value: qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() qvm = qvm_collection.get_vm_by_name(value) if qvm and qvm.qid in qvm_collection: self._vm = qvm # pylint: disable=W0212 return self._vm = None # pylint: disable=W0212
def app_main(): global qubes_host qubes_host = QubesHost() global app app = QApplication(sys.argv) app.setOrganizationName("The Qubes Project") app.setOrganizationDomain("http://qubes-os.org") app.setApplicationName("Qubes Backup VMs") sys.excepthook = handle_exception qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() global backup_window backup_window = BackupVMsWindow() backup_window.show() app.exec_() app.exit()
def setUp(self): self.qc = QubesVmCollection() self.qc.lock_db_for_writing() self.qc.load() self.updatevm = self.qc.add_new_vm( "QubesProxyVm", name="%supdatevm" % VM_PREFIX, template=self.qc.get_default_template()) self.updatevm.create_on_disk(verbose=False) self.saved_updatevm = self.qc.get_updatevm_vm() self.qc.set_updatevm_vm(self.updatevm) self.qc.save() self.qc.unlock_db() subprocess.call(['sudo', 'rpm', '-e', self.pkg_name], stderr=open(os.devnull, 'w')) subprocess.check_call([ 'sudo', 'rpm', '--import', os.path.join(self.tmpdir, 'pubkey.asc') ]) self.updatevm.start()
def notify_vms(): monitor_layout = get_monitor_layout() if len(monitor_layout) == 0: return qc = QubesVmCollection() qc.lock_db_for_reading() qc.load() qc.unlock_db() pipes = [] for vm in qc.values(): if vm.qid == 0: continue if vm.is_running(): pipes.append(notify_vm(vm, monitor_layout)) for p in pipes: p.wait()
def notify_vm_by_name(vmname): monitor_layout = get_monitor_layout() if len(monitor_layout) == 0: return qc = QubesVmCollection() qc.lock_db_for_reading() qc.load() qc.unlock_db() vm = qc.get_vm_by_name(vmname) if not vm: print >> sys.stderr, "No such VM!" return 1 if not vm.is_running(): print >> sys.stderr, "VM not running!" return 1 pipe = notify_vm(vm, monitor_layout) pipe.wait() return 0
def setUp(self): self.qc = QubesVmCollection() self.qc.lock_db_for_writing() self.qc.load() self.updatevm = self.qc.add_new_vm("QubesProxyVm", name="%supdatevm" % VM_PREFIX, template=self.qc.get_default_template()) self.updatevm.create_on_disk(verbose=False) self.saved_updatevm = self.qc.get_updatevm_vm() self.qc.set_updatevm_vm(self.updatevm) self.qc.save() self.qc.unlock_db() subprocess.call(['sudo', 'rpm', '-e', self.pkg_name], stderr=open(os.devnull, 'w')) subprocess.check_call(['sudo', 'rpm', '--import', os.path.join(self.tmpdir, 'pubkey.asc')]) self.updatevm.start()
def notify_vm_by_name(vmname): monitor_layout = get_monitor_layout() if len(monitor_layout) == 0: return qc = QubesVmCollection() qc.lock_db_for_reading() qc.load() qc.unlock_db() vm = qc.get_vm_by_name(vmname) if not vm: print >>sys.stderr, "No such VM!" return 1 if not vm.is_running(): print >>sys.stderr, "VM not running!" return 1 pipe = notify_vm(vm, monitor_layout) pipe.wait() return 0
class QubesTemplateHVm(QubesHVm): """ A class that represents an HVM template. A child of QubesHVm. """ # In which order load this VM type from qubes.xml load_order = 50 def get_attrs_config(self): attrs_config = super(QubesTemplateHVm, self).get_attrs_config() attrs_config['dir_path']['func'] = \ lambda value: value if value is not None else \ os.path.join(system_path["qubes_templates_dir"], self.name) attrs_config['label']['default'] = defaults["template_label"] return attrs_config def __init__(self, **kwargs): super(QubesTemplateHVm, self).__init__(**kwargs) self.appvms = QubesVmCollection() @property def type(self): return "TemplateHVM" @property def updateable(self): return True def is_template(self): return True def is_appvm(self): return False @property def rootcow_img(self): return self.storage.rootcow_img @classmethod def is_template_compatible(cls, template): if template is None: return True return False def resize_root_img(self, size): for vm in self.appvms.values(): if vm.is_running(): raise QubesException("Cannot resize root.img while any VM " "based on this tempate is running") return super(QubesTemplateHVm, self).resize_root_img(size) def start(self, *args, **kwargs): for vm in self.appvms.values(): if vm.is_running(): raise QubesException( "Cannot start HVM template while VMs based on it are running" ) return super(QubesTemplateHVm, self).start(*args, **kwargs) def commit_changes(self, verbose=False): self.log.debug('commit_changes()') if not vmm.offline_mode: assert not self.is_running( ), "Attempt to commit changes on running Template VM!" if verbose: print >> sys.stderr, "--> Commiting template updates... COW: {0}...".format( self.rootcow_img) if dry_run: return self.storage.commit_template_changes()
def setUpClass(cls): if cls.template == 'whonix-gw' or 'minimal' in cls.template: raise unittest.SkipTest( 'Template {} not supported by this test'.format(cls.template)) if cls.template == 'whonix-ws': # TODO remove when Whonix-based DispVMs will work (Whonix 13?) raise unittest.SkipTest( 'Template {} not supported by this test'.format(cls.template)) qc = QubesVmCollection() cls._kill_test_vms(qc, prefix=qubes.tests.CLSVMPREFIX) qc.lock_db_for_writing() qc.load() cls._remove_test_vms(qc, qubes.qubes.vmm.libvirt_conn, prefix=qubes.tests.CLSVMPREFIX) cls.source_vmname = cls.make_vm_name('source', True) source_vm = qc.add_new_vm("QubesAppVm", template=qc.get_vm_by_name(cls.template), name=cls.source_vmname) source_vm.create_on_disk(verbose=False) cls.target_vmname = cls.make_vm_name('target', True) target_vm = qc.add_new_vm("QubesAppVm", template=qc.get_vm_by_name(cls.template), name=cls.target_vmname) target_vm.create_on_disk(verbose=False) qc.save() qc.unlock_db() source_vm.start() target_vm.start() # make sure that DispVMs will be started of the same template retcode = subprocess.call(['/usr/bin/qvm-create-default-dvm', cls.template], stderr=open(os.devnull, 'w')) assert retcode == 0, "Error preparing DispVM"
frontend=sys.argv[3].split('-') if len(frontend)!=2: print 'Error: frontendvm-device must be in <controller>-<port> format' sys.exit(1) (controller, port)=frontend if len(sys.argv)>4: backendvm_xid=int(sys.argv[4]) backendvm_name=xen.lowlevel.xl.ctx().domid_to_name(backendvm_xid) else: backendvm_xid=0 cmd = "/usr/lib/qubes/vusb-ctl.py unbind '%s'" % backendvm_device if backendvm_xid == 0: os.system("sudo %s" % cmd) else: from qubes.qubes import QubesVmCollection qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() # launch qvm_collection.get_vm_by_name(backendvm_name).run(cmd, user="******") # FIXME: command injection os.system("xenstore-write /local/domain/%s/backend/vusb/%s/%s/port/%s ''" % (backendvm_xid, frontendvm_xid, controller, port))
def main(): global qubes_host qubes_host = QubesHost() global app app = QApplication(sys.argv) app.setOrganizationName("The Qubes Project") app.setOrganizationDomain("http://qubes-os.org") app.setApplicationName("Qubes VM Settings") sys.excepthook = handle_exception qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() vm = None tab = "basic" if len(sys.argv) > 1: vm = qvm_collection.get_vm_by_name(sys.argv[1]) if vm is None or vm.qid not in qvm_collection: QMessageBox.critical( None, "Qubes VM Settings Error", "A VM with the name '{0}' does not exist in the system.". format(sys.argv[1])) sys.exit(1) if len(sys.argv) > 2: tab_arg = sys.argv[2] print tab_arg if tab_arg in VMSettingsWindow.tabs_indices: tab = tab_arg else: QMessageBox.warning( None, "Qubes VM Settings Error", "There is no such tab as '{0}'. Opening default tab instead." .format(tab_arg)) else: vms_list = [ vm.name for vm in qvm_collection.values() if (vm.is_appvm() or vm.is_template()) ] vmname = QInputDialog.getItem(None, "Select VM", "Select VM:", vms_list, editable=False) if not vmname[1]: sys.exit(1) vm = qvm_collection.get_vm_by_name(vmname[0]) global settings_window settings_window = VMSettingsWindow(vm, app, qvm_collection, tab) settings_window.show() app.exec_() app.exit()
def main(): module = AnsibleModule(argument_spec=dict( name=dict(required=True, type='str'), state=dict(default='present', choices=['present', 'absent']), type=dict(default='appvm', choices=['appvm', 'netvm', 'proxyvm', 'hvm', 'templatehvm']), template=dict(type='str'), standalone=dict(default=False, type='bool'), label=dict(choices=[ 'red', 'orange', 'yellow', 'green', 'gray', 'blue', 'purple', 'black' ]), pool_name=dict(default='default', type='str'), memory=dict(type='int'), maxmem=dict(type='int'), mac=dict(type='str'), pci_strictreset=dict(type='bool'), pci_e820_host=dict(default=False, type='bool'), netvm=dict(type='str'), dispvm_netvm=dict(type='str'), kernel=dict(type='str'), vcpus=dict(type='int'), kernelopts=dict(type='str'), drive=dict(type='str'), debug=dict(type='bool'), default_user=dict(type='str'), include_in_backups=dict(type='bool'), qrexec_installed=dict(type='bool'), internal=dict(type='bool'), guiagent_installed=dict(type='bool'), seamless_gui_mode=dict(type='bool'), autostart=dict(type='bool'), qrexec_timeout=dict(type='int'), timezone=dict(), )) if not QUBES_DOM0: module.fail_json(msg='This module must be run from QubeOS dom0') qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_writing() qvm_collection.load() # Set parameters changed = True options = set_options(module, qvm_collection) if options['state'] == 'present': if qvm_collection.get_vm_by_name(options['args']['name']) is not None: changed = False qube = qvm_collection.get_vm_by_name(options['args']['name']) for key in options['args']: if key == 'pool_name': if qube.pool_name != options['args']['pool_name']: module.fail_json( msg='Existing VM storage pool cannot be changed') elif getattr(qube, key) != options['args'][key]: setattr(qube, key, options['args'][key]) changed = True if not isinstance(qube, QubesVmClasses[options['type']]): module.fail_json(msg='Existing VM type cannot be changed') else: try: qube = qvm_collection.add_new_vm(options['type'], **options['args']) except QubesException as e: module.fail_json(msg='Unable to create VM: %s' % e) qube.create_on_disk(source_template=options['base_template']) elif options['state'] == 'absent': if qvm_collection.get_vm_by_name(options['args']['name']) is None: changed = False else: qube = qvm_collection.get_vm_by_name(options['args']['name']) if qube.is_running(): try: qube.force_shutdown() except (IOError, OSError, QubesException) as e: module.fail_json(msg='Unable to shutdown VM: %s' % e) if qube.is_template(): # Report what VMs use this template dependent_qubes = qube.qvm_collection.get_vms_based_on( qube.qid) if len(dependent_qubes) > 0: module.fail_json( msg='Please remove VMs dependent on this template first' ) if qvm_collection.default_template_qid == qube.qid: qvm_collection.default_template_qid = None if qube.is_netvm(): if qvm_collection.default_netvm_qid == qube.qid: qvm_collection.default_netvm_qid = None if qube.installed_by_rpm: module.fail_json(msg='Qube managed by RPM/DNF') qube.remove_from_disk() qvm_collection.pop(qube.qid) qvm_collection.save() qvm_collection.unlock_db() module.exit_json(changed=changed)
def do_create_vm(vmclass, vmname, label, template_vm, netvm, standalone, allow_networking, thread_monitor): vm = None qc = QubesVmCollection() qc.lock_db_for_writing() qc.load() try: if not standalone: vm = qc.add_new_vm(vmclass, name=vmname, template=template_vm, label=label) else: vm = qc.add_new_vm(vmclass, name=vmname, template=None, label=label) vm.create_on_disk(verbose=False, source_template=template_vm) if not allow_networking: vm.uses_default_netvm = False vm.netvm = None else: vm.netvm = netvm if vm.netvm.qid == qc.get_default_netvm().qid: vm.uses_default_netvm = True else: vm.uses_default_netvm = False qc.save() except Exception as ex: thread_monitor.set_error_msg(str(ex)) if vm: vm.remove_from_disk() qc.pop(vm.qid) finally: qc.unlock_db() thread_monitor.set_finished()
def __init__(self, **kwargs): super(QubesTemplateHVm, self).__init__(**kwargs) self.appvms = QubesVmCollection()
class TC_00_Dom0Upgrade(qubes.tests.QubesTestCase): cleanup_paths = [] pkg_name = 'qubes-test-pkg' dom0_update_common_opts = [ '--disablerepo=*', '--enablerepo=test', '--setopt=test.copy_local=1' ] @classmethod def generate_key(cls, keydir): gpg_opts = [ 'gpg', '--quiet', '--no-default-keyring', '--homedir', keydir ] p = subprocess.Popen(gpg_opts + ['--gen-key', '--batch'], stdin=subprocess.PIPE, stderr=open(os.devnull, 'w')) p.stdin.write(''' Key-Type: RSA Key-Length: 1024 Key-Usage: sign Name-Real: Qubes test Expire-Date: 0 %commit '''.format(keydir=keydir)) p.stdin.close() p.wait() subprocess.check_call( gpg_opts + ['-a', '--export', '--output', os.path.join(keydir, 'pubkey.asc')]) p = subprocess.Popen(gpg_opts + ['--with-colons', '--list-keys'], stdout=subprocess.PIPE) for line in p.stdout.readlines(): fields = line.split(':') if fields[0] == 'pub': return fields[4][-8:].lower() raise RuntimeError @classmethod def setUpClass(cls): super(TC_00_Dom0Upgrade, cls).setUpClass() cls.tmpdir = tempfile.mkdtemp() cls.cleanup_paths += [cls.tmpdir] cls.keyid = cls.generate_key(cls.tmpdir) p = subprocess.Popen( ['sudo', 'dd', 'status=none', 'of=/etc/yum.repos.d/test.repo'], stdin=subprocess.PIPE) p.stdin.write(''' [test] name = Test baseurl = file:///tmp/repo enabled = 1 ''') p.stdin.close() p.wait() @classmethod def tearDownClass(cls): subprocess.check_call( ['sudo', 'rm', '-f', '/etc/yum.repos.d/test.repo']) for dir in cls.cleanup_paths: shutil.rmtree(dir) cls.cleanup_paths = [] def setUp(self): self.qc = QubesVmCollection() self.qc.lock_db_for_writing() self.qc.load() self.updatevm = self.qc.add_new_vm( "QubesProxyVm", name="%supdatevm" % VM_PREFIX, template=self.qc.get_default_template()) self.updatevm.create_on_disk(verbose=False) self.saved_updatevm = self.qc.get_updatevm_vm() self.qc.set_updatevm_vm(self.updatevm) self.qc.save() self.qc.unlock_db() subprocess.call(['sudo', 'rpm', '-e', self.pkg_name], stderr=open(os.devnull, 'w')) subprocess.check_call([ 'sudo', 'rpm', '--import', os.path.join(self.tmpdir, 'pubkey.asc') ]) self.updatevm.start() def remove_vms(self, vms): self.qc.lock_db_for_writing() self.qc.load() self.qc.set_updatevm_vm(self.qc[self.saved_updatevm.qid]) for vm in vms: if isinstance(vm, str): vm = self.qc.get_vm_by_name(vm) else: vm = self.qc[vm.qid] if vm.is_running(): try: vm.force_shutdown() except: pass try: vm.remove_from_disk() except OSError: pass self.qc.pop(vm.qid) self.qc.save() self.qc.unlock_db() def tearDown(self): vmlist = [ vm for vm in self.qc.values() if vm.name.startswith(VM_PREFIX) ] self.remove_vms(vmlist) subprocess.call(['sudo', 'rpm', '-e', self.pkg_name], stderr=open(os.devnull, 'w')) subprocess.call( ['sudo', 'rpm', '-e', 'gpg-pubkey-{}'.format(self.keyid)], stderr=open(os.devnull, 'w')) for pkg in os.listdir(self.tmpdir): if pkg.endswith('.rpm'): os.unlink(pkg) def create_pkg(self, dir, name, version): spec_path = os.path.join(dir, name + '.spec') spec = open(spec_path, 'w') spec.write(''' Name: {name} Summary: Test Package Version: {version} Release: 1 Vendor: Invisible Things Lab License: GPL Group: Qubes URL: http://www.qubes-os.org %description Test package %install %files '''.format(name=name, version=version)) spec.close() subprocess.check_call([ 'rpmbuild', '--quiet', '-bb', '--define', '_rpmdir {}'.format(dir), spec_path ]) pkg_path = os.path.join(dir, 'x86_64', '{}-{}-1.x86_64.rpm'.format(name, version)) subprocess.check_call(['sudo', 'chmod', 'go-rw', '/dev/tty']) subprocess.check_call([ 'rpm', '--quiet', '--define=_gpg_path {}'.format(dir), '--define=_gpg_name {}'.format("Qubes test"), '--addsign', pkg_path ], stdin=open(os.devnull), stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) subprocess.check_call(['sudo', 'chmod', 'go+rw', '/dev/tty']) return pkg_path def send_pkg(self, filename): p = self.updatevm.run('mkdir -p /tmp/repo; cat > /tmp/repo/{}'.format( os.path.basename(filename)), passio_popen=True) p.stdin.write(open(filename).read()) p.stdin.close() p.wait() self.updatevm.run('cd /tmp/repo; createrepo .', wait=True) def test_000_update(self): filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') subprocess.check_call(['sudo', 'rpm', '-i', filename]) filename = self.create_pkg(self.tmpdir, self.pkg_name, '2.0') self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts, stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) except subprocess.CalledProcessError: self.fail("qubes-dom0-update failed: " + open(logpath).read()) retcode = subprocess.call( ['rpm', '-q', '{}-1.0'.format(self.pkg_name)], stdout=open(os.devnull, 'w')) self.assertEqual( retcode, 1, 'Package {}-1.0 still installed after ' 'update'.format(self.pkg_name)) retcode = subprocess.call( ['rpm', '-q', '{}-2.0'.format(self.pkg_name)], stdout=open(os.devnull, 'w')) self.assertEqual( retcode, 0, 'Package {}-2.0 not installed after ' 'update'.format(self.pkg_name)) def test_010_instal(self): filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts + [self.pkg_name], stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) except subprocess.CalledProcessError: self.fail("qubes-dom0-update failed: " + open(logpath).read()) retcode = subprocess.call( ['rpm', '-q', '{}-1.0'.format(self.pkg_name)], stdout=open('/dev/null', 'w')) self.assertEqual(retcode, 0, 'Package {}-1.0 not installed'.format(self.pkg_name)) def test_020_install_wrong_sign(self): subprocess.call( ['sudo', 'rpm', '-e', 'gpg-pubkey-{}'.format(self.keyid)]) filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts + [self.pkg_name], stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) self.fail("qubes-dom0-update unexpectedly succeeded: " + open(logpath).read()) except subprocess.CalledProcessError: pass retcode = subprocess.call( ['rpm', '-q', '{}-1.0'.format(self.pkg_name)], stdout=open('/dev/null', 'w')) self.assertEqual( retcode, 1, 'Package {}-1.0 installed although ' 'signature is invalid'.format(self.pkg_name)) def test_030_install_unsigned(self): filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') subprocess.check_call(['rpm', '--delsign', filename], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts + [self.pkg_name], stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) self.fail("qubes-dom0-update unexpectedly succeeded: " + open(logpath).read()) except subprocess.CalledProcessError: pass retcode = subprocess.call( ['rpm', '-q', '{}-1.0'.format(self.pkg_name)], stdout=open('/dev/null', 'w')) self.assertEqual( retcode, 1, 'UNSIGNED package {}-1.0 installed'.format(self.pkg_name))
class QubesNetVm(QubesVm): """ A class that represents a NetVM. A child of QubesCowVM. """ # In which order load this VM type from qubes.xml load_order = 70 def get_attrs_config(self): attrs_config = super(QubesNetVm, self).get_attrs_config() attrs_config['dir_path']['func'] = \ lambda value: value if value is not None else \ os.path.join(system_path["qubes_servicevms_dir"], self.name) attrs_config['label']['default'] = defaults["servicevm_label"] attrs_config['memory']['default'] = 300 # New attributes attrs_config['netid'] = { 'save': lambda: str(self.netid), 'order': 30, 'func': lambda value: value if value is not None else self._collection.get_new_unused_netid() } attrs_config['netprefix'] = { 'func': lambda x: "10.137.{0}.".format(self.netid) } attrs_config['dispnetprefix'] = { 'func': lambda x: "10.138.{0}.".format(self.netid) } # Dont save netvm prop attrs_config['netvm'].pop('save') attrs_config['uses_default_netvm'].pop('save') return attrs_config def __init__(self, **kwargs): super(QubesNetVm, self).__init__(**kwargs) self.connected_vms = QubesVmCollection() self.__network = "10.137.{0}.0".format(self.netid) self.__netmask = defaults["vm_default_netmask"] self.__gateway = self.netprefix + "1" self.__secondary_dns = self.netprefix + "254" self.__external_ip_allowed_xids = set() self.log.debug('network={} netmask={} gateway={} secondary_dns={}'.format( self.network, self.netmask, self.gateway, self.secondary_dns)) @property def type(self): return "NetVM" def is_netvm(self): return True @property def gateway(self): return self.__gateway @property def secondary_dns(self): return self.__secondary_dns @property def netmask(self): return self.__netmask @property def network(self): return self.__network def get_ip_for_vm(self, qid): lo = qid % 253 + 2 assert lo >= 2 and lo <= 254, "Wrong IP address for VM" return self.netprefix + "{0}".format(lo) def get_ip_for_dispvm(self, dispid): lo = dispid % 254 + 1 assert lo >= 1 and lo <= 254, "Wrong IP address for VM" return self.dispnetprefix + "{0}".format(lo) def update_external_ip_permissions(self, xid = -1): # TODO: VMs in __external_ip_allowed_xids should be notified via RPC # service on exteran IP change pass def start(self, **kwargs): if dry_run: return xid=super(QubesNetVm, self).start(**kwargs) # Connect vif's of already running VMs for vm in self.connected_vms.values(): if not vm.is_running(): continue if 'verbose' in kwargs and kwargs['verbose']: print >> sys.stderr, "--> Attaching network to '{0}'...".format(vm.name) # Cleanup stale VIFs vm.cleanup_vifs() # force frontend to forget about this device # module actually will be loaded back by udev, as soon as network is attached try: vm.run("modprobe -r xen-netfront xennet", user="******") except: pass try: vm.attach_network(wait=False) except QubesException as ex: print >> sys.stderr, ("WARNING: Cannot attach to network to '{0}': {1}".format(vm.name, ex)) return xid def shutdown(self, force=False): if dry_run: return connected_vms = [vm for vm in self.connected_vms.values() if vm.is_running()] if connected_vms and not force: raise QubesException("There are other VMs connected to this VM: " + str([vm.name for vm in connected_vms])) super(QubesNetVm, self).shutdown(force=force) def add_external_ip_permission(self, xid): if int(xid) < 0: return self.__external_ip_allowed_xids.add(int(xid)) self.update_external_ip_permissions() def remove_external_ip_permission(self, xid): self.__external_ip_allowed_xids.discard(int(xid)) self.update_external_ip_permissions()
class QubesTemplateHVm(QubesHVm): """ A class that represents an HVM template. A child of QubesHVm. """ # In which order load this VM type from qubes.xml load_order = 50 def get_attrs_config(self): attrs_config = super(QubesTemplateHVm, self).get_attrs_config() attrs_config['dir_path']['func'] = \ lambda value: value if value is not None else \ os.path.join(system_path["qubes_templates_dir"], self.name) attrs_config['label']['default'] = defaults["template_label"] return attrs_config def __init__(self, **kwargs): super(QubesTemplateHVm, self).__init__(**kwargs) self.appvms = QubesVmCollection() @property def type(self): return "TemplateHVM" @property def updateable(self): return True def is_template(self): return True def is_appvm(self): return False @property def rootcow_img(self): return self.storage.rootcow_img @classmethod def is_template_compatible(cls, template): if template is None: return True return False def resize_root_img(self, size): for vm in self.appvms.values(): if vm.is_running(): raise QubesException("Cannot resize root.img while any VM " "based on this tempate is running") return super(QubesTemplateHVm, self).resize_root_img(size) def start(self, *args, **kwargs): for vm in self.appvms.values(): if vm.is_running(): raise QubesException("Cannot start HVM template while VMs based on it are running") return super(QubesTemplateHVm, self).start(*args, **kwargs) def commit_changes (self, verbose = False): self.log.debug('commit_changes()') if not vmm.offline_mode: assert not self.is_running(), "Attempt to commit changes on running Template VM!" if verbose: print >> sys.stderr, "--> Commiting template updates... COW: {0}...".format (self.rootcow_img) if dry_run: return self.storage.commit_template_changes()
#!/usr/bin/python # -*- coding: utf-8 -*- """ Show network tree @author: unman """ from qubes.qubes import QubesVmCollection qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_reading() qvm_collection.load() qvm_collection.unlock_db() qvm_collection.pop(0) def tree(netvm, padding): names = {} padding = padding + ' ' connected = netvm.connected_vms for i in connected: names[i] = connected[i].name for name in sorted(names.values()): vm = qvm_collection.get_qid_by_name(name) if qvm_collection[vm].is_running(): vm_name = qvm_collection[vm].name + '* ' else: vm_name = qvm_collection[vm].name if qvm_collection[vm].is_template(): print padding, '|->', vm_name, '(Tpl)' else:
class TC_00_Dom0Upgrade(qubes.tests.QubesTestCase): cleanup_paths = [] pkg_name = 'qubes-test-pkg' dom0_update_common_opts = ['--disablerepo=*', '--enablerepo=test', '--setopt=test.copy_local=1'] @classmethod def generate_key(cls, keydir): gpg_opts = ['gpg', '--quiet', '--no-default-keyring', '--homedir', keydir] p = subprocess.Popen(gpg_opts + ['--gen-key', '--batch'], stdin=subprocess.PIPE, stderr=open(os.devnull, 'w')) p.stdin.write(''' Key-Type: RSA Key-Length: 1024 Key-Usage: sign Name-Real: Qubes test Expire-Date: 0 %commit '''.format(keydir=keydir)) p.stdin.close() p.wait() subprocess.check_call(gpg_opts + ['-a', '--export', '--output', os.path.join(keydir, 'pubkey.asc')]) p = subprocess.Popen(gpg_opts + ['--with-colons', '--list-keys'], stdout=subprocess.PIPE) for line in p.stdout.readlines(): fields = line.split(':') if fields[0] == 'pub': return fields[4][-8:].lower() raise RuntimeError @classmethod def setUpClass(cls): super(TC_00_Dom0Upgrade, cls).setUpClass() cls.tmpdir = tempfile.mkdtemp() cls.cleanup_paths += [cls.tmpdir] cls.keyid = cls.generate_key(cls.tmpdir) p = subprocess.Popen(['sudo', 'dd', 'status=none', 'of=/etc/yum.repos.d/test.repo'], stdin=subprocess.PIPE) p.stdin.write(''' [test] name = Test baseurl = file:///tmp/repo enabled = 1 ''') p.stdin.close() p.wait() @classmethod def tearDownClass(cls): subprocess.check_call(['sudo', 'rm', '-f', '/etc/yum.repos.d/test.repo']) for dir in cls.cleanup_paths: shutil.rmtree(dir) cls.cleanup_paths = [] def setUp(self): self.qc = QubesVmCollection() self.qc.lock_db_for_writing() self.qc.load() self.updatevm = self.qc.add_new_vm("QubesProxyVm", name="%supdatevm" % VM_PREFIX, template=self.qc.get_default_template()) self.updatevm.create_on_disk(verbose=False) self.saved_updatevm = self.qc.get_updatevm_vm() self.qc.set_updatevm_vm(self.updatevm) self.qc.save() self.qc.unlock_db() subprocess.call(['sudo', 'rpm', '-e', self.pkg_name], stderr=open(os.devnull, 'w')) subprocess.check_call(['sudo', 'rpm', '--import', os.path.join(self.tmpdir, 'pubkey.asc')]) self.updatevm.start() def remove_vms(self, vms): self.qc.lock_db_for_writing() self.qc.load() self.qc.set_updatevm_vm(self.qc[self.saved_updatevm.qid]) for vm in vms: if isinstance(vm, str): vm = self.qc.get_vm_by_name(vm) else: vm = self.qc[vm.qid] if vm.is_running(): try: vm.force_shutdown() except: pass try: vm.remove_from_disk() except OSError: pass self.qc.pop(vm.qid) self.qc.save() self.qc.unlock_db() def tearDown(self): vmlist = [vm for vm in self.qc.values() if vm.name.startswith( VM_PREFIX)] self.remove_vms(vmlist) subprocess.call(['sudo', 'rpm', '-e', self.pkg_name], stderr=open( os.devnull, 'w')) subprocess.call(['sudo', 'rpm', '-e', 'gpg-pubkey-{}'.format( self.keyid)], stderr=open(os.devnull, 'w')) for pkg in os.listdir(self.tmpdir): if pkg.endswith('.rpm'): os.unlink(pkg) def create_pkg(self, dir, name, version): spec_path = os.path.join(dir, name+'.spec') spec = open(spec_path, 'w') spec.write( ''' Name: {name} Summary: Test Package Version: {version} Release: 1 Vendor: Invisible Things Lab License: GPL Group: Qubes URL: http://www.qubes-os.org %description Test package %install %files '''.format(name=name, version=version) ) spec.close() subprocess.check_call( ['rpmbuild', '--quiet', '-bb', '--define', '_rpmdir {}'.format(dir), spec_path]) pkg_path = os.path.join(dir, 'x86_64', '{}-{}-1.x86_64.rpm'.format(name, version)) subprocess.check_call(['sudo', 'chmod', 'go-rw', '/dev/tty']) subprocess.check_call( ['rpm', '--quiet', '--define=_gpg_path {}'.format(dir), '--define=_gpg_name {}'.format("Qubes test"), '--addsign', pkg_path], stdin=open(os.devnull), stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) subprocess.check_call(['sudo', 'chmod', 'go+rw', '/dev/tty']) return pkg_path def send_pkg(self, filename): p = self.updatevm.run('mkdir -p /tmp/repo; cat > /tmp/repo/{}'.format( os.path.basename( filename)), passio_popen=True) p.stdin.write(open(filename).read()) p.stdin.close() p.wait() self.updatevm.run('cd /tmp/repo; createrepo .', wait=True) def test_000_update(self): filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') subprocess.check_call(['sudo', 'rpm', '-i', filename]) filename = self.create_pkg(self.tmpdir, self.pkg_name, '2.0') self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts, stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) except subprocess.CalledProcessError: self.fail("qubes-dom0-update failed: " + open( logpath).read()) retcode = subprocess.call(['rpm', '-q', '{}-1.0'.format( self.pkg_name)], stdout=open(os.devnull, 'w')) self.assertEqual(retcode, 1, 'Package {}-1.0 still installed after ' 'update'.format(self.pkg_name)) retcode = subprocess.call(['rpm', '-q', '{}-2.0'.format( self.pkg_name)], stdout=open(os.devnull, 'w')) self.assertEqual(retcode, 0, 'Package {}-2.0 not installed after ' 'update'.format(self.pkg_name)) def test_010_instal(self): filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts + [ self.pkg_name], stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) except subprocess.CalledProcessError: self.fail("qubes-dom0-update failed: " + open( logpath).read()) retcode = subprocess.call(['rpm', '-q', '{}-1.0'.format( self.pkg_name)], stdout=open('/dev/null', 'w')) self.assertEqual(retcode, 0, 'Package {}-1.0 not installed'.format( self.pkg_name)) def test_020_install_wrong_sign(self): subprocess.call(['sudo', 'rpm', '-e', 'gpg-pubkey-{}'.format( self.keyid)]) filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts + [ self.pkg_name], stdout=open(logpath, 'w'), stderr=subprocess.STDOUT) self.fail("qubes-dom0-update unexpectedly succeeded: " + open( logpath).read()) except subprocess.CalledProcessError: pass retcode = subprocess.call(['rpm', '-q', '{}-1.0'.format( self.pkg_name)], stdout=open('/dev/null', 'w')) self.assertEqual(retcode, 1, 'Package {}-1.0 installed although ' 'signature is invalid'.format(self.pkg_name)) def test_030_install_unsigned(self): filename = self.create_pkg(self.tmpdir, self.pkg_name, '1.0') subprocess.check_call(['rpm', '--delsign', filename], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) self.send_pkg(filename) logpath = os.path.join(self.tmpdir, 'dom0-update-output.txt') try: subprocess.check_call(['sudo', 'qubes-dom0-update', '-y'] + self.dom0_update_common_opts + [self.pkg_name], stdout=open(logpath, 'w'), stderr=subprocess.STDOUT ) self.fail("qubes-dom0-update unexpectedly succeeded: " + open( logpath).read()) except subprocess.CalledProcessError: pass retcode = subprocess.call(['rpm', '-q', '{}-1.0'.format( self.pkg_name)], stdout=open('/dev/null', 'w')) self.assertEqual(retcode, 1, 'UNSIGNED package {}-1.0 installed'.format(self.pkg_name))