From 8c82361f5efdffb12d7a3ee70d9d663f3186ff21 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:21:14 +0100 Subject: [PATCH 01/72] Implemented QubesFirewallVm subclass of QubesNetVm --- dom0/qvm-core/qubes.py | 107 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 5 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 55ab2d6b..1d5c3eb6 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -84,6 +84,7 @@ swap_cow_sz = 1024*1024*1024 VM_TEMPLATE = 'TempleteVM' VM_APPVM = 'AppVM' VM_NETVM = 'NetVM' +VM_FWVM = 'FirewallVM' VM_DISPOSABLEVM = 'DisposableVM' class XendSession(object): @@ -282,7 +283,13 @@ class QubesVm(object): return False def is_netvm(self): - if self.type == VM_NETVM: + if self.type == VM_NETVM or self.type == VM_FWVM: + return True + else: + return False + + def is_fwvm(self): + if self.type == VM_FWVM: return True else: return False @@ -559,9 +566,7 @@ class QubesVm(object): xm_cmdline = ["/usr/sbin/xm", "network-attach", self.name, "script=vif-route-qubes", "ip="+actual_ip] if self.netvm_vm.qid != 0: if not self.netvm_vm.is_running(): - print "ERROR: NetVM not running, please start it first" - self.force_shutdown() - raise QubesException ("NetVM not running") + self.netvm_vm.start() retcode = subprocess.call (xm_cmdline + ["backend={0}".format(self.netvm_vm.name)]) if retcode != 0: self.force_shutdown() @@ -891,7 +896,9 @@ class QubesNetVm(QubesServiceVm): if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label - super(QubesNetVm, self).__init__(type=VM_NETVM, installed_by_rpm=True, **kwargs) + if "type" not in kwargs or kwargs["type"] is None: + kwargs["type"] = VM_NETVM + super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) @property def gateway(self): @@ -929,6 +936,30 @@ class QubesNetVm(QubesServiceVm): ) return element +class QubesFirewallVm(QubesNetVm): + """ + A class that represents a FirewallVM. A child of QubesNetVM. + """ + def __init__(self, **kwargs): + self.netvm_vm = kwargs.pop("netvm_vm") if "netvm_vm" in kwargs else None + + super(QubesFirewallVm, self).__init__(type=VM_FWVM, **kwargs) + + def create_xml_element(self): + element = xml.etree.ElementTree.Element( + "QubesFirewallVm", + qid=str(self.qid), + netid=str(self.netid), + name=self.name, + dir_path=self.dir_path, + conf_file=self.conf_file, + root_img=self.root_img, + netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", + private_img=self.private_img, + installed_by_rpm=str(self.installed_by_rpm), + ) + return element + class QubesDom0NetVm(QubesNetVm): def __init__(self): super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0, @@ -1249,6 +1280,7 @@ class QubesVmCollection(dict): def __init__(self, store_filename=qubes_store_filename): super(QubesVmCollection, self).__init__() self.default_netvm_qid = None + self.default_fw_netvm_qid = None self.default_template_qid = None self.qubes_store_filename = store_filename @@ -1348,6 +1380,27 @@ class QubesVmCollection(dict): assert False, "Wrong VM description!" self[vm.qid]=vm + if self.default_fw_netvm_qid is None: + self.set_default_fw_netvm_vm(vm) + + return vm + + def add_new_fwvm(self, name, + dir_path = None, conf_file = None, + root_img = None): + + qid = self.get_new_unused_qid() + netid = self.get_new_unused_netid() + vm = QubesFirewallVm (qid=qid, name=name, + netid=netid, + dir_path=dir_path, conf_file=conf_file, + netvm_vm = self.get_default_fw_netvm_vm(), + root_img=root_img) + + if not self.verify_new_vm (vm): + assert False, "Wrong VM description!" + self[vm.qid]=vm + if self.default_netvm_qid is None: self.set_default_netvm_vm(vm) @@ -1373,6 +1426,16 @@ class QubesVmCollection(dict): else: return self[self.default_netvm_qid] + def set_default_fw_netvm_vm(self, vm): + assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name) + self.default_fw_netvm_qid = vm.qid + + def get_default_fw_netvm_vm(self): + if self.default_fw_netvm_qid is None: + return None + else: + return self[self.default_fw_netvm_qid] + def get_vm_by_name(self, name): for vm in self.values(): if (vm.name == name): @@ -1528,6 +1591,40 @@ class QubesVmCollection(dict): os.path.basename(sys.argv[0]), err)) return False + # Next read in the FirewallVMs, because they may be referenced + # by other VMs + for element in tree.findall("QubesFirewallVm"): + try: + kwargs = {} + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", + "private_img", "root_img", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + kwargs["netid"] = int(kwargs["netid"]) + + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + kwargs["netvm_vm"] = netvm_vm + + vm = QubesFirewallVm(**kwargs) + self[vm.qid] = vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesFirewallVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + self.default_template_qid # Then, read in the TemplateVMs, because a reference to templete VM From 4297c1284a688c74b4d139de786e1e1930edcf46 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:21:41 +0100 Subject: [PATCH 02/72] Show FirewallVMs in qvm-ls --- dom0/qvm-tools/qvm-ls | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 395a3426..ced8f585 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -39,14 +39,15 @@ fields = { + ('}' if vm.is_netvm() else '')"}, "type": {"func": "'Tpl' if vm.is_templete() else \ - (' Net' if vm.is_netvm() else '')"}, + (' Fw' if vm.is_fwvm() else \ + (' Net' if vm.is_netvm() else ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, "template": {"func": "'n/a' if vm.is_templete() or vm.is_netvm() else\ qvm_collection[vm.template_vm.qid].name"}, - "netvm": {"func": "'n/a' if vm.is_netvm() else\ + "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_fwvm() else\ ('*' if vm.uses_default_netvm else '') +\ qvm_collection[vm.netvm_vm.qid].name\ if vm.netvm_vm is not None else '-'"}, From 053ca36ca8d7036f1fe0ba8841edf37b94b5ece9 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:35:36 +0100 Subject: [PATCH 03/72] Refactored QubesVm.is_*vm() methods --- dom0/qvm-core/qubes.py | 68 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1d5c3eb6..b58307eb 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -81,12 +81,6 @@ qubes_appmenu_remove_cmd = "/usr/lib/qubes/remove_appvm_appmenus.sh" # as "should be good for everyone" swap_cow_sz = 1024*1024*1024 -VM_TEMPLATE = 'TempleteVM' -VM_APPVM = 'AppVM' -VM_NETVM = 'NetVM' -VM_FWVM = 'FirewallVM' -VM_DISPOSABLEVM = 'DisposableVM' - class XendSession(object): def __init__(self): self.get_xend_session_old_api() @@ -171,7 +165,7 @@ class QubesVm(object): Note that qid is not the same as Xen's domid! """ - def __init__(self, qid, name, type, + def __init__(self, qid, name, dir_path, conf_file = None, uses_default_netvm = True, netvm_vm = None, @@ -196,7 +190,6 @@ class QubesVm(object): else: self.conf_file = dir_path + "/" + conf_file - self.__type = type self.uses_default_netvm = uses_default_netvm self.netvm_vm = netvm_vm @@ -217,10 +210,6 @@ class QubesVm(object): def qid(self): return self.__qid - @property - def type(self): - return self.__type - @property def ip(self): if self.netvm_vm is not None: @@ -271,34 +260,19 @@ class QubesVm(object): self.updateable = False def is_templete(self): - if self.type == VM_TEMPLATE: - return True - else: - return False + return isinstance(self, QubesTemplateVm) def is_appvm(self): - if self.type == VM_APPVM: - return True - else: - return False + return isinstance(self, QubesAppVm) def is_netvm(self): - if self.type == VM_NETVM or self.type == VM_FWVM: - return True - else: - return False + return isinstance(self, QubesNetVm) def is_fwvm(self): - if self.type == VM_FWVM: - return True - else: - return False + return isinstance(self, QubesFirewallVm) def is_disposablevm(self): - if self.type == VM_DISPOSABLEVM: - return True - else: - return False + return isinstance(self, QubesDisposableVm) def add_to_xen_storage(self): if dry_run: @@ -625,7 +599,7 @@ class QubesTemplateVm(QubesVm): private_img = kwargs.pop("private_img") if "private_img" in kwargs else None appvms_conf_file = kwargs.pop("appvms_conf_file") if "appvms_conf_file" in kwargs else None - super(QubesTemplateVm, self).__init__(type=VM_TEMPLATE, label = default_template_label, **kwargs) + super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs) dir_path = kwargs["dir_path"] @@ -653,6 +627,10 @@ class QubesTemplateVm(QubesVm): self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir self.appvms = QubesVmCollection() + @property + def type(self): + return "TempleteVM" + def set_updateable(self): if self.is_updateable(): return @@ -896,10 +874,12 @@ class QubesNetVm(QubesServiceVm): if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label - if "type" not in kwargs or kwargs["type"] is None: - kwargs["type"] = VM_NETVM super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) + @property + def type(self): + return "NetVM" + @property def gateway(self): return self.__gateway @@ -943,7 +923,11 @@ class QubesFirewallVm(QubesNetVm): def __init__(self, **kwargs): self.netvm_vm = kwargs.pop("netvm_vm") if "netvm_vm" in kwargs else None - super(QubesFirewallVm, self).__init__(type=VM_FWVM, **kwargs) + super(QubesFirewallVm, self).__init__(**kwargs) + + @property + def type(self): + return "FirewallVM" def create_xml_element(self): element = xml.etree.ElementTree.Element( @@ -1041,7 +1025,7 @@ class QubesDisposableVm(QubesVm): template_vm = kwargs.pop("template_vm") - super(QubesDisposableVm, self).__init__(type=VM_DISPOSABLEVM, dir_path=None, **kwargs) + super(QubesDisposableVm, self).__init__(dir_path=None, **kwargs) qid = kwargs["qid"] assert template_vm is not None, "Missing template_vm for DisposableVM!" @@ -1053,6 +1037,10 @@ class QubesDisposableVm(QubesVm): self.template_vm = template_vm template_vm.appvms[qid] = self + @property + def type(self): + return "DisposableVM" + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesDisposableVm", @@ -1082,7 +1070,7 @@ class QubesAppVm(QubesVm): template_vm = kwargs.pop("template_vm") - super(QubesAppVm, self).__init__(type=VM_APPVM, **kwargs) + super(QubesAppVm, self).__init__(**kwargs) qid = kwargs["qid"] dir_path = kwargs["dir_path"] @@ -1106,6 +1094,10 @@ class QubesAppVm(QubesVm): self.swapcow_img = dir_path + "/" + default_swapcow_img + @property + def type(self): + return "AppVM" + def set_updateable(self): if self.is_updateable(): return From a088e142443c8f14511fe874ec97b7cd554b4ca4 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:45:09 +0100 Subject: [PATCH 04/72] Fixed setting netvm of FWVM --- dom0/qvm-core/qubes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index b58307eb..875fb0cf 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -921,9 +921,7 @@ class QubesFirewallVm(QubesNetVm): A class that represents a FirewallVM. A child of QubesNetVM. """ def __init__(self, **kwargs): - self.netvm_vm = kwargs.pop("netvm_vm") if "netvm_vm" in kwargs else None - - super(QubesFirewallVm, self).__init__(**kwargs) + super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) @property def type(self): From 8ca63ba1763ccb47d507bb43e961cdc09df36572 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 22:15:00 +0100 Subject: [PATCH 05/72] Start xend and xenstored during package installation --- rpm_spec/core-dom0.spec | 53 ++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 9256125b..cfe9ea75 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -29,15 +29,16 @@ Name: qubes-core-dom0 Version: %{version} -Release: 1 +Release: 2 Summary: The Qubes core files (Dom0-side) Group: Qubes Vendor: Invisible Things Lab License: GPL URL: http://www.qubes-os.org +BuildRequires: xen-devel Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0 -Conflicts: qubes-gui-dom0 < 1.1.13 +Conflicts: qubes-gui-dom0 < 1.1.13 Requires: NetworkManager >= 0.8.1-1 %define _builddir %(pwd)/dom0 @@ -124,10 +125,16 @@ mkdir -p $RPM_BUILD_ROOT/var/run/qubes %post +# Create NetworkManager configuration if we do not have it +if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then +echo '[main]' > /etc/NetworkManager/NetworkManager.conf +echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf +echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf +fi /usr/lib/qubes/qubes_fix_nm_conf.sh if [ -e /etc/yum.repos.d/qubes-r1-dom0.repo ]; then -# we want the user to use the repo that comes with qubes-code-dom0 packages instead +# we want the user to use the repo that comes with qubes-core-dom0 packages instead rm -f /etc/yum.repos.d/qubes-r1-dom0.repo fi @@ -136,15 +143,16 @@ fi #exit 0 #fi -# TODO: This is only temporary, until we will have our own installer -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - chkconfig $srv off -done +## TODO: This is only temporary, until we will have our own installer +#for f in /etc/init.d/* +#do +# srv=`basename $f` +# [ $srv = 'functions' ] && continue +# [ $srv = 'killall' ] && continue +# [ $srv = 'halt' ] && continue +# [ $srv = 'single' ] && continue +# chkconfig $srv off +#done chkconfig iptables on chkconfig NetworkManager on @@ -165,6 +173,20 @@ chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" chkconfig qubes_netvm on || echo "WARNING: Cannot enable service qubes_netvm!" chkconfig qubes_setupdvm on || echo "WARNING: Cannot enable service qubes_setupdvm!" +HAD_SYSCONFIG_NETWORK=yes +if ! [ -e /etc/sysconfig/network ]; then + HAD_SYSCONFIG_NETWORK=no + # supplant empty one so NetworkManager init script does not complain + touch /etc/sysconfig/network +fi + +# Load evtchn module - xenstored needs it +modprobe evtchn + +# Now launch xend - we will need it for subsequent steps +service xenstored start +service xend start + if ! [ -e /var/lib/qubes/qubes.xml ]; then # echo "Initializing Qubes DB..." umask 007; sg qubes -c qvm-init-storage @@ -173,13 +195,16 @@ for i in /usr/share/qubes/icons/*.png ; do xdg-icon-resource install --novendor --size 48 $i done -/etc/init.d/qubes_core start +service qubes_core start NETVM=$(qvm-get-default-netvm) if [ "X"$NETVM = "X""dom0" ] ; then - /etc/init.d/qubes_netvm start + service qubes_netvm start fi +if [ "x"$HAD_SYSCONFIG_NETWORK = "xno" ]; then + rm -f /etc/sysconfig/network +fi %clean rm -rf $RPM_BUILD_ROOT From a450e51126149bdd47c7cb46f0833b4588bf5175 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Mon, 21 Feb 2011 18:13:27 +0100 Subject: [PATCH 06/72] Implemented firewall_conf storage --- dom0/qvm-core/qubes.py | 44 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 875fb0cf..b2150836 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -65,6 +65,7 @@ default_appvms_conf_file = "appvm-template.conf" default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM cloning default_appmenus_templates_subdir = "apps.templates" default_kernels_subdir = "kernels" +default_firewall_conf_file = "firewall.xml" # do not allow to start a new AppVM if Dom0 mem was to be less than this dom0_min_memory = 700*1024*1024 @@ -529,7 +530,7 @@ class QubesVm(object): print "--> Setting Xen Store info for the VM..." self.create_xenstore_entries(xid) - if not self.is_netvm() and self.netvm_vm is not None: + if (not self.is_netvm() or self.is_fwvm()) and self.netvm_vm is not None: assert self.netvm_vm is not None if verbose: print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name) @@ -927,6 +928,20 @@ class QubesFirewallVm(QubesNetVm): def type(self): return "FirewallVM" + def create_xenstore_entries(self, xid): + if dry_run: + return + + super(QubesFirewallVm, self).create_xenstore_entries(xid) + self.write_iptables_xenstore_entry() + + def write_iptables_xenstore_entry(self): + iptables = "" + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_iptables".format(self.get_xid()), + iptables]) + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesFirewallVm", @@ -1091,6 +1106,11 @@ class QubesAppVm(QubesVm): self.rootcow_img = dir_path + "/" + default_rootcow_img self.swapcow_img = dir_path + "/" + default_swapcow_img + if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None: + kwargs["firewall_conf"] = dir_path + "/" + default_firewall_conf_file + + self.firewall_conf = kwargs["firewall_conf"] + @property def type(self): @@ -1163,6 +1183,26 @@ class QubesAppVm(QubesVm): def create_appmenus(self, verbose): subprocess.check_call ([qubes_appmenu_create_cmd, self.template_vm.appmenus_templates_dir, self.name]) + def write_firewall_conf(self, xml): + f = open(self.firewall_conf, 'a') # create the file if not exist + f.close() + with open(self.firewall_conf, 'w') as f: + fcntl.lockf(f, fcntl.LOCK_EX) + xml.write(f, "UTF-8") + fcntl.lockf(f, fcntl.LOCK_UN) + f.close() + + def get_firewall_conf(self): + try: + tree = xml.etree.ElementTree.parse(self.firewall_conf) + except (EnvironmentError, + xml.parsers.expat.ExpatError) as err: + print("{0}: load error: {1}".format( + os.path.basename(sys.argv[0]), err)) + return None + + return tree.getroot() + def get_disk_utilization_root_img(self): return 0 @@ -1524,7 +1564,7 @@ class QubesVmCollection(dict): self.qubes_store_file.truncate() tree.write(self.qubes_store_file, "UTF-8") except EnvironmentError as err: - print("{0}: import error: {1}".format( + print("{0}: export error: {1}".format( os.path.basename(sys.argv[0]), err)) return False return True From e104f82e3658d69f9aed7a6094299ef320a3c5d7 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 26 Feb 2011 03:42:55 +0100 Subject: [PATCH 07/72] Update TemplateVM with running AppVM: part 1 snapshot and origin device type for xen --- common/block-snapshot | 189 ++++++++++++++++++++++++++++++++++++++++ rpm_spec/core-dom0.spec | 4 + 2 files changed, 193 insertions(+) create mode 100644 common/block-snapshot diff --git a/common/block-snapshot b/common/block-snapshot new file mode 100644 index 00000000..a08a10c5 --- /dev/null +++ b/common/block-snapshot @@ -0,0 +1,189 @@ +#!/bin/bash + +# Usage: block-snapshot add|remove img-file cow-file +# +# This creates dm-snapshot device on given arguments + +dir=$(dirname "$0") +. "$dir/block-common.sh" + + +get_dev() { + dev=$1 + + if [ -L "$dev" ]; then + dev=$(readlink -f "$dev") || fatal "$dev link does not exist." + fi + + if [ -f "$dev" ]; then + file=$dev + + inode=$(stat -c '%i' "$file") + devnum=$(stat -c '%D' "$file") + if [ -z "$inode" ] || [ -z "$devnum" ] + then + release_lock "block" + fatal "Unable to lookup $file: dev: $devnum inode: $inode" + fi + + dev_list=$(losetup -a | grep ' \[0*'${dev}'\]:'${inode} | cut -d : -f 1) + for dev in $dev_list; do + # found existing loop to this file + echo $dev + return + done + + + # assign new loop device + loopdev=$(losetup -f 2>/dev/null || find_free_loopback_dev) + if [ "$loopdev" = '' ] + then + release_lock "block" + fatal 'Failed to find an unused loop device' + fi + + do_or_die losetup "$loopdev" "$file" + echo $loopdev + else + test -e "$dev" || fatal "$dev does not exist." + test -b "$dev" || fatal "$dev is not a block device nor file." + fi +} + +get_dm_snapshot_name() { + base=$1 + cow=$2 + + echo snapshot-$(stat -c '%D:%i' "$base")-$(stat -c '%D:%i' "$cow") +} + +create_dm_snapshot() { + local base_dev cow_dev base_sz + + dm_devname=$1 + base=$2 + cow=$3 + + if [ ! -e /dev/mapper/$dm_devname ]; then + # prepare new snapshot device + base_dev=$(get_dev $base) + cow_dev=$(get_dev $cow) + base_sz=$(blockdev --getsz $base_dev) + do_or_die dmsetup create $dm_devname --table "0 $base_sz snapshot $base_dev $cow_dev P 256" + fi + +} + +create_dm_snapshot_origin() { + local base_dev base_sz + + dm_devname=$1 + base=$2 + + if [ ! -e /dev/mapper/$dm_devname ]; then + # prepare new snapshot-origin device + base_dev=$(get_dev $base) + base_sz=$(blockdev --getsz $base_dev) + do_or_die dmsetup create $dm_devname --table "0 $base_sz snapshot-origin $base_dev" + fi +} + +t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING') + + +case "$command" in + add) + case $t in + snapshot|origin) + p=$(xenstore_read "$XENBUS_PATH/params") + base=${p/:*/} + cow=${p/*:/} + + if [ -L "$base" ]; then + base=$(readlink -f "$base") || fatal "$base link does not exist." + fi + + if [ -L "$cow" ]; then + cow=$(readlink -f "$cow") || fatal "$cow link does not exist." + fi + + # first ensure that snapshot device exists (to write somewhere changes from snapshot-origin) + dm_devname=$(get_dm_snapshot_name "$base" "$cow") + + claim_lock "block" + + # prepare snapshot device + create_dm_snapshot $dm_devname "$base" "$cow" + + if [ "$t" == "snapshot" ]; then + #that's all for snapshot, store name of prepared device + xenstore_write "$XENBUS_PATH/node" "/dev/mapper/$dm_devname" + write_dev /dev/mapper/$dm_devname + elif [ "$t" == "origin" ]; then + # for origin - prepare snapshot-origin device and store its name + dm_devname=origin-$(stat -c '%D:%i' "$base") + create_dm_snapshot_origin $dm_devname "$base" + xenstore_write "$XENBUS_PATH/node" "/dev/mapper/$dm_devname" + write_dev /dev/mapper/$dm_devname + fi + + release_lock "block" + exit 0 + ;; + esac + ;; + remove) + case $t in + snapshot|origin) + node=$(xenstore_read "$XENBUS_PATH/node") + + if [ -z "$node" ]; then + fatal "No device node to remove" + fi + + claim_lock "block" + + use_count=$(dmsetup info $node|grep Open|awk '{print $3}') + + # do not remove snapshot if snapshot origin is still present + if [ "${node/snapshot/}" != "$node" -a + -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then + ((use_count++)) + fi + + if [ "$use_count" -gt 0 ]; then + log info "Device $node still in use - not removing" + exit 0 + fi + + # get list of used (loop) devices + deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" + + # remove unused snapshots + for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2); do + use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') + if [ $use_count -eq 0 ]; then + # unused snapshot - remove it + deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" + dmsetup remove $snap + fi + done + + do_or_die dmsetup remove $node + + # try to free loop devices + for dev in $deps; do + if [ -b "$dev" ]; then + losetup -d $dev 2> /dev/null + fi + done + + release_lock "block" + + exit 0 + ;; + esac + ;; +esac + +# vim:sw=2:et: diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 9256125b..a4e57d86 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -65,6 +65,8 @@ cp pendrive_swapper/qfilexchgd $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp restore/block.qubes $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts +cp ../common/block-snapshot $RPM_BUILD_ROOT/etc/xen/scripts +ln -s block-snapshot $RPM_BUILD_ROOT/etc/xen/scripts/block-origin mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes cp qvm-core/qubes.py $RPM_BUILD_ROOT%{python_sitearch}/qubes @@ -275,6 +277,8 @@ fi /usr/lib/qubes/qubes_restore /usr/lib/qubes/qubes_prepare_saved_domain.sh /etc/xen/scripts/block.qubes +/etc/xen/scripts/block-snapshot +/etc/xen/scripts/block-origin /etc/xen/scripts/vif-route-qubes %attr(4750,root,qubes) /usr/lib/qubes/xenfreepages %attr(2770,root,qubes) %dir /var/log/qubes From 6db640dbfee52049cbe827f98da31b983092dee5 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sun, 27 Feb 2011 00:06:46 +0100 Subject: [PATCH 08/72] Update TemplateVM with running AppVM: part 2 - support for template modify in qvm-core - tool for commit changes to template --- dom0/qvm-core/qubes.py | 41 ++++++++++++++---- dom0/qvm-tools/qvm-prefs | 2 + dom0/qvm-tools/qvm-template-commit | 67 ++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 9 deletions(-) create mode 100755 dom0/qvm-tools/qvm-template-commit diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 55ab2d6b..cc8bfd7f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -624,13 +624,14 @@ class QubesTemplateVm(QubesVm): dir_path = kwargs["dir_path"] - # TempleteVM doesn't use root-cow image if root_img is not None and os.path.isabs(root_img): self.root_img = root_img else: self.root_img = dir_path + "/" + ( root_img if root_img is not None else default_root_img) + self.rootcow_img = dir_path + "/" + default_rootcow_img + if private_img is not None and os.path.isabs(private_img): self.private_img = private_img else: @@ -711,6 +712,14 @@ class QubesTemplateVm(QubesVm): format(src_template_vm.root_img, self.root_img) # We prefer to use Linux's cp, because it nicely handles sparse files retcode = subprocess.call (["cp", src_template_vm.root_img, self.root_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(src_template_vm.root_img, self.root_img)) + if verbose: + print "--> Copying the template's root COW image:\n{0} ==>\n{1}".\ + format(src_template_vm.rootcow_img, self.rootcow_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", src_template_vm.rootcow_img, self.rootcow_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ format(src_template_vm.root_img, self.root_img)) @@ -779,13 +788,29 @@ class QubesTemplateVm(QubesVm): if not self.is_updateable(): raise QubesException ("Cannot start Template VM that is marked \"nonupdatable\"") - # First ensure that none of our appvms is running: - for appvm in self.appvms.values(): - if appvm.is_running(): - raise QubesException ("Cannot start TemplateVM when one of its AppVMs is running!") + # TODO?: check if none of running appvms are outdated return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose) + def commit_changes (self): + + assert not self.is_running(), "Attempt to commit changes on running Template VM!" + + print "--> Commiting template updates... COW: {0}...".format (self.rootcow_img) + + if dry_run: + return + if os.path.exists (self.rootcow_img): + os.remove (self.rootcow_img) + + + f_cow = open (self.rootcow_img, "w") + f_root = open (self.root_img, "r") + f_root.seek(0, os.SEEK_END) + f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img + f_cow.close () + f_root.close() + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesTemplateVm", @@ -795,6 +820,7 @@ class QubesTemplateVm(QubesVm): conf_file=self.conf_file, appvms_conf_file=self.appvms_conf_file, root_img=self.root_img, + rootcow_img=self.rootcow_img, private_img=self.private_img, uses_default_netvm=str(self.uses_default_netvm), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", @@ -1194,10 +1220,6 @@ class QubesAppVm(QubesVm): if self.is_running(): raise QubesException("VM is already running!") - # First ensure that our template is *not* running: - if self.template_vm.is_running(): - raise QubesException ("Cannot start AppVM when its template is running!") - if not self.is_updateable(): self.reset_cow_storage() self.reset_swap_cow_storage() @@ -1728,3 +1750,4 @@ class QubesDaemonPidfile(object): self.remove_pidfile() +# vim:sw=4:et: diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index f2c77367..f9c129ff 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -42,6 +42,8 @@ def do_list(vm): print fmt.format ("config", vm.conf_file) if not vm.is_appvm(): print fmt.format ("root img", vm.root_img) + if vm.is_templete(): + print fmt.format ("root COW img", vm.rootcow_img) if vm.is_appvm(): print fmt.format ("root img", vm.template_vm.root_img) print fmt.format ("root COW img", vm.rootcow_img) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit new file mode 100755 index 00000000..d7d77eac --- /dev/null +++ b/dom0/qvm-tools/qvm-template-commit @@ -0,0 +1,67 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Joanna Rutkowska +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +from qubes.qubes import QubesVmCollection +from qubes.qubes import QubesException +from optparse import OptionParser +import subprocess + +qubes_guid_path = "/usr/bin/qubes_guid" + +def main(): + usage = "usage: %prog [options] " + parser = OptionParser (usage) + + (options, args) = parser.parse_args () + if (len (args) != 1): + parser.error ("You must specify VM name!") + vmname = args[0] + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vm = qvm_collection.get_vm_by_name(vmname) + if vm is None: + print "A VM with the name '{0}' does not exist in the system.".format(vmname) + exit(1) + + if not vm.is_templatevm(): + print "A VM '{0}' is not template.".format(vmname) + exit(1) + + if vm.is_running(): + print "You must stop VM first." + exit(1) + + try: + vm.verify_files() + vm.commit_changes() + except (IOError, OSError, QubesException) as err: + print "ERROR: {0}".format(err) + exit (1) + + exit (0) + + +main() From 2fa145ab362da9632078d60113f79262bd4de901 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Mon, 28 Feb 2011 18:52:55 +0100 Subject: [PATCH 09/72] block-snapshot fixes --- common/block-snapshot | 51 ++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 20 deletions(-) mode change 100644 => 100755 common/block-snapshot diff --git a/common/block-snapshot b/common/block-snapshot old mode 100644 new mode 100755 index a08a10c5..a668935c --- a/common/block-snapshot +++ b/common/block-snapshot @@ -7,7 +7,6 @@ dir=$(dirname "$0") . "$dir/block-common.sh" - get_dev() { dev=$1 @@ -26,10 +25,10 @@ get_dev() { fatal "Unable to lookup $file: dev: $devnum inode: $inode" fi - dev_list=$(losetup -a | grep ' \[0*'${dev}'\]:'${inode} | cut -d : -f 1) - for dev in $dev_list; do + dev_list=$(losetup -a | grep ' \[0*'${devnum}'\]:'${inode} | cut -d : -f 1) + for loopdev in $dev_list; do # found existing loop to this file - echo $dev + echo $loopdev return done @@ -141,40 +140,52 @@ case "$command" in fatal "No device node to remove" fi + if [ ! -e "$node" ]; then + fatal "Device $node does not exists" + fi + claim_lock "block" use_count=$(dmsetup info $node|grep Open|awk '{print $3}') # do not remove snapshot if snapshot origin is still present - if [ "${node/snapshot/}" != "$node" -a - -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then - ((use_count++)) + if [ "${node/snapshot/}" != "$node" -a -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then + use_count=1 fi if [ "$use_count" -gt 0 ]; then log info "Device $node still in use - not removing" + release_lock "block" exit 0 fi # get list of used (loop) devices - deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" + deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" - # remove unused snapshots - for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2); do - use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') - if [ $use_count -eq 0 ]; then - # unused snapshot - remove it - deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" - dmsetup remove $snap - fi - done - - do_or_die dmsetup remove $node + # if this is origin + if [ "${node/origin/}" != "$node" ]; then + # remove unused snapshots + for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2)-*; do + use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') + if [ "$use_count" -eq 0 ]; then + # unused snapshot - remove it + deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" + log debug "Removing $snap" + dmsetup remove $snap + fi + done + fi + + if [ -e $node ]; then + log debug "Removing $node" + dmsetup remove $node + fi # try to free loop devices for dev in $deps; do if [ -b "$dev" ]; then - losetup -d $dev 2> /dev/null + log debug "Removing $dev" + losetup -d $dev || true 2> /dev/null fi done From 5d4a27a0bba04ac36d65abee3ec707d62bde8e80 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:35:36 +0100 Subject: [PATCH 10/72] Add backup files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4d22dcb6..228b7abe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ rpm/ *.pyc +*~ From 143f1519a8dbfa93b5a6c83565aff0fbb22f3bf2 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:52:19 +0100 Subject: [PATCH 11/72] Add typo in qvm-template-commit As in original classes... --- dom0/qvm-tools/qvm-template-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit index d7d77eac..438fbb6a 100755 --- a/dom0/qvm-tools/qvm-template-commit +++ b/dom0/qvm-tools/qvm-template-commit @@ -46,7 +46,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if not vm.is_templatevm(): + if not vm.is_templete(): print "A VM '{0}' is not template.".format(vmname) exit(1) From cf39d17fa03efb787e1b7f9e5fec80ac0632e065 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:55:54 +0100 Subject: [PATCH 12/72] Add BR to core-appvm.spec --- rpm_spec/core-appvm.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index c9a009d1..e901a12c 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -34,6 +34,8 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: /usr/bin/mimeopen +BuildRequires: gcc +BuildRequires: xen-devel Provides: qubes-core-vm %define _builddir %(pwd)/appvm From c3bf11062fcada300947bc2da4a640374a7a8e99 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:58:22 +0100 Subject: [PATCH 13/72] gitignore files - add build products --- .gitignore | 2 ++ appvm/.gitignore | 3 +++ common/.gitignore | 1 + dom0/restore/.gitignore | 3 +++ 4 files changed, 9 insertions(+) create mode 100644 appvm/.gitignore create mode 100644 common/.gitignore create mode 100644 dom0/restore/.gitignore diff --git a/.gitignore b/.gitignore index 228b7abe..e2440642 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ rpm/ *.pyc +*.pyo *~ +*.o diff --git a/appvm/.gitignore b/appvm/.gitignore new file mode 100644 index 00000000..edd6d099 --- /dev/null +++ b/appvm/.gitignore @@ -0,0 +1,3 @@ +qubes_add_pendrive_script +qubes_penctl +qvm-open-in-dvm diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 00000000..03034a95 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1 @@ +meminfo-writer diff --git a/dom0/restore/.gitignore b/dom0/restore/.gitignore new file mode 100644 index 00000000..0535ab8a --- /dev/null +++ b/dom0/restore/.gitignore @@ -0,0 +1,3 @@ +qubes_restore +xenfreepages +xenstore-watch From d758eb8258eb626468eec89091d09c653fc2dab1 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:00:19 +0100 Subject: [PATCH 14/72] Removed trailing whitespace --- dom0/qvm-core/qubes.py | 72 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index b2150836..489b8d27 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -48,9 +48,9 @@ qubes_guid_path = "/usr/bin/qubes_guid" qubes_base_dir = "/var/lib/qubes" -qubes_appvms_dir = qubes_base_dir + "/appvms" -qubes_templates_dir = qubes_base_dir + "/vm-templates" -qubes_servicevms_dir = qubes_base_dir + "/servicevms" +qubes_appvms_dir = qubes_base_dir + "/appvms" +qubes_templates_dir = qubes_base_dir + "/vm-templates" +qubes_servicevms_dir = qubes_base_dir + "/servicevms" qubes_store_filename = qubes_base_dir + "/qubes.xml" qubes_max_qid = 254*254 @@ -105,7 +105,7 @@ class XendSession(object): if not dry_run: xend_session = XendSession() - + class QubesException (Exception) : pass @@ -203,7 +203,7 @@ class QubesVm(object): self.icon_path = self.dir_path + "/icon.png" else: self.icon_path = None - + if not dry_run and xend_session.session is not None: self.refresh_xend_session() @@ -245,7 +245,7 @@ class QubesVm(object): def is_networked(self): if self.is_netvm(): return True - + if self.netvm_vm is not None: return True else: @@ -376,7 +376,7 @@ class QubesVm(object): for cpu in cpus_util: cpu_total_load += cpus_util[cpu] cpu_total_load /= len(cpus_util) - p = 100*cpu_total_load + p = 100*cpu_total_load if p > 100: p = 100 return p @@ -426,7 +426,7 @@ class QubesVm(object): def get_private_img_sz(self): if not os.path.exists(self.private_img): return 0 - + return os.path.getsize(self.private_img) def create_xenstore_entries(self, xid): @@ -506,8 +506,8 @@ class QubesVm(object): if verbose: print "--> Loading the VM (type = {0})...".format(self.type) - if not self.is_netvm(): - total_mem_mb = self.get_total_xen_memory()/1024/1024 + if not self.is_netvm(): + total_mem_mb = self.get_total_xen_memory()/1024/1024 xend_session.xend_server.xend.domain.maxmem_set(self.name, total_mem_mb) mem_required = self.get_mem_dynamic_max() @@ -523,7 +523,7 @@ class QubesVm(object): xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused qmemman_client.close() # let qmemman_daemon resume balancing - + xid = int (xend_session.session.xenapi.VM.get_domid (self.session_uuid)) if verbose: @@ -707,15 +707,15 @@ class QubesTemplateVm(QubesVm): print "--> Copying the template's appvm templates dir:\n{0} ==>\n{1}".\ format(src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) - - + + def get_disk_utilization_root_img(self): return self.get_disk_usage(self.root_img) def get_root_img_sz(self): if not os.path.exists(self.root_img): return 0 - + return os.path.getsize(self.root_img) def verify_files(self): @@ -752,7 +752,7 @@ class QubesTemplateVm(QubesVm): raise QubesException ( "VM's kernels directory does not exist: {0}".\ format(self.kernels_dir)) - + return True def start(self, debug_console = False, verbose = False): @@ -824,7 +824,7 @@ class QubesServiceVm(QubesVm): # In fact there is no point in making it unpdatebale... # So, this is just for completncess assert not self.is_running() - self.updateable = True + self.updateable = True def get_disk_utilization_root_img(self): return self.get_disk_usage(self.root_img) @@ -832,7 +832,7 @@ class QubesServiceVm(QubesVm): def get_root_img_sz(self): if not os.path.exists(self.root_img): return 0 - + return os.path.getsize(self.root_img) def verify_files(self): @@ -899,7 +899,7 @@ class QubesNetVm(QubesServiceVm): def get_ip_for_vm(self, qid): hi = qid / 253 - lo = qid % 253 + 2 + lo = qid % 253 + 2 assert hi >= 0 and hi <= 254 and lo >= 2 and lo <= 254, "Wrong IP address for VM" return self.netprefix + "{0}.{1}".format(hi,lo) @@ -980,7 +980,7 @@ class QubesDom0NetVm(QubesNetVm): for cpu in self.session_cpus: cpu_total_load += xend_session.session.xenapi.host_cpu.get_utilisation(cpu) cpu_total_load /= len(self.session_cpus) - p = 100*cpu_total_load + p = 100*cpu_total_load if p > 100: p = 100 return p @@ -1160,14 +1160,14 @@ class QubesAppVm(QubesVm): if verbose: print "--> Creating the VM config file: {0}".format(self.conf_file) - + self.create_config_file() template_priv = self.template_vm.private_img if verbose: print "--> Copying the template's private image: {0}".\ format(template_priv) - + # We prefer to use Linux's cp, because it nicely handles sparse files retcode = subprocess.call (["cp", template_priv, self.private_img]) if retcode != 0: @@ -1371,7 +1371,7 @@ class QubesVmCollection(dict): installed_by_rpm = True): qid = self.get_new_unused_qid() - vm = QubesTemplateVm (qid=qid, name=name, + vm = QubesTemplateVm (qid=qid, name=name, dir_path=dir_path, conf_file=conf_file, root_img=root_img, private_img=private_img, installed_by_rpm=installed_by_rpm, @@ -1401,7 +1401,7 @@ class QubesVmCollection(dict): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesNetVm (qid=qid, name=name, + vm = QubesNetVm (qid=qid, name=name, netid=netid, dir_path=dir_path, conf_file=conf_file, root_img=root_img) @@ -1421,7 +1421,7 @@ class QubesVmCollection(dict): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesFirewallVm (qid=qid, name=name, + vm = QubesFirewallVm (qid=qid, name=name, netid=netid, dir_path=dir_path, conf_file=conf_file, netvm_vm = self.get_default_fw_netvm_vm(), @@ -1480,7 +1480,7 @@ class QubesVmCollection(dict): vms = set([vm for vm in self.values() if (vm.is_appvm() and vm.template_vm.qid == template_qid)]) return vms - + def verify_new_vm(self, new_vm): # Verify that qid is unique @@ -1557,7 +1557,7 @@ class QubesVmCollection(dict): tree = xml.etree.ElementTree.ElementTree(root) try: - + # We need to manually truncate the file, as we open the # file as "r+" in the lock_db_for_writing() function self.qubes_store_file.seek (0, os.SEEK_SET) @@ -1588,11 +1588,11 @@ class QubesVmCollection(dict): return False element = tree.getroot() - default_template = element.get("default_template") + default_template = element.get("default_template") self.default_template_qid = int(default_template) \ if default_template != "None" else None - default_netvm = element.get("default_netvm") + default_netvm = element.get("default_netvm") if default_netvm is not None: self.default_netvm_qid = int(default_netvm) \ if default_netvm != "None" else None @@ -1615,7 +1615,7 @@ class QubesVmCollection(dict): vm = QubesNetVm(**kwargs) self[vm.qid] = vm - + except (ValueError, LookupError) as err: print("{0}: import error (QubesNetVM) {1}".format( os.path.basename(sys.argv[0]), err)) @@ -1646,10 +1646,10 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + vm = QubesFirewallVm(**kwargs) self[vm.qid] = vm - + except (ValueError, LookupError) as err: print("{0}: import error (QubesFirewallVM) {1}".format( os.path.basename(sys.argv[0]), err)) @@ -1658,7 +1658,7 @@ class QubesVmCollection(dict): self.default_template_qid # Then, read in the TemplateVMs, because a reference to templete VM - # is needed to create each AppVM + # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): try: @@ -1695,7 +1695,7 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + vm = QubesTemplateVm(**kwargs) self[vm.qid] = vm @@ -1747,7 +1747,7 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + if kwargs["label"] is not None: if kwargs["label"] not in QubesVmLabels: print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) @@ -1783,8 +1783,8 @@ class QubesVmCollection(dict): format(kwargs["name"], kwargs["template_qid"]) kwargs["template_vm"] = template_vm - kwargs["netvm_vm"] = self.get_default_netvm_vm() - + kwargs["netvm_vm"] = self.get_default_netvm_vm() + if kwargs["label"] is not None: if kwargs["label"] not in QubesVmLabels: print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) From 353f04e186d3130411f383cc5a29fb961c577c96 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:01:30 +0100 Subject: [PATCH 15/72] Properly set FwVM xenstore files --- dom0/qvm-core/qubes.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 489b8d27..80c25bb6 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -460,8 +460,8 @@ class QubesVm(object): "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netvm_network".format(xid), self.network]) - - elif self.netvm_vm is not None: + + if self.netvm_vm is not None: retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_ip".format(xid), @@ -470,19 +470,17 @@ class QubesVm(object): retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netmask".format(xid), - self.netmask]) + self.netvm_vm.netmask]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_gateway".format(xid), - self.gateway]) + self.netvm_vm.gateway]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_secondary_dns".format(xid), - self.secondary_dns]) - else: - pass + self.netvm_vm.secondary_dns]) def get_total_xen_memory(self): From 6083384e6d68f0b5ea08fb9bb39362596df457cb Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:02:46 +0100 Subject: [PATCH 16/72] Store firewal rules in Python data structure --- dom0/qvm-core/qubes.py | 74 +++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 80c25bb6..157b8cb8 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1181,25 +1181,77 @@ class QubesAppVm(QubesVm): def create_appmenus(self, verbose): subprocess.check_call ([qubes_appmenu_create_cmd, self.template_vm.appmenus_templates_dir, self.name]) - def write_firewall_conf(self, xml): - f = open(self.firewall_conf, 'a') # create the file if not exist - f.close() - with open(self.firewall_conf, 'w') as f: - fcntl.lockf(f, fcntl.LOCK_EX) - xml.write(f, "UTF-8") - fcntl.lockf(f, fcntl.LOCK_UN) - f.close() + def write_firewall_conf(self, conf): + root = xml.etree.ElementTree.Element( + "QubesFirwallRules", + policy="allow" if conf["allow"] else "deny" + ) + + for rule in conf["rules"]: + element = xml.etree.ElementTree.Element( + "allow" if rule["allow"] else "deny", + name=rule["name"], + address=rule["address"], + netmask=str(rule["netmask"]), + port=str(rule["portBegin"]), + ) + if rule["portEnd"] is not None: + element.set("toport", str(rule["portEnd"])) + root.append(element) + + tree = xml.etree.ElementTree.ElementTree(root) + + try: + f = open(self.firewall_conf, 'a') # create the file if not exist + f.close() + + with open(self.firewall_conf, 'w') as f: + fcntl.lockf(f, fcntl.LOCK_EX) + tree.write(f, "UTF-8") + fcntl.lockf(f, fcntl.LOCK_UN) + f.close() + except EnvironmentError as err: + print "{0}: save error: {1}".format( + os.path.basename(sys.argv[0]), err) + return False + + return True def get_firewall_conf(self): + conf = { "allow": True, "rules": list() } + try: tree = xml.etree.ElementTree.parse(self.firewall_conf) - except (EnvironmentError, - xml.parsers.expat.ExpatError) as err: + root = tree.getroot() + + for element in root: + rule = { "allow": element.tag=="allow" } + + attr_list = ("name", "address", "netmask", "port", "toport") + + for attribute in attr_list: + rule[attribute] = element.get(attribute) + + rule["netmask"] = int(rule["netmask"]) + rule["portBegin"] = int(rule["port"]) + if rule["toport"] is not None: + rule["portEnd"] = int(rule["toport"]) + else: + rule["portEnd"] = None + del(rule["port"]) + del(rule["toport"]) + + conf["rules"].append(rule) + + except (EnvironmentError) as err: + return conf + except (xml.parsers.expat.ExpatError, + ValueError, LookupError) as err: print("{0}: load error: {1}".format( os.path.basename(sys.argv[0]), err)) return None - return tree.getroot() + return conf def get_disk_utilization_root_img(self): return 0 From 45f84b171371ab32a0a7c96025339d8e4e35f880 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:03:21 +0100 Subject: [PATCH 17/72] Implemented iptables rules file generator --- dom0/qvm-core/qubes.py | 82 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 157b8cb8..cad42f47 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -29,6 +29,7 @@ import xml.parsers.expat import fcntl import re import shutil +from datetime import datetime from qmemman_client import QMemmanClient # Do not use XenAPI or create/read any VM files @@ -934,8 +935,85 @@ class QubesFirewallVm(QubesNetVm): self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): - iptables = "" - retcode = subprocess.check_call ([ + iptables = "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime()) + iptables += "*filter\n" + iptables += ":INPUT DROP [0:0]\n" + iptables += ":FORWARD DROP [0:0]\n" + iptables += ":OUTPUT ACCEPT [0:0]\n" + + # Strict INPUT rules + iptables += "-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n" + iptables += "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n" + iptables += "-A INPUT -p icmp -j ACCEPT\n" + iptables += "-A INPUT -i lo -j ACCEPT\n" + iptables += "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n" + + # Allow dom0 networking + iptables += "-A FORWARD -i vif0.0 -j ACCEPT\n" + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vms = [vm for vm in qvm_collection.values() if vm.is_appvm()] + for vm in vms: + conf = vm.get_firewall_conf() + + xid = vm.get_xid() + if xid < 0: # VM not active ATM + continue + + iptables += "# '{0}' VM:\n".format(vm.name) + + if conf["allow"]: + iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid) + + allow_rules = 0 + vm_iptables = "" + + for rule in conf["rules"]: + if rule["allow"]: + allow_rules += 1 + + vm_iptables += "# .. {0}:\n".format(rule["name"]) + + vm_iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"]) + if rule["netmask"] != 32: + vm_iptables += "/{0}".format(rule["netmask"]) + + if rule["portBegin"] > 0: + vm_iptables += " -p tcp --dport {0}".format(rule["portBegin"]) + if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: + vm_iptables += ":{0}".format(rule["portEnd"]) + + vm_iptables += " -j {0}\n".format("ACCEPT" if rule["allow"]\ + else "REJECT --reject-with icmp-host-prohibited", + ) + + iptables += vm_iptables + + if allow_rules > 0: + iptables += "# .. Needs DNS access\n" + iptables += "-A FORWARD -i vif{0}.0 -p udp --dport 53 -j ACCEPT\n".format(xid) + iptables += "# .. Allow ICMP to test network connectivity\n" + iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) + iptables += "# .. Deny everything not allowed before\n" + iptables += "-A FORWARD -i vif{0}.0 -j DROP\n".format(xid) + else: + iptables += "# .. Allow everything not denied before\n" + iptables += "-A FORWARD -i vif{0}.0 -j ACCEPT\n".format(xid) + + else: + iptables += "-A FORWARD -i vif{0}.0 -j DROP\n".format(xid) + + iptables += "#End of VM rules\n" + iptables += "-A FORWARD -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT\n" + iptables += "-A FORWARD -j DROP\n" + + iptables += "COMMIT" + + return subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_iptables".format(self.get_xid()), iptables]) From 0a8249d83fe922b1c61ad4588d7891721c30d6cd Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:04:11 +0100 Subject: [PATCH 18/72] Update firewall iptables file during VM start --- dom0/qvm-core/qubes.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index cad42f47..b82df6c8 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -552,9 +552,15 @@ class QubesVm(object): self.force_shutdown() raise OSError ("ERROR: Cannot attach to network backend!") + #if verbose: + # print "--> Updating FirewallVMs rules..." + #for vm in qvm_collection.values(): + # if vm.is_fwvm(): + # vm.write_iptables_xenstore_entry() + if verbose: print "--> Starting the VM..." - xend_session.session.xenapi.VM.unpause (self.session_uuid) + xend_session.session.xenapi.VM.unpause (self.session_uuid) # perhaps we should move it before unpause and fork? if debug_console: From a8cef51b67c4efbfca27c147165f76b3dfad591f Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Thu, 3 Mar 2011 22:40:36 +0100 Subject: [PATCH 19/72] Use new, simplified firewall rules data scheme --- dom0/qvm-core/qubes.py | 94 +++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index b82df6c8..adde77ec 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -971,47 +971,36 @@ class QubesFirewallVm(QubesNetVm): continue iptables += "# '{0}' VM:\n".format(vm.name) + iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid) + + + accept_action = "ACCEPT" + reject_action = "REJECT --reject-with icmp-host-prohibited" if conf["allow"]: - iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid) - - allow_rules = 0 - vm_iptables = "" - - for rule in conf["rules"]: - if rule["allow"]: - allow_rules += 1 - - vm_iptables += "# .. {0}:\n".format(rule["name"]) - - vm_iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"]) - if rule["netmask"] != 32: - vm_iptables += "/{0}".format(rule["netmask"]) - - if rule["portBegin"] > 0: - vm_iptables += " -p tcp --dport {0}".format(rule["portBegin"]) - if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: - vm_iptables += ":{0}".format(rule["portEnd"]) - - vm_iptables += " -j {0}\n".format("ACCEPT" if rule["allow"]\ - else "REJECT --reject-with icmp-host-prohibited", - ) - - iptables += vm_iptables - - if allow_rules > 0: - iptables += "# .. Needs DNS access\n" - iptables += "-A FORWARD -i vif{0}.0 -p udp --dport 53 -j ACCEPT\n".format(xid) - iptables += "# .. Allow ICMP to test network connectivity\n" - iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) - iptables += "# .. Deny everything not allowed before\n" - iptables += "-A FORWARD -i vif{0}.0 -j DROP\n".format(xid) - else: - iptables += "# .. Allow everything not denied before\n" - iptables += "-A FORWARD -i vif{0}.0 -j ACCEPT\n".format(xid) - + rules_action = accept_action + default_action = reject_action + iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) else: - iptables += "-A FORWARD -i vif{0}.0 -j DROP\n".format(xid) + rules_action = reject_action + default_action = accept_action + + for rule in conf["rules"]: + iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"]) + if rule["netmask"] != 32: + iptables += "/{0}".format(rule["netmask"]) + + if rule["portBegin"] is not None and rule["portBegin"] > 0: + iptables += " -p tcp --dport {0}".format(rule["portBegin"]) + if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: + iptables += ":{0}".format(rule["portEnd"]) + + iptables += " -j {0}\n".format(rules_action) + + if conf["allowDns"]: + iptables += "-A FORWARD -i vif{0}.0 -p udp --dport 53 -j ACCEPT\n".format(xid) + + iptables += "-A FORWARD -i vif{0}.0 -j {1}\n".format(xid, default_action) iptables += "#End of VM rules\n" iptables += "-A FORWARD -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT\n" @@ -1268,17 +1257,18 @@ class QubesAppVm(QubesVm): def write_firewall_conf(self, conf): root = xml.etree.ElementTree.Element( "QubesFirwallRules", - policy="allow" if conf["allow"] else "deny" + policy = "allow" if conf["allow"] else "deny", + dns = "allow" if conf["allowDns"] else "deny" ) for rule in conf["rules"]: element = xml.etree.ElementTree.Element( - "allow" if rule["allow"] else "deny", - name=rule["name"], + "rule", address=rule["address"], - netmask=str(rule["netmask"]), port=str(rule["portBegin"]), ) + if rule["netmask"] is not None and rule["netmask"] != 32: + element.set("netmask", str(rule["netmask"])) if rule["portEnd"] is not None: element.set("toport", str(rule["portEnd"])) root.append(element) @@ -1302,32 +1292,40 @@ class QubesAppVm(QubesVm): return True def get_firewall_conf(self): - conf = { "allow": True, "rules": list() } + conf = { "rules": list() } try: tree = xml.etree.ElementTree.parse(self.firewall_conf) root = tree.getroot() - for element in root: - rule = { "allow": element.tag=="allow" } + conf["allow"] = (root.get("policy") == "allow") + conf["allowDns"] = (root.get("dns") == "allow") - attr_list = ("name", "address", "netmask", "port", "toport") + for element in root: + rule = {} + attr_list = ("address", "netmask", "port", "toport") for attribute in attr_list: rule[attribute] = element.get(attribute) - rule["netmask"] = int(rule["netmask"]) + if rule["netmask"] is not None: + rule["netmask"] = int(rule["netmask"]) + else: + rule["netmask"] = 32 + rule["portBegin"] = int(rule["port"]) + if rule["toport"] is not None: rule["portEnd"] = int(rule["toport"]) else: rule["portEnd"] = None + del(rule["port"]) del(rule["toport"]) conf["rules"].append(rule) - except (EnvironmentError) as err: + except EnvironmentError as err: return conf except (xml.parsers.expat.ExpatError, ValueError, LookupError) as err: From 14aaccbc5f811cdcfc3b430b7d582db7586df24a Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sun, 27 Feb 2011 00:06:46 +0100 Subject: [PATCH 20/72] Update TemplateVM with running AppVM: part 2 - support for template modify in qvm-core - tool for commit changes to template --- dom0/qvm-core/qubes.py | 41 ++++++++++++++---- dom0/qvm-tools/qvm-prefs | 2 + dom0/qvm-tools/qvm-template-commit | 67 ++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 9 deletions(-) create mode 100755 dom0/qvm-tools/qvm-template-commit diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 55ab2d6b..cc8bfd7f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -624,13 +624,14 @@ class QubesTemplateVm(QubesVm): dir_path = kwargs["dir_path"] - # TempleteVM doesn't use root-cow image if root_img is not None and os.path.isabs(root_img): self.root_img = root_img else: self.root_img = dir_path + "/" + ( root_img if root_img is not None else default_root_img) + self.rootcow_img = dir_path + "/" + default_rootcow_img + if private_img is not None and os.path.isabs(private_img): self.private_img = private_img else: @@ -711,6 +712,14 @@ class QubesTemplateVm(QubesVm): format(src_template_vm.root_img, self.root_img) # We prefer to use Linux's cp, because it nicely handles sparse files retcode = subprocess.call (["cp", src_template_vm.root_img, self.root_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(src_template_vm.root_img, self.root_img)) + if verbose: + print "--> Copying the template's root COW image:\n{0} ==>\n{1}".\ + format(src_template_vm.rootcow_img, self.rootcow_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", src_template_vm.rootcow_img, self.rootcow_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ format(src_template_vm.root_img, self.root_img)) @@ -779,13 +788,29 @@ class QubesTemplateVm(QubesVm): if not self.is_updateable(): raise QubesException ("Cannot start Template VM that is marked \"nonupdatable\"") - # First ensure that none of our appvms is running: - for appvm in self.appvms.values(): - if appvm.is_running(): - raise QubesException ("Cannot start TemplateVM when one of its AppVMs is running!") + # TODO?: check if none of running appvms are outdated return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose) + def commit_changes (self): + + assert not self.is_running(), "Attempt to commit changes on running Template VM!" + + print "--> Commiting template updates... COW: {0}...".format (self.rootcow_img) + + if dry_run: + return + if os.path.exists (self.rootcow_img): + os.remove (self.rootcow_img) + + + f_cow = open (self.rootcow_img, "w") + f_root = open (self.root_img, "r") + f_root.seek(0, os.SEEK_END) + f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img + f_cow.close () + f_root.close() + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesTemplateVm", @@ -795,6 +820,7 @@ class QubesTemplateVm(QubesVm): conf_file=self.conf_file, appvms_conf_file=self.appvms_conf_file, root_img=self.root_img, + rootcow_img=self.rootcow_img, private_img=self.private_img, uses_default_netvm=str(self.uses_default_netvm), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", @@ -1194,10 +1220,6 @@ class QubesAppVm(QubesVm): if self.is_running(): raise QubesException("VM is already running!") - # First ensure that our template is *not* running: - if self.template_vm.is_running(): - raise QubesException ("Cannot start AppVM when its template is running!") - if not self.is_updateable(): self.reset_cow_storage() self.reset_swap_cow_storage() @@ -1728,3 +1750,4 @@ class QubesDaemonPidfile(object): self.remove_pidfile() +# vim:sw=4:et: diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index f2c77367..f9c129ff 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -42,6 +42,8 @@ def do_list(vm): print fmt.format ("config", vm.conf_file) if not vm.is_appvm(): print fmt.format ("root img", vm.root_img) + if vm.is_templete(): + print fmt.format ("root COW img", vm.rootcow_img) if vm.is_appvm(): print fmt.format ("root img", vm.template_vm.root_img) print fmt.format ("root COW img", vm.rootcow_img) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit new file mode 100755 index 00000000..d0b574bd --- /dev/null +++ b/dom0/qvm-tools/qvm-template-commit @@ -0,0 +1,67 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2011 Marek Marczykowski +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +from qubes.qubes import QubesVmCollection +from qubes.qubes import QubesException +from optparse import OptionParser +import subprocess + +qubes_guid_path = "/usr/bin/qubes_guid" + +def main(): + usage = "usage: %prog [options] " + parser = OptionParser (usage) + + (options, args) = parser.parse_args () + if (len (args) != 1): + parser.error ("You must specify VM name!") + vmname = args[0] + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vm = qvm_collection.get_vm_by_name(vmname) + if vm is None: + print "A VM with the name '{0}' does not exist in the system.".format(vmname) + exit(1) + + if not vm.is_templatevm(): + print "A VM '{0}' is not template.".format(vmname) + exit(1) + + if vm.is_running(): + print "You must stop VM first." + exit(1) + + try: + vm.verify_files() + vm.commit_changes() + except (IOError, OSError, QubesException) as err: + print "ERROR: {0}".format(err) + exit (1) + + exit (0) + + +main() From 50cdb7ca7e3d637c6602be8583fd517169ecc8bd Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Mon, 28 Feb 2011 18:52:55 +0100 Subject: [PATCH 21/72] block-snapshot fixes --- common/block-snapshot | 51 ++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 20 deletions(-) mode change 100644 => 100755 common/block-snapshot diff --git a/common/block-snapshot b/common/block-snapshot old mode 100644 new mode 100755 index a08a10c5..a668935c --- a/common/block-snapshot +++ b/common/block-snapshot @@ -7,7 +7,6 @@ dir=$(dirname "$0") . "$dir/block-common.sh" - get_dev() { dev=$1 @@ -26,10 +25,10 @@ get_dev() { fatal "Unable to lookup $file: dev: $devnum inode: $inode" fi - dev_list=$(losetup -a | grep ' \[0*'${dev}'\]:'${inode} | cut -d : -f 1) - for dev in $dev_list; do + dev_list=$(losetup -a | grep ' \[0*'${devnum}'\]:'${inode} | cut -d : -f 1) + for loopdev in $dev_list; do # found existing loop to this file - echo $dev + echo $loopdev return done @@ -141,40 +140,52 @@ case "$command" in fatal "No device node to remove" fi + if [ ! -e "$node" ]; then + fatal "Device $node does not exists" + fi + claim_lock "block" use_count=$(dmsetup info $node|grep Open|awk '{print $3}') # do not remove snapshot if snapshot origin is still present - if [ "${node/snapshot/}" != "$node" -a - -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then - ((use_count++)) + if [ "${node/snapshot/}" != "$node" -a -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then + use_count=1 fi if [ "$use_count" -gt 0 ]; then log info "Device $node still in use - not removing" + release_lock "block" exit 0 fi # get list of used (loop) devices - deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" + deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" - # remove unused snapshots - for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2); do - use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') - if [ $use_count -eq 0 ]; then - # unused snapshot - remove it - deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]+\))#/dev/loop\1#g')" - dmsetup remove $snap - fi - done - - do_or_die dmsetup remove $node + # if this is origin + if [ "${node/origin/}" != "$node" ]; then + # remove unused snapshots + for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2)-*; do + use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') + if [ "$use_count" -eq 0 ]; then + # unused snapshot - remove it + deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" + log debug "Removing $snap" + dmsetup remove $snap + fi + done + fi + + if [ -e $node ]; then + log debug "Removing $node" + dmsetup remove $node + fi # try to free loop devices for dev in $deps; do if [ -b "$dev" ]; then - losetup -d $dev 2> /dev/null + log debug "Removing $dev" + losetup -d $dev || true 2> /dev/null fi done From 8bcbbb647b16a6cd825a1570355d58deab0c03ac Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:35:36 +0100 Subject: [PATCH 22/72] Add backup files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4d22dcb6..228b7abe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ rpm/ *.pyc +*~ From b778fa3210d5529bdeff82eefa5a027a5d5cf1d2 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:52:19 +0100 Subject: [PATCH 23/72] Add typo in qvm-template-commit As in original classes... --- dom0/qvm-tools/qvm-template-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit index d0b574bd..8ccd39fe 100755 --- a/dom0/qvm-tools/qvm-template-commit +++ b/dom0/qvm-tools/qvm-template-commit @@ -46,7 +46,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if not vm.is_templatevm(): + if not vm.is_templete(): print "A VM '{0}' is not template.".format(vmname) exit(1) From d1cfcac49cdade42039fbaf0d67bf9acb299c21f Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:55:54 +0100 Subject: [PATCH 24/72] Add BR to core-appvm.spec --- rpm_spec/core-appvm.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index c9a009d1..e901a12c 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -34,6 +34,8 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: /usr/bin/mimeopen +BuildRequires: gcc +BuildRequires: xen-devel Provides: qubes-core-vm %define _builddir %(pwd)/appvm From 24c0778154262c709dc20463bd47666d8dc4c8ea Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 2 Mar 2011 11:58:22 +0100 Subject: [PATCH 25/72] gitignore files - add build products --- .gitignore | 2 ++ appvm/.gitignore | 3 +++ common/.gitignore | 1 + dom0/restore/.gitignore | 3 +++ 4 files changed, 9 insertions(+) create mode 100644 appvm/.gitignore create mode 100644 common/.gitignore create mode 100644 dom0/restore/.gitignore diff --git a/.gitignore b/.gitignore index 228b7abe..e2440642 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ rpm/ *.pyc +*.pyo *~ +*.o diff --git a/appvm/.gitignore b/appvm/.gitignore new file mode 100644 index 00000000..edd6d099 --- /dev/null +++ b/appvm/.gitignore @@ -0,0 +1,3 @@ +qubes_add_pendrive_script +qubes_penctl +qvm-open-in-dvm diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 00000000..03034a95 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1 @@ +meminfo-writer diff --git a/dom0/restore/.gitignore b/dom0/restore/.gitignore new file mode 100644 index 00000000..0535ab8a --- /dev/null +++ b/dom0/restore/.gitignore @@ -0,0 +1,3 @@ +qubes_restore +xenfreepages +xenstore-watch From d207ecaceaf1cc078d8fc99c47675d8a37f62b3d Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:21:14 +0100 Subject: [PATCH 26/72] Implemented QubesFirewallVm subclass of QubesNetVm --- dom0/qvm-core/qubes.py | 107 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 5 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index cc8bfd7f..d38f101d 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -84,6 +84,7 @@ swap_cow_sz = 1024*1024*1024 VM_TEMPLATE = 'TempleteVM' VM_APPVM = 'AppVM' VM_NETVM = 'NetVM' +VM_FWVM = 'FirewallVM' VM_DISPOSABLEVM = 'DisposableVM' class XendSession(object): @@ -282,7 +283,13 @@ class QubesVm(object): return False def is_netvm(self): - if self.type == VM_NETVM: + if self.type == VM_NETVM or self.type == VM_FWVM: + return True + else: + return False + + def is_fwvm(self): + if self.type == VM_FWVM: return True else: return False @@ -559,9 +566,7 @@ class QubesVm(object): xm_cmdline = ["/usr/sbin/xm", "network-attach", self.name, "script=vif-route-qubes", "ip="+actual_ip] if self.netvm_vm.qid != 0: if not self.netvm_vm.is_running(): - print "ERROR: NetVM not running, please start it first" - self.force_shutdown() - raise QubesException ("NetVM not running") + self.netvm_vm.start() retcode = subprocess.call (xm_cmdline + ["backend={0}".format(self.netvm_vm.name)]) if retcode != 0: self.force_shutdown() @@ -917,7 +922,9 @@ class QubesNetVm(QubesServiceVm): if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label - super(QubesNetVm, self).__init__(type=VM_NETVM, installed_by_rpm=True, **kwargs) + if "type" not in kwargs or kwargs["type"] is None: + kwargs["type"] = VM_NETVM + super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) @property def gateway(self): @@ -955,6 +962,30 @@ class QubesNetVm(QubesServiceVm): ) return element +class QubesFirewallVm(QubesNetVm): + """ + A class that represents a FirewallVM. A child of QubesNetVM. + """ + def __init__(self, **kwargs): + self.netvm_vm = kwargs.pop("netvm_vm") if "netvm_vm" in kwargs else None + + super(QubesFirewallVm, self).__init__(type=VM_FWVM, **kwargs) + + def create_xml_element(self): + element = xml.etree.ElementTree.Element( + "QubesFirewallVm", + qid=str(self.qid), + netid=str(self.netid), + name=self.name, + dir_path=self.dir_path, + conf_file=self.conf_file, + root_img=self.root_img, + netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", + private_img=self.private_img, + installed_by_rpm=str(self.installed_by_rpm), + ) + return element + class QubesDom0NetVm(QubesNetVm): def __init__(self): super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0, @@ -1271,6 +1302,7 @@ class QubesVmCollection(dict): def __init__(self, store_filename=qubes_store_filename): super(QubesVmCollection, self).__init__() self.default_netvm_qid = None + self.default_fw_netvm_qid = None self.default_template_qid = None self.qubes_store_filename = store_filename @@ -1370,6 +1402,27 @@ class QubesVmCollection(dict): assert False, "Wrong VM description!" self[vm.qid]=vm + if self.default_fw_netvm_qid is None: + self.set_default_fw_netvm_vm(vm) + + return vm + + def add_new_fwvm(self, name, + dir_path = None, conf_file = None, + root_img = None): + + qid = self.get_new_unused_qid() + netid = self.get_new_unused_netid() + vm = QubesFirewallVm (qid=qid, name=name, + netid=netid, + dir_path=dir_path, conf_file=conf_file, + netvm_vm = self.get_default_fw_netvm_vm(), + root_img=root_img) + + if not self.verify_new_vm (vm): + assert False, "Wrong VM description!" + self[vm.qid]=vm + if self.default_netvm_qid is None: self.set_default_netvm_vm(vm) @@ -1395,6 +1448,16 @@ class QubesVmCollection(dict): else: return self[self.default_netvm_qid] + def set_default_fw_netvm_vm(self, vm): + assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name) + self.default_fw_netvm_qid = vm.qid + + def get_default_fw_netvm_vm(self): + if self.default_fw_netvm_qid is None: + return None + else: + return self[self.default_fw_netvm_qid] + def get_vm_by_name(self, name): for vm in self.values(): if (vm.name == name): @@ -1550,6 +1613,40 @@ class QubesVmCollection(dict): os.path.basename(sys.argv[0]), err)) return False + # Next read in the FirewallVMs, because they may be referenced + # by other VMs + for element in tree.findall("QubesFirewallVm"): + try: + kwargs = {} + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", + "private_img", "root_img", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + kwargs["netid"] = int(kwargs["netid"]) + + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + kwargs["netvm_vm"] = netvm_vm + + vm = QubesFirewallVm(**kwargs) + self[vm.qid] = vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesFirewallVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + self.default_template_qid # Then, read in the TemplateVMs, because a reference to templete VM From cba89a87473f6a997cf35fd513f48e53813df73c Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:21:41 +0100 Subject: [PATCH 27/72] Show FirewallVMs in qvm-ls --- dom0/qvm-tools/qvm-ls | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 395a3426..ced8f585 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -39,14 +39,15 @@ fields = { + ('}' if vm.is_netvm() else '')"}, "type": {"func": "'Tpl' if vm.is_templete() else \ - (' Net' if vm.is_netvm() else '')"}, + (' Fw' if vm.is_fwvm() else \ + (' Net' if vm.is_netvm() else ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, "template": {"func": "'n/a' if vm.is_templete() or vm.is_netvm() else\ qvm_collection[vm.template_vm.qid].name"}, - "netvm": {"func": "'n/a' if vm.is_netvm() else\ + "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_fwvm() else\ ('*' if vm.uses_default_netvm else '') +\ qvm_collection[vm.netvm_vm.qid].name\ if vm.netvm_vm is not None else '-'"}, From 60caf9af7f54482e3ef95511ab501ce9ea89fd78 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:35:36 +0100 Subject: [PATCH 28/72] Refactored QubesVm.is_*vm() methods --- dom0/qvm-core/qubes.py | 68 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index d38f101d..cab94a27 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -81,12 +81,6 @@ qubes_appmenu_remove_cmd = "/usr/lib/qubes/remove_appvm_appmenus.sh" # as "should be good for everyone" swap_cow_sz = 1024*1024*1024 -VM_TEMPLATE = 'TempleteVM' -VM_APPVM = 'AppVM' -VM_NETVM = 'NetVM' -VM_FWVM = 'FirewallVM' -VM_DISPOSABLEVM = 'DisposableVM' - class XendSession(object): def __init__(self): self.get_xend_session_old_api() @@ -171,7 +165,7 @@ class QubesVm(object): Note that qid is not the same as Xen's domid! """ - def __init__(self, qid, name, type, + def __init__(self, qid, name, dir_path, conf_file = None, uses_default_netvm = True, netvm_vm = None, @@ -196,7 +190,6 @@ class QubesVm(object): else: self.conf_file = dir_path + "/" + conf_file - self.__type = type self.uses_default_netvm = uses_default_netvm self.netvm_vm = netvm_vm @@ -217,10 +210,6 @@ class QubesVm(object): def qid(self): return self.__qid - @property - def type(self): - return self.__type - @property def ip(self): if self.netvm_vm is not None: @@ -271,34 +260,19 @@ class QubesVm(object): self.updateable = False def is_templete(self): - if self.type == VM_TEMPLATE: - return True - else: - return False + return isinstance(self, QubesTemplateVm) def is_appvm(self): - if self.type == VM_APPVM: - return True - else: - return False + return isinstance(self, QubesAppVm) def is_netvm(self): - if self.type == VM_NETVM or self.type == VM_FWVM: - return True - else: - return False + return isinstance(self, QubesNetVm) def is_fwvm(self): - if self.type == VM_FWVM: - return True - else: - return False + return isinstance(self, QubesFirewallVm) def is_disposablevm(self): - if self.type == VM_DISPOSABLEVM: - return True - else: - return False + return isinstance(self, QubesDisposableVm) def add_to_xen_storage(self): if dry_run: @@ -625,7 +599,7 @@ class QubesTemplateVm(QubesVm): private_img = kwargs.pop("private_img") if "private_img" in kwargs else None appvms_conf_file = kwargs.pop("appvms_conf_file") if "appvms_conf_file" in kwargs else None - super(QubesTemplateVm, self).__init__(type=VM_TEMPLATE, label = default_template_label, **kwargs) + super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs) dir_path = kwargs["dir_path"] @@ -654,6 +628,10 @@ class QubesTemplateVm(QubesVm): self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir self.appvms = QubesVmCollection() + @property + def type(self): + return "TempleteVM" + def set_updateable(self): if self.is_updateable(): return @@ -922,10 +900,12 @@ class QubesNetVm(QubesServiceVm): if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label - if "type" not in kwargs or kwargs["type"] is None: - kwargs["type"] = VM_NETVM super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) + @property + def type(self): + return "NetVM" + @property def gateway(self): return self.__gateway @@ -969,7 +949,11 @@ class QubesFirewallVm(QubesNetVm): def __init__(self, **kwargs): self.netvm_vm = kwargs.pop("netvm_vm") if "netvm_vm" in kwargs else None - super(QubesFirewallVm, self).__init__(type=VM_FWVM, **kwargs) + super(QubesFirewallVm, self).__init__(**kwargs) + + @property + def type(self): + return "FirewallVM" def create_xml_element(self): element = xml.etree.ElementTree.Element( @@ -1067,7 +1051,7 @@ class QubesDisposableVm(QubesVm): template_vm = kwargs.pop("template_vm") - super(QubesDisposableVm, self).__init__(type=VM_DISPOSABLEVM, dir_path=None, **kwargs) + super(QubesDisposableVm, self).__init__(dir_path=None, **kwargs) qid = kwargs["qid"] assert template_vm is not None, "Missing template_vm for DisposableVM!" @@ -1079,6 +1063,10 @@ class QubesDisposableVm(QubesVm): self.template_vm = template_vm template_vm.appvms[qid] = self + @property + def type(self): + return "DisposableVM" + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesDisposableVm", @@ -1108,7 +1096,7 @@ class QubesAppVm(QubesVm): template_vm = kwargs.pop("template_vm") - super(QubesAppVm, self).__init__(type=VM_APPVM, **kwargs) + super(QubesAppVm, self).__init__(**kwargs) qid = kwargs["qid"] dir_path = kwargs["dir_path"] @@ -1132,6 +1120,10 @@ class QubesAppVm(QubesVm): self.swapcow_img = dir_path + "/" + default_swapcow_img + @property + def type(self): + return "AppVM" + def set_updateable(self): if self.is_updateable(): return From 026a109d1fec96d2da620040f4e69ec97a660443 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 21:45:09 +0100 Subject: [PATCH 29/72] Fixed setting netvm of FWVM --- dom0/qvm-core/qubes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index cab94a27..c70b7704 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -947,9 +947,7 @@ class QubesFirewallVm(QubesNetVm): A class that represents a FirewallVM. A child of QubesNetVM. """ def __init__(self, **kwargs): - self.netvm_vm = kwargs.pop("netvm_vm") if "netvm_vm" in kwargs else None - - super(QubesFirewallVm, self).__init__(**kwargs) + super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) @property def type(self): From 167c30aa6e8299c8c668f0d81b21652d65335765 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Feb 2011 22:15:00 +0100 Subject: [PATCH 30/72] Start xend and xenstored during package installation --- rpm_spec/core-dom0.spec | 53 ++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index a4e57d86..6b9e1d4d 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -29,15 +29,16 @@ Name: qubes-core-dom0 Version: %{version} -Release: 1 +Release: 2 Summary: The Qubes core files (Dom0-side) Group: Qubes Vendor: Invisible Things Lab License: GPL URL: http://www.qubes-os.org +BuildRequires: xen-devel Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0 -Conflicts: qubes-gui-dom0 < 1.1.13 +Conflicts: qubes-gui-dom0 < 1.1.13 Requires: NetworkManager >= 0.8.1-1 %define _builddir %(pwd)/dom0 @@ -126,10 +127,16 @@ mkdir -p $RPM_BUILD_ROOT/var/run/qubes %post +# Create NetworkManager configuration if we do not have it +if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then +echo '[main]' > /etc/NetworkManager/NetworkManager.conf +echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf +echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf +fi /usr/lib/qubes/qubes_fix_nm_conf.sh if [ -e /etc/yum.repos.d/qubes-r1-dom0.repo ]; then -# we want the user to use the repo that comes with qubes-code-dom0 packages instead +# we want the user to use the repo that comes with qubes-core-dom0 packages instead rm -f /etc/yum.repos.d/qubes-r1-dom0.repo fi @@ -138,15 +145,16 @@ fi #exit 0 #fi -# TODO: This is only temporary, until we will have our own installer -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - chkconfig $srv off -done +## TODO: This is only temporary, until we will have our own installer +#for f in /etc/init.d/* +#do +# srv=`basename $f` +# [ $srv = 'functions' ] && continue +# [ $srv = 'killall' ] && continue +# [ $srv = 'halt' ] && continue +# [ $srv = 'single' ] && continue +# chkconfig $srv off +#done chkconfig iptables on chkconfig NetworkManager on @@ -167,6 +175,20 @@ chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" chkconfig qubes_netvm on || echo "WARNING: Cannot enable service qubes_netvm!" chkconfig qubes_setupdvm on || echo "WARNING: Cannot enable service qubes_setupdvm!" +HAD_SYSCONFIG_NETWORK=yes +if ! [ -e /etc/sysconfig/network ]; then + HAD_SYSCONFIG_NETWORK=no + # supplant empty one so NetworkManager init script does not complain + touch /etc/sysconfig/network +fi + +# Load evtchn module - xenstored needs it +modprobe evtchn + +# Now launch xend - we will need it for subsequent steps +service xenstored start +service xend start + if ! [ -e /var/lib/qubes/qubes.xml ]; then # echo "Initializing Qubes DB..." umask 007; sg qubes -c qvm-init-storage @@ -175,13 +197,16 @@ for i in /usr/share/qubes/icons/*.png ; do xdg-icon-resource install --novendor --size 48 $i done -/etc/init.d/qubes_core start +service qubes_core start NETVM=$(qvm-get-default-netvm) if [ "X"$NETVM = "X""dom0" ] ; then - /etc/init.d/qubes_netvm start + service qubes_netvm start fi +if [ "x"$HAD_SYSCONFIG_NETWORK = "xno" ]; then + rm -f /etc/sysconfig/network +fi %clean rm -rf $RPM_BUILD_ROOT From 8e465a13b57737ad858862b4431e1b31873205d8 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Mon, 21 Feb 2011 18:13:27 +0100 Subject: [PATCH 31/72] Implemented firewall_conf storage --- dom0/qvm-core/qubes.py | 44 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index c70b7704..c00786ed 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -65,6 +65,7 @@ default_appvms_conf_file = "appvm-template.conf" default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM cloning default_appmenus_templates_subdir = "apps.templates" default_kernels_subdir = "kernels" +default_firewall_conf_file = "firewall.xml" # do not allow to start a new AppVM if Dom0 mem was to be less than this dom0_min_memory = 700*1024*1024 @@ -529,7 +530,7 @@ class QubesVm(object): print "--> Setting Xen Store info for the VM..." self.create_xenstore_entries(xid) - if not self.is_netvm() and self.netvm_vm is not None: + if (not self.is_netvm() or self.is_fwvm()) and self.netvm_vm is not None: assert self.netvm_vm is not None if verbose: print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name) @@ -953,6 +954,20 @@ class QubesFirewallVm(QubesNetVm): def type(self): return "FirewallVM" + def create_xenstore_entries(self, xid): + if dry_run: + return + + super(QubesFirewallVm, self).create_xenstore_entries(xid) + self.write_iptables_xenstore_entry() + + def write_iptables_xenstore_entry(self): + iptables = "" + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_iptables".format(self.get_xid()), + iptables]) + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesFirewallVm", @@ -1117,6 +1132,11 @@ class QubesAppVm(QubesVm): self.rootcow_img = dir_path + "/" + default_rootcow_img self.swapcow_img = dir_path + "/" + default_swapcow_img + if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None: + kwargs["firewall_conf"] = dir_path + "/" + default_firewall_conf_file + + self.firewall_conf = kwargs["firewall_conf"] + @property def type(self): @@ -1189,6 +1209,26 @@ class QubesAppVm(QubesVm): def create_appmenus(self, verbose): subprocess.check_call ([qubes_appmenu_create_cmd, self.template_vm.appmenus_templates_dir, self.name]) + def write_firewall_conf(self, xml): + f = open(self.firewall_conf, 'a') # create the file if not exist + f.close() + with open(self.firewall_conf, 'w') as f: + fcntl.lockf(f, fcntl.LOCK_EX) + xml.write(f, "UTF-8") + fcntl.lockf(f, fcntl.LOCK_UN) + f.close() + + def get_firewall_conf(self): + try: + tree = xml.etree.ElementTree.parse(self.firewall_conf) + except (EnvironmentError, + xml.parsers.expat.ExpatError) as err: + print("{0}: load error: {1}".format( + os.path.basename(sys.argv[0]), err)) + return None + + return tree.getroot() + def get_disk_utilization_root_img(self): return 0 @@ -1546,7 +1586,7 @@ class QubesVmCollection(dict): self.qubes_store_file.truncate() tree.write(self.qubes_store_file, "UTF-8") except EnvironmentError as err: - print("{0}: import error: {1}".format( + print("{0}: export error: {1}".format( os.path.basename(sys.argv[0]), err)) return False return True From bd05975a53c1c6c8aefd0bec42756b550feb5698 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:00:19 +0100 Subject: [PATCH 32/72] Removed trailing whitespace --- dom0/qvm-core/qubes.py | 72 +++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index c00786ed..5abd93c2 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -48,9 +48,9 @@ qubes_guid_path = "/usr/bin/qubes_guid" qubes_base_dir = "/var/lib/qubes" -qubes_appvms_dir = qubes_base_dir + "/appvms" -qubes_templates_dir = qubes_base_dir + "/vm-templates" -qubes_servicevms_dir = qubes_base_dir + "/servicevms" +qubes_appvms_dir = qubes_base_dir + "/appvms" +qubes_templates_dir = qubes_base_dir + "/vm-templates" +qubes_servicevms_dir = qubes_base_dir + "/servicevms" qubes_store_filename = qubes_base_dir + "/qubes.xml" qubes_max_qid = 254*254 @@ -105,7 +105,7 @@ class XendSession(object): if not dry_run: xend_session = XendSession() - + class QubesException (Exception) : pass @@ -203,7 +203,7 @@ class QubesVm(object): self.icon_path = self.dir_path + "/icon.png" else: self.icon_path = None - + if not dry_run and xend_session.session is not None: self.refresh_xend_session() @@ -245,7 +245,7 @@ class QubesVm(object): def is_networked(self): if self.is_netvm(): return True - + if self.netvm_vm is not None: return True else: @@ -376,7 +376,7 @@ class QubesVm(object): for cpu in cpus_util: cpu_total_load += cpus_util[cpu] cpu_total_load /= len(cpus_util) - p = 100*cpu_total_load + p = 100*cpu_total_load if p > 100: p = 100 return p @@ -426,7 +426,7 @@ class QubesVm(object): def get_private_img_sz(self): if not os.path.exists(self.private_img): return 0 - + return os.path.getsize(self.private_img) def create_xenstore_entries(self, xid): @@ -506,8 +506,8 @@ class QubesVm(object): if verbose: print "--> Loading the VM (type = {0})...".format(self.type) - if not self.is_netvm(): - total_mem_mb = self.get_total_xen_memory()/1024/1024 + if not self.is_netvm(): + total_mem_mb = self.get_total_xen_memory()/1024/1024 xend_session.xend_server.xend.domain.maxmem_set(self.name, total_mem_mb) mem_required = self.get_mem_dynamic_max() @@ -523,7 +523,7 @@ class QubesVm(object): xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused qmemman_client.close() # let qmemman_daemon resume balancing - + xid = int (xend_session.session.xenapi.VM.get_domid (self.session_uuid)) if verbose: @@ -716,15 +716,15 @@ class QubesTemplateVm(QubesVm): print "--> Copying the template's appvm templates dir:\n{0} ==>\n{1}".\ format(src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) - - + + def get_disk_utilization_root_img(self): return self.get_disk_usage(self.root_img) def get_root_img_sz(self): if not os.path.exists(self.root_img): return 0 - + return os.path.getsize(self.root_img) def verify_files(self): @@ -761,7 +761,7 @@ class QubesTemplateVm(QubesVm): raise QubesException ( "VM's kernels directory does not exist: {0}".\ format(self.kernels_dir)) - + return True def start(self, debug_console = False, verbose = False): @@ -850,7 +850,7 @@ class QubesServiceVm(QubesVm): # In fact there is no point in making it unpdatebale... # So, this is just for completncess assert not self.is_running() - self.updateable = True + self.updateable = True def get_disk_utilization_root_img(self): return self.get_disk_usage(self.root_img) @@ -858,7 +858,7 @@ class QubesServiceVm(QubesVm): def get_root_img_sz(self): if not os.path.exists(self.root_img): return 0 - + return os.path.getsize(self.root_img) def verify_files(self): @@ -925,7 +925,7 @@ class QubesNetVm(QubesServiceVm): def get_ip_for_vm(self, qid): hi = qid / 253 - lo = qid % 253 + 2 + lo = qid % 253 + 2 assert hi >= 0 and hi <= 254 and lo >= 2 and lo <= 254, "Wrong IP address for VM" return self.netprefix + "{0}.{1}".format(hi,lo) @@ -1006,7 +1006,7 @@ class QubesDom0NetVm(QubesNetVm): for cpu in self.session_cpus: cpu_total_load += xend_session.session.xenapi.host_cpu.get_utilisation(cpu) cpu_total_load /= len(self.session_cpus) - p = 100*cpu_total_load + p = 100*cpu_total_load if p > 100: p = 100 return p @@ -1186,14 +1186,14 @@ class QubesAppVm(QubesVm): if verbose: print "--> Creating the VM config file: {0}".format(self.conf_file) - + self.create_config_file() template_priv = self.template_vm.private_img if verbose: print "--> Copying the template's private image: {0}".\ format(template_priv) - + # We prefer to use Linux's cp, because it nicely handles sparse files retcode = subprocess.call (["cp", template_priv, self.private_img]) if retcode != 0: @@ -1393,7 +1393,7 @@ class QubesVmCollection(dict): installed_by_rpm = True): qid = self.get_new_unused_qid() - vm = QubesTemplateVm (qid=qid, name=name, + vm = QubesTemplateVm (qid=qid, name=name, dir_path=dir_path, conf_file=conf_file, root_img=root_img, private_img=private_img, installed_by_rpm=installed_by_rpm, @@ -1423,7 +1423,7 @@ class QubesVmCollection(dict): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesNetVm (qid=qid, name=name, + vm = QubesNetVm (qid=qid, name=name, netid=netid, dir_path=dir_path, conf_file=conf_file, root_img=root_img) @@ -1443,7 +1443,7 @@ class QubesVmCollection(dict): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesFirewallVm (qid=qid, name=name, + vm = QubesFirewallVm (qid=qid, name=name, netid=netid, dir_path=dir_path, conf_file=conf_file, netvm_vm = self.get_default_fw_netvm_vm(), @@ -1502,7 +1502,7 @@ class QubesVmCollection(dict): vms = set([vm for vm in self.values() if (vm.is_appvm() and vm.template_vm.qid == template_qid)]) return vms - + def verify_new_vm(self, new_vm): # Verify that qid is unique @@ -1579,7 +1579,7 @@ class QubesVmCollection(dict): tree = xml.etree.ElementTree.ElementTree(root) try: - + # We need to manually truncate the file, as we open the # file as "r+" in the lock_db_for_writing() function self.qubes_store_file.seek (0, os.SEEK_SET) @@ -1610,11 +1610,11 @@ class QubesVmCollection(dict): return False element = tree.getroot() - default_template = element.get("default_template") + default_template = element.get("default_template") self.default_template_qid = int(default_template) \ if default_template != "None" else None - default_netvm = element.get("default_netvm") + default_netvm = element.get("default_netvm") if default_netvm is not None: self.default_netvm_qid = int(default_netvm) \ if default_netvm != "None" else None @@ -1637,7 +1637,7 @@ class QubesVmCollection(dict): vm = QubesNetVm(**kwargs) self[vm.qid] = vm - + except (ValueError, LookupError) as err: print("{0}: import error (QubesNetVM) {1}".format( os.path.basename(sys.argv[0]), err)) @@ -1668,10 +1668,10 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + vm = QubesFirewallVm(**kwargs) self[vm.qid] = vm - + except (ValueError, LookupError) as err: print("{0}: import error (QubesFirewallVM) {1}".format( os.path.basename(sys.argv[0]), err)) @@ -1680,7 +1680,7 @@ class QubesVmCollection(dict): self.default_template_qid # Then, read in the TemplateVMs, because a reference to templete VM - # is needed to create each AppVM + # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): try: @@ -1717,7 +1717,7 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + vm = QubesTemplateVm(**kwargs) self[vm.qid] = vm @@ -1769,7 +1769,7 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + if kwargs["label"] is not None: if kwargs["label"] not in QubesVmLabels: print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) @@ -1805,8 +1805,8 @@ class QubesVmCollection(dict): format(kwargs["name"], kwargs["template_qid"]) kwargs["template_vm"] = template_vm - kwargs["netvm_vm"] = self.get_default_netvm_vm() - + kwargs["netvm_vm"] = self.get_default_netvm_vm() + if kwargs["label"] is not None: if kwargs["label"] not in QubesVmLabels: print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) From aa536fdbda71525db337368ed1c3f2f37fff945a Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:01:30 +0100 Subject: [PATCH 33/72] Properly set FwVM xenstore files --- dom0/qvm-core/qubes.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 5abd93c2..afa3d9f6 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -460,8 +460,8 @@ class QubesVm(object): "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netvm_network".format(xid), self.network]) - - elif self.netvm_vm is not None: + + if self.netvm_vm is not None: retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_ip".format(xid), @@ -470,19 +470,17 @@ class QubesVm(object): retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netmask".format(xid), - self.netmask]) + self.netvm_vm.netmask]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_gateway".format(xid), - self.gateway]) + self.netvm_vm.gateway]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_secondary_dns".format(xid), - self.secondary_dns]) - else: - pass + self.netvm_vm.secondary_dns]) def get_total_xen_memory(self): From 0c1b6ca4b0010e83a6e615357d66942a3e749c58 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:02:46 +0100 Subject: [PATCH 34/72] Store firewal rules in Python data structure --- dom0/qvm-core/qubes.py | 74 +++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index afa3d9f6..4ccff877 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1207,25 +1207,77 @@ class QubesAppVm(QubesVm): def create_appmenus(self, verbose): subprocess.check_call ([qubes_appmenu_create_cmd, self.template_vm.appmenus_templates_dir, self.name]) - def write_firewall_conf(self, xml): - f = open(self.firewall_conf, 'a') # create the file if not exist - f.close() - with open(self.firewall_conf, 'w') as f: - fcntl.lockf(f, fcntl.LOCK_EX) - xml.write(f, "UTF-8") - fcntl.lockf(f, fcntl.LOCK_UN) - f.close() + def write_firewall_conf(self, conf): + root = xml.etree.ElementTree.Element( + "QubesFirwallRules", + policy="allow" if conf["allow"] else "deny" + ) + + for rule in conf["rules"]: + element = xml.etree.ElementTree.Element( + "allow" if rule["allow"] else "deny", + name=rule["name"], + address=rule["address"], + netmask=str(rule["netmask"]), + port=str(rule["portBegin"]), + ) + if rule["portEnd"] is not None: + element.set("toport", str(rule["portEnd"])) + root.append(element) + + tree = xml.etree.ElementTree.ElementTree(root) + + try: + f = open(self.firewall_conf, 'a') # create the file if not exist + f.close() + + with open(self.firewall_conf, 'w') as f: + fcntl.lockf(f, fcntl.LOCK_EX) + tree.write(f, "UTF-8") + fcntl.lockf(f, fcntl.LOCK_UN) + f.close() + except EnvironmentError as err: + print "{0}: save error: {1}".format( + os.path.basename(sys.argv[0]), err) + return False + + return True def get_firewall_conf(self): + conf = { "allow": True, "rules": list() } + try: tree = xml.etree.ElementTree.parse(self.firewall_conf) - except (EnvironmentError, - xml.parsers.expat.ExpatError) as err: + root = tree.getroot() + + for element in root: + rule = { "allow": element.tag=="allow" } + + attr_list = ("name", "address", "netmask", "port", "toport") + + for attribute in attr_list: + rule[attribute] = element.get(attribute) + + rule["netmask"] = int(rule["netmask"]) + rule["portBegin"] = int(rule["port"]) + if rule["toport"] is not None: + rule["portEnd"] = int(rule["toport"]) + else: + rule["portEnd"] = None + del(rule["port"]) + del(rule["toport"]) + + conf["rules"].append(rule) + + except (EnvironmentError) as err: + return conf + except (xml.parsers.expat.ExpatError, + ValueError, LookupError) as err: print("{0}: load error: {1}".format( os.path.basename(sys.argv[0]), err)) return None - return tree.getroot() + return conf def get_disk_utilization_root_img(self): return 0 From f33fcff37270c2c3c7226b5d0cd7b642371fedd1 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:03:21 +0100 Subject: [PATCH 35/72] Implemented iptables rules file generator --- dom0/qvm-core/qubes.py | 82 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 4ccff877..51a9b60c 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -29,6 +29,7 @@ import xml.parsers.expat import fcntl import re import shutil +from datetime import datetime from qmemman_client import QMemmanClient # Do not use XenAPI or create/read any VM files @@ -960,8 +961,85 @@ class QubesFirewallVm(QubesNetVm): self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): - iptables = "" - retcode = subprocess.check_call ([ + iptables = "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime()) + iptables += "*filter\n" + iptables += ":INPUT DROP [0:0]\n" + iptables += ":FORWARD DROP [0:0]\n" + iptables += ":OUTPUT ACCEPT [0:0]\n" + + # Strict INPUT rules + iptables += "-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n" + iptables += "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n" + iptables += "-A INPUT -p icmp -j ACCEPT\n" + iptables += "-A INPUT -i lo -j ACCEPT\n" + iptables += "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n" + + # Allow dom0 networking + iptables += "-A FORWARD -i vif0.0 -j ACCEPT\n" + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vms = [vm for vm in qvm_collection.values() if vm.is_appvm()] + for vm in vms: + conf = vm.get_firewall_conf() + + xid = vm.get_xid() + if xid < 0: # VM not active ATM + continue + + iptables += "# '{0}' VM:\n".format(vm.name) + + if conf["allow"]: + iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid) + + allow_rules = 0 + vm_iptables = "" + + for rule in conf["rules"]: + if rule["allow"]: + allow_rules += 1 + + vm_iptables += "# .. {0}:\n".format(rule["name"]) + + vm_iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"]) + if rule["netmask"] != 32: + vm_iptables += "/{0}".format(rule["netmask"]) + + if rule["portBegin"] > 0: + vm_iptables += " -p tcp --dport {0}".format(rule["portBegin"]) + if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: + vm_iptables += ":{0}".format(rule["portEnd"]) + + vm_iptables += " -j {0}\n".format("ACCEPT" if rule["allow"]\ + else "REJECT --reject-with icmp-host-prohibited", + ) + + iptables += vm_iptables + + if allow_rules > 0: + iptables += "# .. Needs DNS access\n" + iptables += "-A FORWARD -i vif{0}.0 -p udp --dport 53 -j ACCEPT\n".format(xid) + iptables += "# .. Allow ICMP to test network connectivity\n" + iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) + iptables += "# .. Deny everything not allowed before\n" + iptables += "-A FORWARD -i vif{0}.0 -j DROP\n".format(xid) + else: + iptables += "# .. Allow everything not denied before\n" + iptables += "-A FORWARD -i vif{0}.0 -j ACCEPT\n".format(xid) + + else: + iptables += "-A FORWARD -i vif{0}.0 -j DROP\n".format(xid) + + iptables += "#End of VM rules\n" + iptables += "-A FORWARD -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT\n" + iptables += "-A FORWARD -j DROP\n" + + iptables += "COMMIT" + + return subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_iptables".format(self.get_xid()), iptables]) From e9bd19299f9ebf26c5c88d37b6d99bd40a93309f Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 2 Mar 2011 15:04:11 +0100 Subject: [PATCH 36/72] Update firewall iptables file during VM start --- dom0/qvm-core/qubes.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 51a9b60c..6e6fe16f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -552,9 +552,15 @@ class QubesVm(object): self.force_shutdown() raise OSError ("ERROR: Cannot attach to network backend!") + #if verbose: + # print "--> Updating FirewallVMs rules..." + #for vm in qvm_collection.values(): + # if vm.is_fwvm(): + # vm.write_iptables_xenstore_entry() + if verbose: print "--> Starting the VM..." - xend_session.session.xenapi.VM.unpause (self.session_uuid) + xend_session.session.xenapi.VM.unpause (self.session_uuid) # perhaps we should move it before unpause and fork? if debug_console: From bef584c248059e21ad0074e873bb7083dee60699 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 5 Mar 2011 11:43:51 +0100 Subject: [PATCH 37/72] Restore rev 1 in core-dom0.spec --- rpm_spec/core-dom0.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 6b9e1d4d..6bd6a808 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -29,7 +29,7 @@ Name: qubes-core-dom0 Version: %{version} -Release: 2 +Release: 1 Summary: The Qubes core files (Dom0-side) Group: Qubes From 13c3a04755d3fa88506b4607c24eea01c0a353fb Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sat, 5 Mar 2011 15:13:31 +0100 Subject: [PATCH 38/72] Fix typo 'templete' --- dom0/aux-tools/reset_vm_configs.py | 2 +- dom0/qvm-core/qubes.py | 10 +++++----- dom0/qvm-tools/qvm-backup | 2 +- dom0/qvm-tools/qvm-backup-restore | 16 ++++++++-------- dom0/qvm-tools/qvm-create | 2 +- dom0/qvm-tools/qvm-ls | 10 +++++----- dom0/qvm-tools/qvm-prefs | 4 ++-- dom0/qvm-tools/qvm-remove | 2 +- dom0/qvm-tools/qvm-set-default-template | 2 +- dom0/qvm-tools/qvm-template-commit | 2 +- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/dom0/aux-tools/reset_vm_configs.py b/dom0/aux-tools/reset_vm_configs.py index 6930a1fa..faaf54b3 100755 --- a/dom0/aux-tools/reset_vm_configs.py +++ b/dom0/aux-tools/reset_vm_configs.py @@ -14,7 +14,7 @@ def main(): if tvm is None: print 'Template', templ, 'does not exist' sys.exit(1) - if not tvm.is_templete(): + if not tvm.is_template(): print templ, 'is not a template' sys.exit(1) for vm in qvm_collection.values(): diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 6e6fe16f..3ab0ba83 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -261,7 +261,7 @@ class QubesVm(object): # We can always downgrade a VM to non-updateable... self.updateable = False - def is_templete(self): + def is_template(self): return isinstance(self, QubesTemplateVm) def is_appvm(self): @@ -1150,7 +1150,7 @@ class QubesDisposableVm(QubesVm): qid = kwargs["qid"] assert template_vm is not None, "Missing template_vm for DisposableVM!" - if not template_vm.is_templete(): + if not template_vm.is_template(): print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ format(new_vm.template_vm.qid) return False @@ -1196,7 +1196,7 @@ class QubesAppVm(QubesVm): dir_path = kwargs["dir_path"] assert template_vm is not None, "Missing template_vm for AppVM!" - if not template_vm.is_templete(): + if not template_vm.is_template(): print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ format(new_vm.template_vm.qid) return False @@ -1593,7 +1593,7 @@ class QubesVmCollection(dict): return vm def set_default_template_vm(self, vm): - assert vm.is_templete(), "VM {0} is not a TempleteVM!".format(vm.name) + assert vm.is_template(), "VM {0} is not a TempleteVM!".format(vm.name) self.default_template_qid = vm.qid def get_default_template_vm(self): @@ -1813,7 +1813,7 @@ class QubesVmCollection(dict): self.default_template_qid - # Then, read in the TemplateVMs, because a reference to templete VM + # Then, read in the TemplateVMs, because a reference to template VM # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): try: diff --git a/dom0/qvm-tools/qvm-backup b/dom0/qvm-tools/qvm-backup index 664b6050..52975f31 100755 --- a/dom0/qvm-tools/qvm-backup +++ b/dom0/qvm-tools/qvm-backup @@ -135,7 +135,7 @@ def main(): print s - template_vms_worth_backingup = [ vm for vm in vms_list if (vm.is_templete() and not vm.installed_by_rpm)] + template_vms_worth_backingup = [ vm for vm in vms_list if (vm.is_template() and not vm.installed_by_rpm)] if len (template_vms_worth_backingup): for vm in template_vms_worth_backingup: vm_sz = vm.get_disk_utilization() diff --git a/dom0/qvm-tools/qvm-backup-restore b/dom0/qvm-tools/qvm-backup-restore index 6ddaf82c..2c2be89f 100755 --- a/dom0/qvm-tools/qvm-backup-restore +++ b/dom0/qvm-tools/qvm-backup-restore @@ -47,18 +47,18 @@ fields = { "name": {"func": "('=>' if backup_collection.get_default_template_vm() is not None\ and vm.qid == backup_collection.get_default_template_vm().qid else '')\ - + ('[' if vm.is_templete() else '')\ + + ('[' if vm.is_template() else '')\ + ('{' if vm.is_netvm() else '')\ + vm.name \ - + (']' if vm.is_templete() else '')\ + + (']' if vm.is_template() else '')\ + ('}' if vm.is_netvm() else '')"}, - "type": {"func": "'Tpl' if vm.is_templete() else \ + "type": {"func": "'Tpl' if vm.is_template() else \ (' Net' if vm.is_netvm() else 'App')"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, - "template": {"func": "'n/a' if vm.is_templete() or vm.is_netvm() else\ + "template": {"func": "'n/a' if vm.is_template() or vm.is_netvm() else\ backup_collection[vm.template_vm.qid].name"}, "netvm": {"func": "'n/a' if vm.is_netvm() else\ @@ -193,11 +193,11 @@ def main(): template_vm_on_host = host_collection.get_vm_by_name (templatevm_name) # No template on the host? - if not ((template_vm_on_host is not None) and template_vm_on_host.is_templete): + if not ((template_vm_on_host is not None) and template_vm_on_host.is_template): # Maybe the (custom) template is in the backup? template_vm_on_backup = backup_collection.get_vm_by_name (templatevm_name) - if not ((template_vm_on_backup is not None) and template_vm_on_backup.is_templete): + if not ((template_vm_on_backup is not None) and template_vm_on_backup.is_template): s += " <-- No matching template on the host or in the backup found!" there_are_missing_templates = True good_to_go = False if not (options.ignore_missing) else True @@ -282,13 +282,13 @@ def main(): if vm.is_updateable(): restore_vm_file (backup_dir, vm.rootcow_img) - elif vm.is_templete(): + elif vm.is_template(): restore_vm_dir (backup_dir, vm.dir_path, qubes_templates_dir); else: print "ERROR: VM '{0}', type='{1}': unsupported VM type!".format(vm.name, vm.type) # Add templates... - for vm in [ vm for vm in vms_to_restore if vm.is_templete()]: + for vm in [ vm for vm in vms_to_restore if vm.is_template()]: print "-> Adding Template VM {0}...".format(vm.name) updateable = vm.updateable vm = host_collection.add_new_templatevm(vm.name, diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index 9ff9c6c0..b0f227ab 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -68,7 +68,7 @@ def main(): if template_vm is None: print "There is no (Templete)VM with the name '{0}'".format(options.template) exit (1) - if not template_vm.is_templete(): + if not template_vm.is_template(): print "VM '{0}' is not a TemplateVM".format(options.template) exit (1) if (options.verbose): diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index ced8f585..1bb241f0 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -30,21 +30,21 @@ fields = { "name": {"func": "('=>' if qvm_collection.get_default_template_vm() is not None\ and vm.qid == qvm_collection.get_default_template_vm().qid else '')\ - + ('[' if vm.is_templete() else '')\ + + ('[' if vm.is_template() else '')\ + ('<' if vm.is_disposablevm() else '')\ + ('{' if vm.is_netvm() else '')\ + vm.name \ - + (']' if vm.is_templete() else '')\ + + (']' if vm.is_template() else '')\ + ('>' if vm.is_disposablevm() else '')\ + ('}' if vm.is_netvm() else '')"}, - "type": {"func": "'Tpl' if vm.is_templete() else \ + "type": {"func": "'Tpl' if vm.is_template() else \ (' Fw' if vm.is_fwvm() else \ (' Net' if vm.is_netvm() else ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, - "template": {"func": "'n/a' if vm.is_templete() or vm.is_netvm() else\ + "template": {"func": "'n/a' if vm.is_template() or vm.is_netvm() else\ qvm_collection[vm.template_vm.qid].name"}, "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_fwvm() else\ @@ -142,7 +142,7 @@ def main(): # Now, the template, and all its AppVMs... for tvm in vms_list: - if tvm.is_templete(): + if tvm.is_template(): vms_to_display.append (tvm) for vm in vms_list: if (vm.is_appvm() or vm.is_disposablevm()) and vm.template_vm.qid == tvm.qid: diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index f9c129ff..743de112 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -42,7 +42,7 @@ def do_list(vm): print fmt.format ("config", vm.conf_file) if not vm.is_appvm(): print fmt.format ("root img", vm.root_img) - if vm.is_templete(): + if vm.is_template(): print fmt.format ("root COW img", vm.rootcow_img) if vm.is_appvm(): print fmt.format ("root img", vm.template_vm.root_img) @@ -115,7 +115,7 @@ def set_updateable(vms, vm, args): print "If you want to make this AppVM updateable, you must first make the Template VM nonupdateable." return False - if vm.is_templete(): + if vm.is_template(): # Make sure that all the AppVMs are non-updateable... for appvm in vm.appvms.values(): if appvm.is_updateable(): diff --git a/dom0/qvm-tools/qvm-remove b/dom0/qvm-tools/qvm-remove index 06e39af5..50beaf65 100755 --- a/dom0/qvm-tools/qvm-remove +++ b/dom0/qvm-tools/qvm-remove @@ -42,7 +42,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if vm.is_templete(): + if vm.is_template(): dependent_vms = qvm_collection.get_vms_based_on(vm.qid) if len(dependent_vms) > 0: print "The following AppVMs use '{0}' as a template:".format(vmname) diff --git a/dom0/qvm-tools/qvm-set-default-template b/dom0/qvm-tools/qvm-set-default-template index 429d34ad..98003bc0 100755 --- a/dom0/qvm-tools/qvm-set-default-template +++ b/dom0/qvm-tools/qvm-set-default-template @@ -39,7 +39,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if not vm.is_templete(): + if not vm.is_template(): print "VM '{0}' is not a TemplateVM".format(vmname) exit (1) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit index 8ccd39fe..a82249d0 100755 --- a/dom0/qvm-tools/qvm-template-commit +++ b/dom0/qvm-tools/qvm-template-commit @@ -46,7 +46,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if not vm.is_templete(): + if not vm.is_template(): print "A VM '{0}' is not template.".format(vmname) exit(1) From c1bd86142c65f9eb2176cf23c799dacab731c846 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Sun, 6 Mar 2011 17:06:45 +0100 Subject: [PATCH 39/72] NetVM and ProxyVM based on template: part 1 (core) --- dom0/qvm-core/qubes.py | 552 ++++++++++++++++++++++------------------- dom0/qvm-tools/qvm-ls | 9 +- 2 files changed, 300 insertions(+), 261 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 3ab0ba83..19a4ce18 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -71,7 +71,7 @@ default_firewall_conf_file = "firewall.xml" # do not allow to start a new AppVM if Dom0 mem was to be less than this dom0_min_memory = 700*1024*1024 -# We need this global reference, as each instance of QubesVM +# We need this global reference, as each instance of QubesVm # must be able to ask Dom0 VM about how much memory it currently has... dom0_vm = None @@ -270,8 +270,8 @@ class QubesVm(object): def is_netvm(self): return isinstance(self, QubesNetVm) - def is_fwvm(self): - return isinstance(self, QubesFirewallVm) + def is_proxyvm(self): + return isinstance(self, QubesProxyVm) def is_disposablevm(self): return isinstance(self, QubesDisposableVm) @@ -529,7 +529,7 @@ class QubesVm(object): print "--> Setting Xen Store info for the VM..." self.create_xenstore_entries(xid) - if (not self.is_netvm() or self.is_fwvm()) and self.netvm_vm is not None: + if self.netvm_vm is not None: assert self.netvm_vm is not None if verbose: print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name) @@ -555,7 +555,7 @@ class QubesVm(object): #if verbose: # print "--> Updating FirewallVMs rules..." #for vm in qvm_collection.values(): - # if vm.is_fwvm(): + # if vm.is_proxyvm(): # vm.write_iptables_xenstore_entry() if verbose: @@ -591,7 +591,7 @@ class QubesVm(object): class QubesTemplateVm(QubesVm): """ - A class that represents an TemplateVM. A child of QubesVM. + A class that represents an TemplateVM. A child of QubesVm. """ def __init__(self, **kwargs): @@ -818,59 +818,110 @@ class QubesTemplateVm(QubesVm): ) return element -class QubesServiceVm(QubesVm): +class QubesCowVm(QubesVm): """ - A class that represents a ServiceVM, e.g. NetVM. A child of QubesVM. + A class that represent a VM based on some template, i.e. doesn't have own root.img """ - def __init__(self, **kwargs): - + def __init__(self, **kwargs): if "dir_path" not in kwargs or kwargs["dir_path"] is None: - kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"] + kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"] - root_img = kwargs.pop("root_img") if "root_img" in kwargs else None - private_img = kwargs.pop("private_img") if "private_img" in kwargs else None + if "updateable" not in kwargs or kwargs["updateable"] is None: + kwargs["updateable"] = False - kwargs["updateable"] = True - super(QubesServiceVm, self).__init__(**kwargs) + private_img = kwargs.pop("private_img") + template_vm = kwargs.pop("template_vm") + + super(QubesCowVm, self).__init__(**kwargs) + qid = kwargs["qid"] dir_path = kwargs["dir_path"] - assert dir_path is not None + # Dirty hack for QubesDom0NetVm... + if not isinstance(self, QubesDom0NetVm): + assert template_vm is not None, "Missing template_vm for template based VM!" + if not template_vm.is_template(): + print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ + format(template_vm.qid) + return False - if root_img is not None and os.path.isabs(root_img): - self.root_img = root_img - else: - self.root_img = dir_path + "/" + ( - root_img if root_img is not None else default_root_img) + template_vm.appvms[qid] = self + self.template_vm = template_vm + # template based VM doesn't have its own root_img, it uses the one provided by the TemplateVM if private_img is not None and os.path.isabs(private_img): self.private_img = private_img else: self.private_img = dir_path + "/" + ( private_img if private_img is not None else default_private_img) + self.rootcow_img = dir_path + "/" + default_rootcow_img + def set_updateable(self): if self.is_updateable(): return - # ServiceVMs are standalone, we can always make it updateable - # In fact there is no point in making it unpdatebale... - # So, this is just for completncess + assert not self.is_running() - self.updateable = True + # Check if the TemaplteVM is *non* updatable... + if not self.template_vm.is_updateable(): + self.updateable = True + self.reset_cow_storage() + else: + # Temaplate VM is Updatable itself --> can't make the AppVM updateable too + # as this would cause COW-backed storage incoherency + raise QubesException ("TemaplteVM is updateable: cannot make the template based VM '{0}' updateable".format(self.name)) + + def create_config_file(self): + conf_template = open (self.template_vm.appvms_conf_file, "r") + if os.path.isfile(self.conf_file): + shutil.copy(self.conf_file, self.conf_file + ".backup") + conf_appvm = open(self.conf_file, "w") + rx_vmname = re.compile (r"%VMNAME%") + rx_vmdir = re.compile (r"%VMDIR%") + rx_template = re.compile (r"%TEMPLATEDIR%") + + for line in conf_template: + line = rx_vmname.sub (self.name, line) + line = rx_vmdir.sub (self.dir_path, line) + line = rx_template.sub (self.template_vm.dir_path, line) + conf_appvm.write(line) + + conf_template.close() + conf_appvm.close() + + def create_on_disk(self, verbose): + if dry_run: + return + + if verbose: + print "--> Creating directory: {0}".format(self.dir_path) + os.mkdir (self.dir_path) + + if verbose: + print "--> Creating the VM config file: {0}".format(self.conf_file) + + self.create_config_file() + + template_priv = self.template_vm.private_img + if verbose: + print "--> Copying the template's private image: {0}".\ + format(template_priv) + + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", template_priv, self.private_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(template_priv, self.private_img)) def get_disk_utilization_root_img(self): - return self.get_disk_usage(self.root_img) + return 0 def get_root_img_sz(self): - if not os.path.exists(self.root_img): - return 0 - - return os.path.getsize(self.root_img) + return 0 def verify_files(self): if dry_run: return - if not os.path.exists (self.dir_path): raise QubesException ( "VM directory doesn't exist: {0}".\ @@ -881,19 +932,53 @@ class QubesServiceVm(QubesVm): "VM config file doesn't exist: {0}".\ format(self.conf_file)) - if not os.path.exists (self.root_img): + if not os.path.exists (self.private_img): raise QubesException ( - "VM root image file doesn't exist: {0}".\ - format(self.root_img)) - + "VM private image file doesn't exist: {0}".\ + format(self.private_img)) return True - def create_xml_element(self): - raise NotImplementedError + def start(self, debug_console = False, verbose = False, preparing_dvm = False): + if dry_run: + return -class QubesNetVm(QubesServiceVm): + if self.is_running(): + raise QubesException("VM is already running!") + + if not self.is_updateable(): + self.reset_cow_storage() + + return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) + + def reset_cow_storage (self): + + print "--> Resetting the COW storage: {0}...".format (self.rootcow_img) + + if dry_run: + return + # this is probbaly not needed, as open (..., "w") should remove the previous file + if os.path.exists (self.rootcow_img): + os.remove (self.rootcow_img) + + + f_cow = open (self.rootcow_img, "w") + f_root = open (self.template_vm.root_img, "r") + f_root.seek(0, os.SEEK_END) + f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img + f_cow.close () + f_root.close() + + def remove_from_disk(self): + if dry_run: + return + + + subprocess.check_call ([qubes_appmenu_remove_cmd, self.name]) + shutil.rmtree (self.dir_path) + +class QubesNetVm(QubesCowVm): """ - A class that represents a NetVM. A child of QubesServiceVM. + A class that represents a NetVM. A child of QubesCowVM. """ def __init__(self, **kwargs): netid = kwargs.pop("netid") @@ -904,6 +989,9 @@ class QubesNetVm(QubesServiceVm): self.__gateway = self.netprefix + "0.1" self.__secondary_dns = self.netprefix + "255.254" + if "dir_path" not in kwargs or kwargs["dir_path"] is None: + kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"] + if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) @@ -942,28 +1030,29 @@ class QubesNetVm(QubesServiceVm): name=self.name, dir_path=self.dir_path, conf_file=self.conf_file, - root_img=self.root_img, + template_qid=str(self.template_vm.qid), + updateable=str(self.updateable), private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), ) return element -class QubesFirewallVm(QubesNetVm): +class QubesProxyVm(QubesNetVm): """ - A class that represents a FirewallVM. A child of QubesNetVM. + A class that represents a ProxyVM, ex FirewallVM. A child of QubesNetVM. """ def __init__(self, **kwargs): - super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) + super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) @property def type(self): - return "FirewallVM" + return "ProxyVM" def create_xenstore_entries(self, xid): if dry_run: return - super(QubesFirewallVm, self).create_xenstore_entries(xid) + super(QubesProxyVm, self).create_xenstore_entries(xid) self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): @@ -1052,13 +1141,14 @@ class QubesFirewallVm(QubesNetVm): def create_xml_element(self): element = xml.etree.ElementTree.Element( - "QubesFirewallVm", + "QubesProxyVm", qid=str(self.qid), netid=str(self.netid), name=self.name, dir_path=self.dir_path, conf_file=self.conf_file, - root_img=self.root_img, + template_qid=str(self.template_vm.qid), + updateable=str(self.updateable), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), @@ -1068,8 +1158,9 @@ class QubesFirewallVm(QubesNetVm): class QubesDom0NetVm(QubesNetVm): def __init__(self): super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0, - dir_path=None, root_img = None, + dir_path=None, private_img = None, + template_vm = None, label = default_template_label) if not dry_run and xend_session.session is not None: self.session_hosts = xend_session.session.xenapi.host.get_all() @@ -1140,7 +1231,7 @@ class QubesDom0NetVm(QubesNetVm): class QubesDisposableVm(QubesVm): """ - A class that represents an DisposableVM. A child of QubesVM. + A class that represents an DisposableVM. A child of QubesVm. """ def __init__(self, **kwargs): @@ -1175,43 +1266,15 @@ class QubesDisposableVm(QubesVm): return True -class QubesAppVm(QubesVm): +class QubesAppVm(QubesCowVm): """ - A class that represents an AppVM. A child of QubesVM. + A class that represents an AppVM. A child of QubesVm. """ def __init__(self, **kwargs): - if "dir_path" not in kwargs or kwargs["dir_path"] is None: - kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"] - - if "updateable" not in kwargs or kwargs["updateable"] is None: - kwargs["updateable"] = False - - private_img = kwargs.pop("private_img") - template_vm = kwargs.pop("template_vm") - - super(QubesAppVm, self).__init__(**kwargs) - qid = kwargs["qid"] dir_path = kwargs["dir_path"] - assert template_vm is not None, "Missing template_vm for AppVM!" - if not template_vm.is_template(): - print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ - format(new_vm.template_vm.qid) - return False - - self.template_vm = template_vm - template_vm.appvms[qid] = self - - # AppVM doesn't have its own root_img, it uses the one provided by the TemplateVM - if private_img is not None and os.path.isabs(private_img): - self.private_img = private_img - else: - self.private_img = dir_path + "/" + ( - private_img if private_img is not None else default_private_img) - - self.rootcow_img = dir_path + "/" + default_rootcow_img self.swapcow_img = dir_path + "/" + default_swapcow_img if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None: @@ -1225,62 +1288,15 @@ class QubesAppVm(QubesVm): return "AppVM" def set_updateable(self): - if self.is_updateable(): - return - assert not self.is_running() - # Check if the TemaplteVM is *non* updatable... - if not self.template_vm.is_updateable(): - self.updateable = True - self.reset_cow_storage() - self.reset_swap_cow_storage() - else: - # Temaplate VM is Updatable itself --> can't make the AppVM updateable too - # as this would cause COW-backed storage incoherency - raise QubesException ("TemaplteVM is updateable: cannot make the AppVM '{0}' updateable".format(self.name)) - - def create_config_file(self): - conf_template = open (self.template_vm.appvms_conf_file, "r") - if os.path.isfile(self.conf_file): - shutil.copy(self.conf_file, self.conf_file + ".backup") - conf_appvm = open(self.conf_file, "w") - rx_vmname = re.compile (r"%VMNAME%") - rx_vmdir = re.compile (r"%VMDIR%") - rx_template = re.compile (r"%TEMPLATEDIR%") - - for line in conf_template: - line = rx_vmname.sub (self.name, line) - line = rx_vmdir.sub (self.dir_path, line) - line = rx_template.sub (self.template_vm.dir_path, line) - conf_appvm.write(line) - - conf_template.close() - conf_appvm.close() + super(QubesAppVm, self).set_updateable() + self.reset_swap_cow_storage() def create_on_disk(self, verbose): if dry_run: return - - if verbose: - print "--> Creating directory: {0}".format(self.dir_path) - os.mkdir (self.dir_path) - - if verbose: - print "--> Creating the VM config file: {0}".format(self.conf_file) - - self.create_config_file() - - template_priv = self.template_vm.private_img - if verbose: - print "--> Copying the template's private image: {0}".\ - format(template_priv) - - # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", template_priv, self.private_img]) - if retcode != 0: - raise IOError ("Error while copying {0} to {1}".\ - format(template_priv, self.private_img)) + super(QubesAppVm, self).create_on_disk(verbose) if verbose: print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path) @@ -1363,35 +1379,6 @@ class QubesAppVm(QubesVm): return conf - def get_disk_utilization_root_img(self): - return 0 - - def get_root_img_sz(self): - return 0 - - - def verify_files(self): - if dry_run: - return - - - if not os.path.exists (self.dir_path): - raise QubesException ( - "VM directory doesn't exist: {0}".\ - format(self.dir_path)) - - if not os.path.exists (self.conf_file): - raise QubesException ( - "VM config file doesn't exist: {0}".\ - format(self.conf_file)) - - if not os.path.exists (self.private_img): - raise QubesException ( - "VM private image file doesn't exist: {0}".\ - format(self.private_img)) - return True - - def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesAppVm", @@ -1412,33 +1399,10 @@ class QubesAppVm(QubesVm): if dry_run: return - if self.is_running(): - raise QubesException("VM is already running!") - - if not self.is_updateable(): - self.reset_cow_storage() self.reset_swap_cow_storage() return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - def reset_cow_storage (self): - - print "--> Resetting the COW storage: {0}...".format (self.rootcow_img) - - if dry_run: - return - # this is probbaly not needed, as open (..., "w") should remove the previous file - if os.path.exists (self.rootcow_img): - os.remove (self.rootcow_img) - - - f_cow = open (self.rootcow_img, "w") - f_root = open (self.template_vm.root_img, "r") - f_root.seek(0, os.SEEK_END) - f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img - f_cow.close () - f_root.close() - def reset_swap_cow_storage (self): print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img) if os.path.exists (self.swapcow_img): @@ -1448,16 +1412,6 @@ class QubesAppVm(QubesVm): f_swap_cow.truncate (swap_cow_sz) f_swap_cow.close() - def remove_from_disk(self): - if dry_run: - return - - - subprocess.check_call ([qubes_appmenu_remove_cmd, self.name]) - shutil.rmtree (self.dir_path) - - - class QubesVmCollection(dict): """ A collection of Qubes VMs indexed by Qubes id (qid) @@ -1551,13 +1505,13 @@ class QubesVmCollection(dict): return vm - def add_new_netvm(self, name, + def add_new_netvm(self, name, template_vm, dir_path = None, conf_file = None, root_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesNetVm (qid=qid, name=name, + vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm, netid=netid, dir_path=dir_path, conf_file=conf_file, root_img=root_img) @@ -1571,13 +1525,13 @@ class QubesVmCollection(dict): return vm - def add_new_fwvm(self, name, + def add_new_proxyvm(self, name, template_vm, dir_path = None, conf_file = None, root_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesFirewallVm (qid=qid, name=name, + vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm, netid=netid, dir_path=dir_path, conf_file=conf_file, netvm_vm = self.get_default_fw_netvm_vm(), @@ -1603,7 +1557,7 @@ class QubesVmCollection(dict): return self[self.default_template_qid] def set_default_netvm_vm(self, vm): - assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name) + assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name) self.default_netvm_qid = vm.qid def get_default_netvm_vm(self): @@ -1613,7 +1567,7 @@ class QubesVmCollection(dict): return self[self.default_netvm_qid] def set_default_fw_netvm_vm(self, vm): - assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name) + assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name) self.default_fw_netvm_qid = vm.qid def get_default_fw_netvm_vm(self): @@ -1754,65 +1708,6 @@ class QubesVmCollection(dict): if default_netvm != "None" else None #assert self.default_netvm_qid is not None - # Read in the NetVMs first, because a reference to NetVM - # is needed to create all other VMs - for element in tree.findall("QubesNetVm"): - try: - kwargs = {} - attr_list = ("qid", "netid", "name", "dir_path", "conf_file", - "private_img", "root_img", - ) - - for attribute in attr_list: - kwargs[attribute] = element.get(attribute) - - kwargs["qid"] = int(kwargs["qid"]) - kwargs["netid"] = int(kwargs["netid"]) - - vm = QubesNetVm(**kwargs) - self[vm.qid] = vm - - except (ValueError, LookupError) as err: - print("{0}: import error (QubesNetVM) {1}".format( - os.path.basename(sys.argv[0]), err)) - return False - - # Next read in the FirewallVMs, because they may be referenced - # by other VMs - for element in tree.findall("QubesFirewallVm"): - try: - kwargs = {} - attr_list = ("qid", "netid", "name", "dir_path", "conf_file", - "private_img", "root_img", "netvm_qid") - - for attribute in attr_list: - kwargs[attribute] = element.get(attribute) - - kwargs["qid"] = int(kwargs["qid"]) - kwargs["netid"] = int(kwargs["netid"]) - - if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: - netvm_vm = None - kwargs.pop("netvm_qid") - else: - netvm_qid = int(kwargs.pop("netvm_qid")) - if netvm_qid not in self: - netvm_vm = None - else: - netvm_vm = self[netvm_qid] - - kwargs["netvm_vm"] = netvm_vm - - vm = QubesFirewallVm(**kwargs) - self[vm.qid] = vm - - except (ValueError, LookupError) as err: - print("{0}: import error (QubesFirewallVM) {1}".format( - os.path.basename(sys.argv[0]), err)) - return False - - - self.default_template_qid # Then, read in the TemplateVMs, because a reference to template VM # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): @@ -1860,6 +1755,149 @@ class QubesVmCollection(dict): os.path.basename(sys.argv[0]), err)) return False + # Read in the NetVMs first, because a reference to NetVM + # is needed to create all other VMs + for element in tree.findall("QubesNetVm"): + try: + kwargs = {} + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", + "private_img", "root_img", "template_qid", + ) + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + kwargs["template_qid"] = int(kwargs["template_qid"]) + if kwargs["updateable"] is not None: + kwargs["updateable"] = True if kwargs["updateable"] == "True" else False + + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + + kwargs["template_vm"] = template_vm + kwargs["netid"] = int(kwargs["netid"]) + + vm = QubesNetVm(**kwargs) + self[vm.qid] = vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesNetVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + + # Next read in the ProxyVMs, because they may be referenced + # by other VMs + for element in tree.findall("QubesProxyVm"): + try: + kwargs = {} + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", + "private_img", "root_img", "netvm_qid", "template_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + kwargs["template_qid"] = int(kwargs["template_qid"]) + if kwargs["updateable"] is not None: + kwargs["updateable"] = True if kwargs["updateable"] == "True" else False + + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + + kwargs["template_vm"] = template_vm + kwargs["netid"] = int(kwargs["netid"]) + + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + kwargs["netvm_vm"] = netvm_vm + + vm = QubesProxyVm(**kwargs) + self[vm.qid] = vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesProxyVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + + # After importing all NetVMs and ProxyVMs, set netvm references + # 1. For TemplateVMs + for element in tree.findall("QubesTemplateVm"): + try: + + kwargs = {} + attr_list = ("qid", "uses_default_netvm", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + vm = self[int(kwargs["qid"])] + + if "uses_default_netvm" not in kwargs: + vm.uses_default_netvm = True + else: + vm.uses_default_netvm = True if kwargs["uses_default_netvm"] == "True" else False + if vm.uses_default_netvm is True: + netvm_vm = self.get_default_netvm_vm() + kwargs.pop("netvm_qid") + else: + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + vm.netvm_vm = netvm_vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesTemplateVm): {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + + # 2. For PoxyVMs + for element in tree.findall("QubesProxyVm"): + try: + kwargs = {} + attr_list = ("qid", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + vm = self[int(kwargs["qid"])] + + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + vm.netvm_vm = netvm_vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesProxyVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + # Finally, read in the AppVMs for element in tree.findall("QubesAppVm"): try: @@ -2011,4 +2049,4 @@ class QubesDaemonPidfile(object): self.remove_pidfile() -# vim:sw=4:et: +# vim:sw=4:et:ts=4: diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 1bb241f0..7516af1e 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -39,15 +39,16 @@ fields = { + ('}' if vm.is_netvm() else '')"}, "type": {"func": "'Tpl' if vm.is_template() else \ - (' Fw' if vm.is_fwvm() else \ + (' Prox' if vm.is_proxyvm() else \ (' Net' if vm.is_netvm() else ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, - "template": {"func": "'n/a' if vm.is_template() or vm.is_netvm() else\ - qvm_collection[vm.template_vm.qid].name"}, + "template": {"func": "'n/a' if vm.is_template() else\ + ('None' if vm.template_vm is None else\ + qvm_collection[vm.template_vm.qid].name)"}, - "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_fwvm() else\ + "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_proxyvm() else\ ('*' if vm.uses_default_netvm else '') +\ qvm_collection[vm.netvm_vm.qid].name\ if vm.netvm_vm is not None else '-'"}, From e35fccef35f6b51c9caf1f09f65ecd22a8cb5484 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 9 Mar 2011 15:24:54 +0100 Subject: [PATCH 40/72] Fix AppVm constructior --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 19a4ce18..3e0611c5 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1273,7 +1273,7 @@ class QubesAppVm(QubesCowVm): def __init__(self, **kwargs): super(QubesAppVm, self).__init__(**kwargs) - dir_path = kwargs["dir_path"] + dir_path = self.dir_path self.swapcow_img = dir_path + "/" + default_swapcow_img From f05c244321fab1c61049beb1afe1aea9d240c8ff Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 17:27:35 +0100 Subject: [PATCH 41/72] Added AppVM version of xenstore-watch. --- appvm/Makefile | 7 +++++-- appvm/xenstore-watch.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 appvm/xenstore-watch.c diff --git a/appvm/Makefile b/appvm/Makefile index 6bb1dea6..0ef375a7 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,11 +1,14 @@ CC=gcc CFLAGS=-Wall -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o $(CC) -o qubes_add_pendrive_script qubes_add_pendrive_script.o -lxenstore qvm-open-in-dvm: qvm-open-in-dvm.o $(CC) -o qvm-open-in-dvm qvm-open-in-dvm.o -lxenstore +xenstore-watch: xenstore-watch.o + $(CC) -o xenstore-watch xenstore-watch.o -lxenstore + clean: - rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~ + rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch *.o *~ diff --git a/appvm/xenstore-watch.c b/appvm/xenstore-watch.c new file mode 100644 index 00000000..497ed294 --- /dev/null +++ b/appvm/xenstore-watch.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +main(int argc, char **argv) +{ + struct xs_handle *xs; + unsigned int count; + char **vec; + char dummy; + if (argc != 2) { + fprintf(stderr, "usage: %s xenstore_path\n", argv[0]); + exit(1); + } + xs = xs_domain_open(); + if (!xs) { + perror("xs_domain_open"); + exit(1); + } + if (!xs_watch(xs, argv[1], &dummy)) { + perror("xs_watch"); + exit(1); + } + vec = xs_read_watch(xs, &count); + free(vec); + vec = xs_read_watch(xs, &count); + free(vec); +} From ca81f0103d0d9741f6c7b0b232f14cdde75bc111 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 17:38:34 +0100 Subject: [PATCH 42/72] Update firewall rules on VM start --- dom0/qvm-core/qubes.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index adde77ec..04ef5616 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -552,11 +552,16 @@ class QubesVm(object): self.force_shutdown() raise OSError ("ERROR: Cannot attach to network backend!") - #if verbose: - # print "--> Updating FirewallVMs rules..." - #for vm in qvm_collection.values(): - # if vm.is_fwvm(): - # vm.write_iptables_xenstore_entry() + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + if verbose: + print "--> Updating FirewallVMs rules..." + for vm in qvm_collection.values(): + if vm.is_fwvm(): + vm.write_iptables_xenstore_entry() if verbose: print "--> Starting the VM..." From fd8ecca9bdb2369a87ba53e3bdb72c34fd28adac Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 17:39:09 +0100 Subject: [PATCH 43/72] Create qubes_iptables_error xenstore file in FwVM and set its permissions. --- dom0/qvm-core/qubes.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 04ef5616..d6d9a28f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -943,6 +943,14 @@ class QubesFirewallVm(QubesNetVm): return super(QubesFirewallVm, self).create_xenstore_entries(xid) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_iptables_error".format(xid), + ""]) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-chmod", + "/local/domain/{0}/qubes_iptables_error".format(xid), + "r{0} w{1}".format(xid,xid)]) self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): From 6ad91617a79e9a2d61a72274845f08ee275aadc3 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 18:07:22 +0100 Subject: [PATCH 44/72] Store the state of FwVM rules --- dom0/qvm-core/qubes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index d6d9a28f..d28dbeeb 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -933,6 +933,7 @@ class QubesFirewallVm(QubesNetVm): """ def __init__(self, **kwargs): super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) + self.rules_applied = None @property def type(self): @@ -1021,6 +1022,7 @@ class QubesFirewallVm(QubesNetVm): iptables += "COMMIT" + self.rules_applied = None return subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_iptables".format(self.get_xid()), From 87ff30fe2626515d1e00df89f8433ae6fea66114 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 19:47:08 +0100 Subject: [PATCH 45/72] Fixed xenstore-chmod call syntax --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index d28dbeeb..93934f5e 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -951,7 +951,7 @@ class QubesFirewallVm(QubesNetVm): retcode = subprocess.check_call ([ "/usr/bin/xenstore-chmod", "/local/domain/{0}/qubes_iptables_error".format(xid), - "r{0} w{1}".format(xid,xid)]) + "n0", "r{0}".format(xid), "w{0}".format(xid)]) self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): From 58a4b4c82bc53f9727b8e414325f62f5a22c04f9 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 19:47:12 +0100 Subject: [PATCH 46/72] Implemented qubes_netvm_external_ip feature. --- dom0/qvm-core/qubes.py | 47 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 93934f5e..e7c75b3d 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -883,6 +883,8 @@ class QubesNetVm(QubesServiceVm): self.__gateway = self.netprefix + "0.1" self.__secondary_dns = self.netprefix + "255.254" + self.__external_ip_allowed_xids = set() + if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) @@ -913,6 +915,42 @@ class QubesNetVm(QubesServiceVm): assert hi >= 0 and hi <= 254 and lo >= 2 and lo <= 254, "Wrong IP address for VM" return self.netprefix + "{0}.{1}".format(hi,lo) + def create_xenstore_entries(self, xid): + if dry_run: + return + + super(QubesNetVm, self).create_xenstore_entries(xid) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_netvm_external_ip".format(xid), + ""]) + self.update_external_ip_permissions() + + def update_external_ip_permissions(self): + xid = self.get_xid() + command = [ + "/usr/bin/xenstore-chmod", + "/local/domain/{0}/qubes_netvm_external_ip".format(xid) + ] + + command.append("r{0}".format(xid,xid)) + command.append("w{0}".format(xid,xid)) + + for id in self.__external_ip_allowed_xids: + command.append("r{0}".format(id)) + + return subprocess.check_call(command) + + 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() + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesNetVm", @@ -934,11 +972,18 @@ class QubesFirewallVm(QubesNetVm): def __init__(self, **kwargs): super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) self.rules_applied = None + self.netvm_vm.add_external_ip_permission(self.get_xid()) @property def type(self): return "FirewallVM" + def force_shutdown(self): + if dry_run: + return + self.netvm_vm.remove_external_ip_permission(self.get_xid()) + super(QubesFirewallVm, self).force_shutdown() + def create_xenstore_entries(self, xid): if dry_run: return @@ -951,7 +996,7 @@ class QubesFirewallVm(QubesNetVm): retcode = subprocess.check_call ([ "/usr/bin/xenstore-chmod", "/local/domain/{0}/qubes_iptables_error".format(xid), - "n0", "r{0}".format(xid), "w{0}".format(xid)]) + "r{0}".format(xid), "w{0}".format(xid)]) self.write_iptables_xenstore_entry() def write_iptables_xenstore_entry(self): From afbdfe8ae4f2aa7f594307b6fe15675e3e39b45f Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 20:38:14 +0100 Subject: [PATCH 47/72] Store netvm domid in FwVM. --- dom0/qvm-core/qubes.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index e7c75b3d..7b3e48d7 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -989,6 +989,10 @@ class QubesFirewallVm(QubesNetVm): return super(QubesFirewallVm, self).create_xenstore_entries(xid) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_netvm_domid".format(xid), + "{0}".format(self.netvm_vm.get_xid())]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_iptables_error".format(xid), From a71b846ee2855e12be6df26aea98b66e4937b861 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Wed, 9 Mar 2011 20:50:13 +0100 Subject: [PATCH 48/72] Added FirewallVM related VM scripts --- fwvm/bin/qubes_firewall | 33 +++++++++++++++++++ fwvm/init.d/qubes_core | 67 ++++++++++++++++++++++++++++++++++++++ fwvm/init.d/qubes_firewall | 42 ++++++++++++++++++++++++ netvm/30-qubes_external_ip | 8 +++++ rpm_spec/core-netvm.spec | 1 + 5 files changed, 151 insertions(+) create mode 100755 fwvm/bin/qubes_firewall create mode 100755 fwvm/init.d/qubes_core create mode 100755 fwvm/init.d/qubes_firewall create mode 100755 netvm/30-qubes_external_ip diff --git a/fwvm/bin/qubes_firewall b/fwvm/bin/qubes_firewall new file mode 100755 index 00000000..6f1cc267 --- /dev/null +++ b/fwvm/bin/qubes_firewall @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +PIDFILE=/var/run/qubes/qubes_firewall.pid +XENSTORE_IPTABLES=qubes_iptables +XENSTORE_ERROR=qubes_iptables_error +OLD_RULES="" + +# PIDfile handling +[[ -e $PIDFILE ]] && kill -s 0 $(<$PIDFILE) 2>/dev/null && exit 0 +echo $$ >$PIDFILE + +trap 'exit 0' SIGTERM + +while true; do + RULES=$(/usr/bin/xenstore-read $XENSTORE_IPTABLES) + + if [[ "$RULES" != "$OLD_RULES" ]]; then + IPTABLES_SAVE=$(/sbin/iptables-save | sed '/^\*filter/,/^COMMIT/d') + OUT=`echo -e "$RULES\n$IPTABLES_SAVE" | /sbin/iptables-restore 2>&1 || :` + /usr/bin/xenstore-write $XENSTORE_ERROR "$OUT" + + if [[ -z "$OUT" ]]; then + # If OK save it for later + /sbin/service iptables save >/dev/null + fi + + OLD_RULES="$RULES" + fi + + # Wait for changes in xenstore file + /usr/bin/xenstore-watch $XENSTORE_IPTABLES +done diff --git a/fwvm/init.d/qubes_core b/fwvm/init.d/qubes_core new file mode 100755 index 00000000..d6bcac28 --- /dev/null +++ b/fwvm/init.d/qubes_core @@ -0,0 +1,67 @@ +#!/bin/sh +# +# chkconfig: 345 90 90 +# description: Executes Qubes core scripts at VM boot +# +# Source function library. +. /etc/rc.d/init.d/functions + +start() +{ + echo -n $"Executing Qubes Core scripts FirewallVM:" + + if ! [ -x /usr/bin/xenstore-read ] ; then + echo "ERROR: /usr/bin/xenstore-read not found!" + exit 1 + fi + + name=$(/usr/bin/xenstore-read name) + hostname $name + + # Setup gateway for all the VMs this netVM is serviceing... + modprobe netbk + gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) + netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) + network=$(/usr/bin/xenstore-read qubes_netvm_network) + secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) + echo "NS1=$gateway" > /var/run/qubes/qubes_ns + echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns + /usr/lib/qubes/qubes_setup_dnat_to_ns + echo "1" > /proc/sys/net/ipv4/ip_forward + + # Now setup "AppVM" part of FirewallVM + ip=$(/usr/bin/xenstore-read qubes_ip) + netmask=$(/usr/bin/xenstore-read qubes_netmask) + gateway=$(/usr/bin/xenstore-read qubes_gateway) + secondary_dns=$(/usr/bin/xenstore-read qubes_secondary_dns) + if [ x$ip != x ]; then + /sbin/ifconfig eth0 $ip netmask 255.255.255.255 up + /sbin/route add default dev eth0 + echo "nameserver $gateway" > /etc/resolv.conf + echo "nameserver $secondary_dns" >> /etc/resolv.conf + fi + + success + echo "" + return 0 +} + +stop() +{ + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/fwvm/init.d/qubes_firewall b/fwvm/init.d/qubes_firewall new file mode 100755 index 00000000..f970734f --- /dev/null +++ b/fwvm/init.d/qubes_firewall @@ -0,0 +1,42 @@ +#!/bin/sh +# +# chkconfig: 345 91 91 +# description: Starts Qubes Firewall monitor +# +# Source function library. +. /etc/rc.d/init.d/functions + +PIDFILE=/var/run/qubes/qubes_firewall.pid + +start() +{ + echo -n $"Starting Qubes Firewall monitor:" + /sbin/ethtool -K eth0 sg off + /usr/bin/qubes_firewall & + success + echo "" + return 0 +} + +stop() +{ + echo -n "Stopping Qubes Firewall monitor:" + kill $(cat $PIDFILE) 2>/dev/null && success || failure + echo "" + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/netvm/30-qubes_external_ip b/netvm/30-qubes_external_ip new file mode 100755 index 00000000..66ae5269 --- /dev/null +++ b/netvm/30-qubes_external_ip @@ -0,0 +1,8 @@ +#!/bin/sh +if [ x$2 == xup ]; then + INET=$(/sbin/ip addr show dev $1 | /bin/grep inet) + /usr/bin/xenstore-write qubes_netvm_external_ip "$INET" +fi +if [ x$2 == xdown ]; then + /usr/bin/xenstore-write qubes_netvm_external_ip "" +fi diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 83d88c29..47a04ff0 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -67,6 +67,7 @@ mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d ln -s /usr/lib/qubes/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes_setup_dnat_to_ns.sh mkdir -p $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ cp ../common/qubes_nmhook $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ +cp ../netvm/30-qubes_external_ip $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d cp ../netvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d mkdir -p $RPM_BUILD_ROOT/sbin From ae2d170a7ead6175021b6d0ca9c999b8f8f09945 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Thu, 10 Mar 2011 13:38:49 +0100 Subject: [PATCH 49/72] Fixed external_ip permissions setting and netvm_domid entry handling. --- dom0/qvm-core/qubes.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 7b3e48d7..6a19ea5f 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -924,10 +924,14 @@ class QubesNetVm(QubesServiceVm): "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netvm_external_ip".format(xid), ""]) - self.update_external_ip_permissions() + self.update_external_ip_permissions(xid) + + def update_external_ip_permissions(self, xid = -1): + if xid < 0: + xid = self.get_xid() + if xid < 0: + return - def update_external_ip_permissions(self): - xid = self.get_xid() command = [ "/usr/bin/xenstore-chmod", "/local/domain/{0}/qubes_netvm_external_ip".format(xid) @@ -972,12 +976,19 @@ class QubesFirewallVm(QubesNetVm): def __init__(self, **kwargs): super(QubesFirewallVm, self).__init__(uses_default_netvm=False, **kwargs) self.rules_applied = None - self.netvm_vm.add_external_ip_permission(self.get_xid()) @property def type(self): return "FirewallVM" + def start(self, debug_console = False, verbose = False, preparing_dvm = False): + if dry_run: + return + retcode = super(QubesFirewallVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) + self.netvm_vm.add_external_ip_permission(self.get_xid()) + self.write_netvm_domid_entry() + return retcode + def force_shutdown(self): if dry_run: return @@ -989,10 +1000,6 @@ class QubesFirewallVm(QubesNetVm): return super(QubesFirewallVm, self).create_xenstore_entries(xid) - retcode = subprocess.check_call ([ - "/usr/bin/xenstore-write", - "/local/domain/{0}/qubes_netvm_domid".format(xid), - "{0}".format(self.netvm_vm.get_xid())]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_iptables_error".format(xid), @@ -1003,6 +1010,15 @@ class QubesFirewallVm(QubesNetVm): "r{0}".format(xid), "w{0}".format(xid)]) self.write_iptables_xenstore_entry() + def write_netvm_domid_entry(self, xid = -1): + if xid < 0: + xid = self.get_xid() + + return subprocess.check_call ([ + "/usr/bin/xenstore-write", "--", + "/local/domain/{0}/qubes_netvm_domid".format(xid), + "{0}".format(self.netvm_vm.get_xid())]) + def write_iptables_xenstore_entry(self): iptables = "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime()) iptables += "*filter\n" @@ -1071,6 +1087,8 @@ class QubesFirewallVm(QubesNetVm): iptables += "COMMIT" + self.write_netvm_domid_entry() + self.rules_applied = None return subprocess.check_call ([ "/usr/bin/xenstore-write", From 7e29c397aa1f753ba567c07a07261425bdb76a4f Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 10 Mar 2011 16:09:37 +0100 Subject: [PATCH 50/72] Add 30-qubes_external_ip to netvm.spec --- rpm_spec/core-netvm.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 47a04ff0..d03591a1 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -180,6 +180,7 @@ rm -rf $RPM_BUILD_ROOT /usr/lib/qubes/qubes_fix_nm_conf.sh /etc/dhclient.d/qubes_setup_dnat_to_ns.sh /etc/NetworkManager/dispatcher.d/qubes_nmhook +/etc/NetworkManager/dispatcher.d/30-qubes_external_ip /etc/yum.repos.d/qubes.repo /sbin/qubes_serial_login /etc/xen/scripts/vif-route-qubes From 9895665f2c996fc90fbbe6399b1bd518024c0c5c Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 10 Mar 2011 16:16:39 +0100 Subject: [PATCH 51/72] fwvm -> proxyvm rename fix --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1852d322..aa8a72a0 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -560,7 +560,7 @@ class QubesVm(object): if verbose: print "--> Updating FirewallVMs rules..." for vm in qvm_collection.values(): - if vm.is_fwvm(): + if vm.is_proxyvm(): vm.write_iptables_xenstore_entry() if verbose: From 980efbc6c6e6e9c22d54ca6af6ead266e88315fb Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 10 Mar 2011 16:23:45 +0100 Subject: [PATCH 52/72] .gitignore --- appvm/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/appvm/.gitignore b/appvm/.gitignore index edd6d099..23680333 100644 --- a/appvm/.gitignore +++ b/appvm/.gitignore @@ -1,3 +1,4 @@ qubes_add_pendrive_script qubes_penctl qvm-open-in-dvm +xenstore-watch From 4c14652245a0d7af932a4feeb544b18c6064ece6 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Thu, 10 Mar 2011 17:24:56 +0100 Subject: [PATCH 53/72] Add preparing_dvm param to TemplateVM.start (to start it as any other VM) --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index aa8a72a0..45a44308 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -774,7 +774,7 @@ class QubesTemplateVm(QubesVm): return True - def start(self, debug_console = False, verbose = False): + def start(self, debug_console = False, verbose = False, preparing_dvm=False): if dry_run: return From c7a832a279eb07e23e2385d372a5ca1b24257843 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 01:38:04 +0100 Subject: [PATCH 54/72] NetVM, AppVM, ProxyVM from single template - VM side Modify VM packages to: - do not conflicts - starts services if its VM type need it Added core-proxyvm (firewall) and core-commonvm (common parts) packages. --- Makefile | 6 ++ {appvm => common}/fstab | 0 {appvm => common}/qubes_core | 31 -------- dom0/qvm-core/qubes.py | 2 +- fwvm/init.d/qubes_core | 67 ------------------ fwvm/init.d/qubes_firewall | 42 ----------- netvm/qubes_core | 54 -------------- netvm/qubes_core_netvm | 58 +++++++++++++++ {fwvm => proxyvm}/bin/qubes_firewall | 0 proxyvm/init.d/qubes_firewall | 48 +++++++++++++ rpm_spec/core-appvm.spec | 98 ++------------------------ rpm_spec/core-dom0.spec | 2 + rpm_spec/core-netvm.spec | 101 ++------------------------- 13 files changed, 127 insertions(+), 382 deletions(-) rename {appvm => common}/fstab (100%) rename {appvm => common}/qubes_core (61%) delete mode 100755 fwvm/init.d/qubes_core delete mode 100755 fwvm/init.d/qubes_firewall delete mode 100755 netvm/qubes_core create mode 100755 netvm/qubes_core_netvm rename {fwvm => proxyvm}/bin/qubes_firewall (100%) create mode 100755 proxyvm/init.d/qubes_firewall diff --git a/Makefile b/Makefile index 01085b28..b38c5f5c 100644 --- a/Makefile +++ b/Makefile @@ -5,20 +5,26 @@ help: @echo "make update-repo-testing -- same, but to -testing repo" rpms: + rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-commonvm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-appvm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-netvm.spec + rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-proxyvm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-dom0.spec rpm --addsign $(RPMS_DIR)/x86_64/*.rpm update-repo: ln -f $(RPMS_DIR)/x86_64/qubes-core-dom0-*.rpm ../yum/r1/dom0/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-appvm-*.rpm ../yum/r1/appvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-commonvm-*.rpm ../yum/r1/netvm/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-netvm-*.rpm ../yum/r1/netvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-proxyvm-*.rpm ../yum/r1/netvm/rpm/ update-repo-testing: ln -f $(RPMS_DIR)/x86_64/qubes-core-dom0-*.rpm ../yum/r1-testing/dom0/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-appvm-*.rpm ../yum/r1-testing/appvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-commonvm-*.rpm ../yum/r1-testing/netvm/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-netvm-*.rpm ../yum/r1-testing/netvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-proxyvm-*.rpm ../yum/r1-testing/netvm/rpm/ diff --git a/appvm/fstab b/common/fstab similarity index 100% rename from appvm/fstab rename to common/fstab diff --git a/appvm/qubes_core b/common/qubes_core similarity index 61% rename from appvm/qubes_core rename to common/qubes_core index 714727a2..26fe514d 100755 --- a/appvm/qubes_core +++ b/common/qubes_core @@ -6,18 +6,6 @@ # Source function library. . /etc/rc.d/init.d/functions -possibly_run_save_script() -{ - ENCODED_SCRIPT=$(xenstore-read qubes_save_script) - if [ -z "$ENCODED_SCRIPT" ] ; then return ; fi - echo $ENCODED_SCRIPT|perl -e 'use MIME::Base64 qw(decode_base64); local($/) = undef;print decode_base64()' >/tmp/qubes_save_script - chmod 755 /tmp/qubes_save_script - Xorg -config /etc/X11/xorg-preload-apps.conf :0 & - sleep 2 - DISPLAY=:0 su - user -c /tmp/qubes_save_script - killall Xorg -} - start() { echo -n $"Executing Qubes Core scripts:" @@ -26,21 +14,6 @@ start() echo "ERROR: /usr/bin/xenstore-read not found!" exit 1 fi - if xenstore-read qubes_save_request 2>/dev/null ; then - ln -sf /home_volatile /home - possibly_run_save_script - touch /etc/this_is_dvm - dmesg -c >/dev/null - free | grep Mem: | - (read a b c d ; xenstore-write device/qubes_used_mem $c) - # we're still running in DispVM template - echo "Waiting for save/restore..." - # ... wait until qubes_restore.c (in Dom0) recreates VM-specific keys - while ! xenstore-read qubes_restore_complete 2>/dev/null ; do - usleep 10 - done - echo Back to life. - fi name=$(/usr/bin/xenstore-read name) if ! [ -f /etc/this_is_dvm ] ; then @@ -81,10 +54,6 @@ start() fi fi - MEM_CHANGE_THRESHOLD_KB=30000 - MEMINFO_DELAY_USEC=100000 - /usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC & - [ -x /rw/config/rc.local ] && /rw/config/rc.local success echo "" diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 45a44308..270d0049 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -195,7 +195,7 @@ class QubesVm(object): self.uses_default_netvm = uses_default_netvm self.netvm_vm = netvm_vm - # We use it in remove from disk to avoid removing rpm files (for templates and netvms) + # We use it in remove from disk to avoid removing rpm files (for templates) self.installed_by_rpm = installed_by_rpm self.updateable = updateable diff --git a/fwvm/init.d/qubes_core b/fwvm/init.d/qubes_core deleted file mode 100755 index d6bcac28..00000000 --- a/fwvm/init.d/qubes_core +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/sh -# -# chkconfig: 345 90 90 -# description: Executes Qubes core scripts at VM boot -# -# Source function library. -. /etc/rc.d/init.d/functions - -start() -{ - echo -n $"Executing Qubes Core scripts FirewallVM:" - - if ! [ -x /usr/bin/xenstore-read ] ; then - echo "ERROR: /usr/bin/xenstore-read not found!" - exit 1 - fi - - name=$(/usr/bin/xenstore-read name) - hostname $name - - # Setup gateway for all the VMs this netVM is serviceing... - modprobe netbk - gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) - netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) - network=$(/usr/bin/xenstore-read qubes_netvm_network) - secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) - echo "NS1=$gateway" > /var/run/qubes/qubes_ns - echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns - /usr/lib/qubes/qubes_setup_dnat_to_ns - echo "1" > /proc/sys/net/ipv4/ip_forward - - # Now setup "AppVM" part of FirewallVM - ip=$(/usr/bin/xenstore-read qubes_ip) - netmask=$(/usr/bin/xenstore-read qubes_netmask) - gateway=$(/usr/bin/xenstore-read qubes_gateway) - secondary_dns=$(/usr/bin/xenstore-read qubes_secondary_dns) - if [ x$ip != x ]; then - /sbin/ifconfig eth0 $ip netmask 255.255.255.255 up - /sbin/route add default dev eth0 - echo "nameserver $gateway" > /etc/resolv.conf - echo "nameserver $secondary_dns" >> /etc/resolv.conf - fi - - success - echo "" - return 0 -} - -stop() -{ - return 0 -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - *) - echo $"Usage: $0 {start|stop}" - exit 3 - ;; -esac - -exit $RETVAL diff --git a/fwvm/init.d/qubes_firewall b/fwvm/init.d/qubes_firewall deleted file mode 100755 index f970734f..00000000 --- a/fwvm/init.d/qubes_firewall +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -# -# chkconfig: 345 91 91 -# description: Starts Qubes Firewall monitor -# -# Source function library. -. /etc/rc.d/init.d/functions - -PIDFILE=/var/run/qubes/qubes_firewall.pid - -start() -{ - echo -n $"Starting Qubes Firewall monitor:" - /sbin/ethtool -K eth0 sg off - /usr/bin/qubes_firewall & - success - echo "" - return 0 -} - -stop() -{ - echo -n "Stopping Qubes Firewall monitor:" - kill $(cat $PIDFILE) 2>/dev/null && success || failure - echo "" - return 0 -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - *) - echo $"Usage: $0 {start|stop}" - exit 3 - ;; -esac - -exit $RETVAL diff --git a/netvm/qubes_core b/netvm/qubes_core deleted file mode 100755 index dbfaad7a..00000000 --- a/netvm/qubes_core +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh -# -# chkconfig: 345 90 90 -# description: Executes Qubes core scripts at VM boot -# -# Source function library. -. /etc/rc.d/init.d/functions - -start() -{ - echo -n $"Executing Qubes Core scripts NetVM:" - - if ! [ -x /usr/bin/xenstore-read ] ; then - echo "ERROR: /usr/bin/xenstore-read not found!" - exit 1 - fi - - name=$(/usr/bin/xenstore-read name) - hostname $name - - # Setup gateway for all the VMs this netVM is serviceing... - modprobe netbk - gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) - netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) - network=$(/usr/bin/xenstore-read qubes_netvm_network) - secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) - echo "NS1=$gateway" > /var/run/qubes/qubes_ns - echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns - /usr/lib/qubes/qubes_setup_dnat_to_ns - echo "1" > /proc/sys/net/ipv4/ip_forward - success - echo "" - return 0 -} - -stop() -{ - return 0 -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - *) - echo $"Usage: $0 {start|stop}" - exit 3 - ;; -esac - -exit $RETVAL diff --git a/netvm/qubes_core_netvm b/netvm/qubes_core_netvm new file mode 100755 index 00000000..dd713fce --- /dev/null +++ b/netvm/qubes_core_netvm @@ -0,0 +1,58 @@ +#!/bin/sh +# +# chkconfig: 345 90 90 +# description: Executes Qubes core scripts at NetVM boot +# +# Source function library. +. /etc/rc.d/init.d/functions + +start() +{ + if ! [ -x /usr/bin/xenstore-read ] ; then + echo "ERROR: /usr/bin/xenstore-read not found!" + exit 1 + fi + + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "NetVM" ]; then + /sbin/service NetworkManager start + fi + + echo -n $"Executing Qubes Core scripts NetVM:" + + # Setup gateway for all the VMs this netVM is serviceing... + network=$(/usr/bin/xenstore-read qubes_netvm_network 2>/dev/null) + if [ "x$network" != "x" ]; then + gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) + netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) + secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) + modprobe netbk + echo "NS1=$gateway" > /var/run/qubes/qubes_ns + echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns + /usr/lib/qubes/qubes_setup_dnat_to_ns + echo "1" > /proc/sys/net/ipv4/ip_forward + fi + success + echo "" + return 0 +} + +stop() +{ + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/fwvm/bin/qubes_firewall b/proxyvm/bin/qubes_firewall similarity index 100% rename from fwvm/bin/qubes_firewall rename to proxyvm/bin/qubes_firewall diff --git a/proxyvm/init.d/qubes_firewall b/proxyvm/init.d/qubes_firewall new file mode 100755 index 00000000..1d3c0266 --- /dev/null +++ b/proxyvm/init.d/qubes_firewall @@ -0,0 +1,48 @@ +#!/bin/sh +# +# chkconfig: 345 91 91 +# description: Starts Qubes Firewall monitor +# +# Source function library. +. /etc/rc.d/init.d/functions + +PIDFILE=/var/run/qubes/qubes_firewall.pid + +start() +{ + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "ProxyVM" ]; then + echo -n $"Starting Qubes Firewall monitor:" + /sbin/ethtool -K eth0 sg off + /usr/sbin/qubes_firewall & + success + echo "" + fi + return 0 +} + +stop() +{ + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "ProxyVM" ]; then + echo -n "Stopping Qubes Firewall monitor:" + kill $(cat $PIDFILE) 2>/dev/null && success || failure + echo "" + fi + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index e901a12c..a6eea0d8 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -34,6 +34,7 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: /usr/bin/mimeopen +Requires: qubes-core-commonvm BuildRequires: gcc BuildRequires: xen-devel Provides: qubes-core-vm @@ -55,9 +56,6 @@ fi adduser --create-home user mkdir -p $RPM_BUILD_ROOT/var/lib/qubes -if [ -e $RPM_BUILD_ROOT/etc/fstab ] ; then -mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig -fi %build make clean all @@ -65,10 +63,8 @@ make -C ../common %install -mkdir -p $RPM_BUILD_ROOT/etc -cp fstab $RPM_BUILD_ROOT/etc/fstab mkdir -p $RPM_BUILD_ROOT/etc/init.d -cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ +cp qubes_core_appvm $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/bin cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin @@ -80,17 +76,9 @@ mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} cp qvm-copy.desktop qvm-dvm.desktop $RPM_BUILD_ROOT/%{kde_service_dir} mkdir -p $RPM_BUILD_ROOT/etc/udev/rules.d cp qubes.rules $RPM_BUILD_ROOT/etc/udev/rules.d -mkdir -p $RPM_BUILD_ROOT/etc/sysconfig -cp iptables $RPM_BUILD_ROOT/etc/sysconfig/ mkdir -p $RPM_BUILD_ROOT/mnt/incoming mkdir -p $RPM_BUILD_ROOT/mnt/outgoing mkdir -p $RPM_BUILD_ROOT/mnt/removable -mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d -cp ../appvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d -mkdir -p $RPM_BUILD_ROOT/sbin -cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin -mkdir -p $RPM_BUILD_ROOT/etc -cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ mkdir -p $RPM_BUILD_ROOT/etc/X11 cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11 @@ -98,9 +86,6 @@ cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11 mkdir -p $RPM_BUILD_ROOT/home_volatile/user chown 500:500 $RPM_BUILD_ROOT/home_volatile/user -%triggerin -- initscripts -cp /var/lib/qubes/serial.conf /etc/init/serial.conf - %post if [ "$1" != 1 ] ; then @@ -108,81 +93,15 @@ if [ "$1" != 1 ] ; then exit 0 fi -usermod -L root usermod -L user -if ! [ -f /var/lib/qubes/serial.orig ] ; then - cp /etc/init/serial.conf /var/lib/qubes/serial.orig -fi -#echo "--> Disabling SELinux..." -sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed -mv /etc/selinux/config.processed /etc/selinux/config -setenforce 0 2>/dev/null - -#echo "--> Turning off unnecessary services..." -# FIXME: perhaps there is more elegant way to do this? -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - [ $srv = 'single' ] && continue - chkconfig $srv off -done - -#echo "--> Enabling essential services..." -chkconfig rsyslog on -chkconfig haldaemon on -chkconfig messagebus on -chkconfig cups on -chkconfig iptables on -chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!" -chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" - - -# TODO: make this not display the silly message about security context... -sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab - -# Remove most of the udev scripts to speed up the VM boot time -# Just leave the xen* scripts, that are needed if this VM was -# ever used as a net backend (e.g. as a VPN domain in the future) -#echo "--> Removing unnecessary udev scripts..." -mkdir -p /var/lib/qubes/removed-udev-scripts -for f in /etc/udev/rules.d/* -do - if [ $(basename $f) == "xen-backend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "xend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "qubes.rules" ] ; then - continue - fi - - if [ $(basename $f) == "90-hal.rules" ] ; then - continue - fi - - - mv $f /var/lib/qubes/removed-udev-scripts/ -done -mkdir -p /rw -#rm -f /etc/mtab -#echo "--> Removing HWADDR setting from /etc/sysconfig/network-scripts/ifcfg-eth0" -#mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.orig -#grep -v HWADDR /etc/sysconfig/network-scripts/ifcfg-eth0.orig > /etc/sysconfig/network-scripts/ifcfg-eth0 +chkconfig --add qubes_core_appvm || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core_appvm on || echo "WARNING: Cannot enable service qubes_core!" %preun if [ "$1" = 0 ] ; then # no more packages left - chkconfig qubes_core off - mv /var/lib/qubes/fstab.orig /etc/fstab - mv /var/lib/qubes/removed-udev-scripts/* /etc/udev/rules.d/ - mv /var/lib/qubes/serial.orig /etc/init/serial.conf + chkconfig qubes_core_appvm off fi %clean @@ -190,8 +109,7 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) -/etc/fstab -/etc/init.d/qubes_core +/etc/init.d/qubes_core_appvm /usr/bin/qvm-copy-to-vm /usr/lib/qubes/qvm-copy-to-vm.kde %attr(4755,root,root) /usr/bin/qvm-open-in-dvm @@ -202,13 +120,9 @@ rm -rf $RPM_BUILD_ROOT %attr(4755,root,root) /usr/lib/qubes/qubes_penctl /usr/lib/qubes/qubes_add_pendrive_script /etc/udev/rules.d/qubes.rules -/etc/sysconfig/iptables -/var/lib/qubes %dir /mnt/incoming %dir /mnt/outgoing %dir /mnt/removable -/etc/yum.repos.d/qubes.repo -/sbin/qubes_serial_login /usr/bin/qubes_timestamp %dir /home_volatile %attr(700,user,user) /home_volatile/user diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 6bd6a808..389cfb61 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -97,6 +97,7 @@ cp restore/qubes_prepare_saved_domain.sh $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/vm-templates mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/appvms +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/servicevms mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/backup mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/dvmdata @@ -285,6 +286,7 @@ fi %attr(770,root,qubes) %dir /var/lib/qubes %attr(770,root,qubes) %dir /var/lib/qubes/vm-templates %attr(770,root,qubes) %dir /var/lib/qubes/appvms +%attr(770,root,qubes) %dir /var/lib/qubes/servicevms %attr(770,root,qubes) %dir /var/lib/qubes/backup %attr(770,root,qubes) %dir /var/lib/qubes/dvmdata %dir /usr/share/qubes/icons/*.png diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index d03591a1..f813e373 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -34,6 +34,7 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: NetworkManager >= 0.8.1-1 +Requires: qubes-core-commonvm Provides: qubes-core-vm %define _builddir %(pwd)/netvm @@ -43,22 +44,13 @@ The Qubes core files for installation inside a Qubes NetVM. %pre -mkdir -p $RPM_BUILD_ROOT/var/lib/qubes -if [ -e $RPM_BUILD_ROOT/etc/fstab ] ; then -mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig -fi - - %build %install -mkdir -p $RPM_BUILD_ROOT/etc/sysconfig -cp ../common/iptables $RPM_BUILD_ROOT/etc/sysconfig mkdir -p $RPM_BUILD_ROOT/etc -cp fstab $RPM_BUILD_ROOT/etc/fstab mkdir -p $RPM_BUILD_ROOT/etc/init.d -cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ +cp qubes_core_netvm $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes @@ -68,19 +60,10 @@ ln -s /usr/lib/qubes/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes mkdir -p $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ cp ../common/qubes_nmhook $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ cp ../netvm/30-qubes_external_ip $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ -mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d -cp ../netvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d -mkdir -p $RPM_BUILD_ROOT/sbin -cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin -mkdir -p $RPM_BUILD_ROOT/etc -cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ mkdir -p $RPM_BUILD_ROOT/var/run/qubes mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts -%triggerin -- initscripts -cp /var/lib/qubes/serial.conf /etc/init/serial.conf - %post /usr/lib/qubes/qubes_fix_nm_conf.sh @@ -90,81 +73,14 @@ if [ "$1" != 1 ] ; then exit 0 fi -sed 's/^net.ipv4.ip_forward.*/net.ipv4.ip_forward = 1/' -i /etc/sysctl.conf -usermod -L root -if ! [ -f /var/lib/qubes/serial.orig ] ; then - cp /etc/init/serial.conf /var/lib/qubes/serial.orig -fi +chkconfig --add qubes_core_netvm || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core_netvm on || echo "WARNING: Cannot enable service qubes_core!" -#echo "--> Disabling SELinux..." -sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed -mv /etc/selinux/config.processed /etc/selinux/config -setenforce 0 2>/dev/null - -#echo "--> Turning off unnecessary services..." -# FIXME: perhaps there is more elegant way to do this? -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - [ $srv = 'single' ] && continue - chkconfig $srv off -done - -#echo "--> Enabling essential services..." -chkconfig iptables on -chkconfig rsyslog on -chkconfig haldaemon on -chkconfig messagebus on -chkconfig NetworkManager on -chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!" -chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" - - -# TODO: make this not display the silly message about security context... -sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab - -# Remove most of the udev scripts to speed up the VM boot time -# Just leave the xen* scripts, that are needed if this VM was -# ever used as a net backend (e.g. as a VPN domain in the future) -#echo "--> Removing unnecessary udev scripts..." -mkdir -p /var/lib/qubes/removed-udev-scripts -for f in /etc/udev/rules.d/* -do - if [ $(basename $f) == "xen-backend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "xend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "qubes.rules" ] ; then - continue - fi - - if [ $(basename $f) == "90-hal.rules" ] ; then - continue - fi - - - mv $f /var/lib/qubes/removed-udev-scripts/ -done - -#rm -f /etc/mtab -#echo "--> Removing HWADDR setting from /etc/sysconfig/network-scripts/ifcfg-eth0" -#mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.orig -#grep -v HWADDR /etc/sysconfig/network-scripts/ifcfg-eth0.orig > /etc/sysconfig/network-scripts/ifcfg-eth0 %preun if [ "$1" = 0 ] ; then # no more packages left - chkconfig qubes_core off - mv /var/lib/qubes/fstab.orig /etc/fstab - mv /var/lib/qubes/removed-udev-scripts/* /etc/udev/rules.d/ - mv /var/lib/qubes/serial.orig /etc/init/serial.conf + chkconfig qubes_core_netvm off fi %clean @@ -172,16 +88,11 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) -/etc/fstab -/etc/sysconfig/iptables -/etc/init.d/qubes_core -/var/lib/qubes +/etc/init.d/qubes_core_netvm /usr/lib/qubes/qubes_setup_dnat_to_ns /usr/lib/qubes/qubes_fix_nm_conf.sh /etc/dhclient.d/qubes_setup_dnat_to_ns.sh /etc/NetworkManager/dispatcher.d/qubes_nmhook /etc/NetworkManager/dispatcher.d/30-qubes_external_ip -/etc/yum.repos.d/qubes.repo -/sbin/qubes_serial_login /etc/xen/scripts/vif-route-qubes %dir /var/run/qubes From 08b4490b91c679f86787b79e887ef0b43a421c52 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 01:42:42 +0100 Subject: [PATCH 55/72] NetVM, AppVM, ProxyVM from single template - VM side (missing files...) --- appvm/qubes_core_appvm | 82 ++++++++++++++++++ rpm_spec/core-commonvm.spec | 166 ++++++++++++++++++++++++++++++++++++ rpm_spec/core-proxyvm.spec | 77 +++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100755 appvm/qubes_core_appvm create mode 100644 rpm_spec/core-commonvm.spec create mode 100644 rpm_spec/core-proxyvm.spec diff --git a/appvm/qubes_core_appvm b/appvm/qubes_core_appvm new file mode 100755 index 00000000..ed250dd5 --- /dev/null +++ b/appvm/qubes_core_appvm @@ -0,0 +1,82 @@ +#!/bin/sh +# +# chkconfig: 345 85 85 +# description: Executes Qubes core scripts at AppVM boot +# +# Source function library. +. /etc/rc.d/init.d/functions + +possibly_run_save_script() +{ + ENCODED_SCRIPT=$(xenstore-read qubes_save_script) + if [ -z "$ENCODED_SCRIPT" ] ; then return ; fi + echo $ENCODED_SCRIPT|perl -e 'use MIME::Base64 qw(decode_base64); local($/) = undef;print decode_base64()' >/tmp/qubes_save_script + chmod 755 /tmp/qubes_save_script + Xorg -config /etc/X11/xorg-preload-apps.conf :0 & + sleep 2 + DISPLAY=:0 su - user -c /tmp/qubes_save_script + killall Xorg +} + +start() +{ + if ! [ -x /usr/bin/xenstore-read ] ; then + echo "ERROR: /usr/bin/xenstore-read not found!" + exit 1 + fi + + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" != "AppVM" -a "$type" != "DisposableVM" ]; then + # This script runs only on AppVMs + return 0 + fi + + # Start AppVM specific services + /sbin/service cups start + + echo -n $"Executing Qubes Core scripts for AppVM:" + + if xenstore-read qubes_save_request 2>/dev/null ; then + ln -sf /home_volatile /home + possibly_run_save_script + touch /etc/this_is_dvm + dmesg -c >/dev/null + free | grep Mem: | + (read a b c d ; xenstore-write device/qubes_used_mem $c) + # we're still running in DispVM template + echo "Waiting for save/restore..." + # ... wait until qubes_restore.c (in Dom0) recreates VM-specific keys + while ! xenstore-read qubes_restore_complete 2>/dev/null ; do + usleep 10 + done + echo Back to life. + fi + + MEM_CHANGE_THRESHOLD_KB=30000 + MEMINFO_DELAY_USEC=100000 + /usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC & + + success + echo "" + return 0 +} + +stop() +{ + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/rpm_spec/core-commonvm.spec b/rpm_spec/core-commonvm.spec new file mode 100644 index 00000000..6705c28b --- /dev/null +++ b/rpm_spec/core-commonvm.spec @@ -0,0 +1,166 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Joanna Rutkowska +# Copyright (C) 2010 Rafal Wojtczuk +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +%{!?version: %define version %(cat version_vm)} + +Name: qubes-core-commonvm +Version: %{version} +Release: 1 +Summary: The Qubes core files for any VM + +Group: Qubes +Vendor: Invisible Things Lab +License: GPL +URL: http://www.qubes-os.org +Requires: /usr/bin/xenstore-read +Requires: fedora-release = 13 + +%define _builddir %(pwd)/common + +%description +The Qubes core files for installation inside a Qubes VM. + +%pre + +if [ "$1" != 1 ] ; then +# do this whole %pre thing only when updating for the first time... +exit 0 +fi + +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes +if [ -e $RPM_BUILD_ROOT/etc/fstab ] ; then +mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig +fi + +%install + +mkdir -p $RPM_BUILD_ROOT/etc +cp fstab $RPM_BUILD_ROOT/etc/fstab +mkdir -p $RPM_BUILD_ROOT/etc/init.d +cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes +mkdir -p $RPM_BUILD_ROOT/etc/sysconfig +cp iptables $RPM_BUILD_ROOT/etc/sysconfig/ +mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d +cp ../appvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d +mkdir -p $RPM_BUILD_ROOT/sbin +cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT/etc +cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ + +%triggerin -- initscripts +cp /var/lib/qubes/serial.conf /etc/init/serial.conf + +%post + +if [ "$1" != 1 ] ; then +# do this whole %post thing only when updating for the first time... +exit 0 +fi + +usermod -L root +if ! [ -f /var/lib/qubes/serial.orig ] ; then + cp /etc/init/serial.conf /var/lib/qubes/serial.orig +fi + +#echo "--> Disabling SELinux..." +sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed +mv /etc/selinux/config.processed /etc/selinux/config +setenforce 0 2>/dev/null + +#echo "--> Turning off unnecessary services..." +# FIXME: perhaps there is more elegant way to do this? +for f in /etc/init.d/* +do + srv=`basename $f` + [ $srv = 'functions' ] && continue + [ $srv = 'killall' ] && continue + [ $srv = 'halt' ] && continue + [ $srv = 'single' ] && continue + [ $srv = 'qubes_gui' ] && continue + chkconfig $srv off +done + +#echo "--> Enabling essential services..." +chkconfig rsyslog on +chkconfig haldaemon on +chkconfig messagebus on +chkconfig iptables on +chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" + + +# TODO: make this not display the silly message about security context... +sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab + +# Remove most of the udev scripts to speed up the VM boot time +# Just leave the xen* scripts, that are needed if this VM was +# ever used as a net backend (e.g. as a VPN domain in the future) +#echo "--> Removing unnecessary udev scripts..." +mkdir -p /var/lib/qubes/removed-udev-scripts +for f in /etc/udev/rules.d/* +do + if [ $(basename $f) == "xen-backend.rules" ] ; then + continue + fi + + if [ $(basename $f) == "xend.rules" ] ; then + continue + fi + + if [ $(basename $f) == "qubes.rules" ] ; then + continue + fi + + if [ $(basename $f) == "90-hal.rules" ] ; then + continue + fi + + + mv $f /var/lib/qubes/removed-udev-scripts/ +done +mkdir -p /rw +#rm -f /etc/mtab +#echo "--> Removing HWADDR setting from /etc/sysconfig/network-scripts/ifcfg-eth0" +#mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.orig +#grep -v HWADDR /etc/sysconfig/network-scripts/ifcfg-eth0.orig > /etc/sysconfig/network-scripts/ifcfg-eth0 + +%preun +if [ "$1" = 0 ] ; then + # no more packages left + chkconfig qubes_core off + mv /var/lib/qubes/fstab.orig /etc/fstab + mv /var/lib/qubes/removed-udev-scripts/* /etc/udev/rules.d/ + mv /var/lib/qubes/serial.orig /etc/init/serial.conf +fi + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +/etc/fstab +/etc/init.d/qubes_core +/etc/sysconfig/iptables +/var/lib/qubes +/etc/yum.repos.d/qubes.repo +/sbin/qubes_serial_login diff --git a/rpm_spec/core-proxyvm.spec b/rpm_spec/core-proxyvm.spec new file mode 100644 index 00000000..c5a5ad30 --- /dev/null +++ b/rpm_spec/core-proxyvm.spec @@ -0,0 +1,77 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Joanna Rutkowska +# Copyright (C) 2010 Rafal Wojtczuk +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +%{!?version: %define version %(cat version_vm)} + +Name: qubes-core-proxyvm +Version: %{version} +Release: 1 +Summary: The Qubes core files for NetVM + +Group: Qubes +Vendor: Invisible Things Lab +License: GPL +URL: http://www.qubes-os.org +Requires: /usr/bin/xenstore-read +Requires: /sbin/ethtool +Requires: fedora-release = 13 +Requires: qubes-core-netvm + +%define _builddir %(pwd)/proxyvm + +%description +The Qubes core files for installation inside a Qubes ProxyVM in addition to NetVM scripts. + +%pre + +%build + +%install + +mkdir -p $RPM_BUILD_ROOT/etc/init.d +cp init.d/qubes_firewall $RPM_BUILD_ROOT/etc/init.d/ +mkdir -p $RPM_BUILD_ROOT/usr/sbin +cp bin/qubes_firewall $RPM_BUILD_ROOT/usr/sbin/ + +%post + +if [ "$1" != 1 ] ; then +# do this whole %post thing only when updating for the first time... +exit 0 +fi + +chkconfig --add qubes_firewall || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_firewall on || echo "WARNING: Cannot enable service qubes_core!" + +%preun +if [ "$1" = 0 ] ; then + # no more packages left + chkconfig qubes_firewall off +fi + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +/etc/init.d/qubes_firewall +/usr/sbin/qubes_firewall From 969b14b5ede9468282488dcf85af5dc1c9fbbe21 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 01:48:27 +0100 Subject: [PATCH 56/72] qvm-create: support for netvm and proxyvm Move PCI config from qvm-add-netvm to qvm-core. Remove qvm-add-netvm as useless when netvm is template-based --- dom0/qvm-core/qubes.py | 5 ++ dom0/qvm-tools/qvm-add-netvm | 119 ----------------------------------- dom0/qvm-tools/qvm-create | 73 ++++++++++++++++----- 3 files changed, 64 insertions(+), 133 deletions(-) delete mode 100755 dom0/qvm-tools/qvm-add-netvm diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 270d0049..94d687c8 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -205,6 +205,9 @@ class QubesVm(object): else: self.icon_path = None + # PCI devices - used only by NetVM + self.pcidevs = "" + if not dry_run and xend_session.session is not None: self.refresh_xend_session() @@ -883,11 +886,13 @@ class QubesCowVm(QubesVm): rx_vmname = re.compile (r"%VMNAME%") rx_vmdir = re.compile (r"%VMDIR%") rx_template = re.compile (r"%TEMPLATEDIR%") + rx_pcidevs = re.compile (r"%PCIDEVS%") for line in conf_template: line = rx_vmname.sub (self.name, line) line = rx_vmdir.sub (self.dir_path, line) line = rx_template.sub (self.template_vm.dir_path, line) + line = rx_pcidevs.sub (self.pcidevs, line) conf_appvm.write(line) conf_template.close() diff --git a/dom0/qvm-tools/qvm-add-netvm b/dom0/qvm-tools/qvm-add-netvm deleted file mode 100755 index f5d3cde4..00000000 --- a/dom0/qvm-tools/qvm-add-netvm +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/python2.6 -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2010 Joanna Rutkowska -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# - -from optparse import OptionParser -import subprocess -import shutil -import re - -from qubes.qubes import QubesVmCollection -from qubes.qubes import QubesException - -def find_net_devices(): - p = subprocess.Popen (["lspci", "-mm", "-n"], stdout=subprocess.PIPE) - result = p.communicate() - retcode = p.returncode - if (retcode != 0): - print "ERROR when executing lspci!" - raise IOError - - net_devices = set() - rx_netdev = re.compile (r"^([0-9][0-9]:[0-9][0-9].[0-9]) \"02") - for dev in str(result[0]).splitlines(): - match = rx_netdev.match (dev) - if match is not None: - dev_bdf = match.group(1) - assert dev_bdf is not None - net_devices.add (dev_bdf) - - return net_devices - -def main(): - usage = "usage: %prog [options] " - parser = OptionParser (usage) - parser.add_option ("-p", "--path", dest="dir_path", - help="Specify path to the template directory") - parser.add_option ("-c", "--conf", dest="conf_file", - help="Specify the Xen VM .conf file to use\ - (relative to the template dir path)") - - - (options, args) = parser.parse_args () - if (len (args) != 1): - parser.error ("You must specify a NetVM name!") - netvmname = args[0] - - qvm_collection = QubesVmCollection() - qvm_collection.lock_db_for_writing() - qvm_collection.load() - - if qvm_collection.get_vm_by_name(netvmname) is not None: - print "ERROR: A VM with the name '{0}' already exists in the system.".format(netvmname) - exit(1) - - vm = qvm_collection.add_new_netvm(netvmname, - conf_file=options.conf_file, - dir_path=options.dir_path) - - try: - vm.verify_files() - except QubesException as err: - print "ERROR: {0}".format(err) - qvm_collection.pop(vm.qid) - exit (1) - - - net_devices = find_net_devices() - print "Found the following net devices in your system:" - dev_str = '' - for dev in net_devices: - print "--> {0}".format(dev) - dev_str += '"{0}", '.format(dev) - - print "Assigning them to the netvm '{0}'".format(netvmname) - rx_pcidevs = re.compile (r"%NETVMPCIDEVS%") - conf_template = open (vm.conf_file, "r") - conf_vm = open(vm.conf_file + ".processed", "w") - - for line in conf_template: - line = rx_pcidevs.sub(dev_str, line) - conf_vm.write(line) - - conf_template.close() - conf_vm.close() - - shutil.move (vm.conf_file + ".processed", vm.conf_file) - - - try: - pass - vm.add_to_xen_storage() - - except (IOError, OSError) as err: - print "ERROR: {0}".format(err) - qvm_collection.pop(vm.qid) - exit (1) - - qvm_collection.save() - qvm_collection.unlock_db() - -main() diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index b0f227ab..aa567b98 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -24,6 +24,26 @@ from qubes.qubes import QubesVmCollection from qubes.qubes import QubesVmLabels from optparse import OptionParser; import subprocess +import re + +def find_net_devices(): + p = subprocess.Popen (["lspci", "-mm", "-n"], stdout=subprocess.PIPE) + result = p.communicate() + retcode = p.returncode + if (retcode != 0): + print "ERROR when executing lspci!" + raise IOError + + net_devices = set() + rx_netdev = re.compile (r"^([0-9][0-9]:[0-9][0-9].[0-9]) \"02") + for dev in str(result[0]).splitlines(): + match = rx_netdev.match (dev) + if match is not None: + dev_bdf = match.group(1) + assert dev_bdf is not None + net_devices.add (dev_bdf) + + return net_devices def main(): @@ -33,6 +53,10 @@ def main(): help="Specify the TemplateVM to use") parser.add_option ("-l", "--label", dest="label", help="Specify the label to use for the new VM (e.g. red, yellow, green, ...)") + parser.add_option ("-p", "--proxy", action="store_true", dest="proxyvm", default=False, + help="Create ProxyVM") + parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False, + help="Create NetVM") parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) (options, args) = parser.parse_args () @@ -40,20 +64,24 @@ def main(): parser.error ("You must specify VM name!") vmname = args[0] - if options.label is None: - print "You must choose a label for the new VM by passing the --label option." - print "Possible values are:" - for l in QubesVmLabels.values(): - print "* {0}".format(l.name) - exit (1) - - if options.label not in QubesVmLabels: - print "Wrong label name, supported values are the following:" - for l in QubesVmLabels.values(): - print "* {0}".format(l.name) - exit (1) - label = QubesVmLabels[options.label] + if options.netvm and options.proxyvm: + parser.error ("You must specify at most one of --proxy and --net") + + label = None + if not options.proxyvm and not options.netvm: + if options.label is None: + print "You must choose a label for the new VM by passing the --label option." + print "Possible values are:" + for l in QubesVmLabels.values(): + print "* {0}".format(l.name) + exit (1) + if options.label not in QubesVmLabels: + print "Wrong label name, supported values are the following:" + for l in QubesVmLabels.values(): + print "* {0}".format(l.name) + exit (1) + label = QubesVmLabels[options.label] qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_writing() @@ -83,7 +111,24 @@ def main(): if (options.verbose): print "--> Using default TemplateVM: {0}".format(template_vm.name) - vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) + vm = None + if options.netvm: + vm = qvm_collection.add_new_netvm(vmname, template_vm) + + net_devices = find_net_devices() + print "Found the following net devices in your system:" + dev_str = '' + for dev in net_devices: + print "--> {0}".format(dev) + dev_str += '"{0}", '.format(dev) + + print "Assigning them to the netvm '{0}'".format(vmname) + vm.pcidevs = dev_str + + elif options.proxyvm: + vm = qvm_collection.add_new_proxyvm(vmname, template_vm) + else: + vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) try: vm.create_on_disk(verbose=options.verbose) vm.add_to_xen_storage() From 3043a391e0f0880777e3a647ef0a48d001cd2282 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 01:52:09 +0100 Subject: [PATCH 57/72] 'templete' typo again --- dom0/qvm-core/qubes.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 94d687c8..1df7857d 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -644,7 +644,7 @@ class QubesTemplateVm(QubesVm): @property def type(self): - return "TempleteVM" + return "TemplateVM" def set_updateable(self): if self.is_updateable(): @@ -655,7 +655,7 @@ class QubesTemplateVm(QubesVm): for appvm in self.appvms.values(): if appvm.is_updateable(): raise QubesException("One of the AppVMs ('{0}')is also 'updateable'\ - -- cannot make the TempleteVM {'{1}'} 'nonupdatable'".\ + -- cannot make the TemplateVM {'{1}'} 'nonupdatable'".\ format (appvm.name, self.name)) self.updateable = True @@ -840,7 +840,6 @@ class QubesCowVm(QubesVm): private_img = kwargs.pop("private_img") template_vm = kwargs.pop("template_vm") - super(QubesCowVm, self).__init__(**kwargs) qid = kwargs["qid"] dir_path = kwargs["dir_path"] @@ -848,7 +847,7 @@ class QubesCowVm(QubesVm): if not isinstance(self, QubesDom0NetVm): assert template_vm is not None, "Missing template_vm for template based VM!" if not template_vm.is_template(): - print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ + print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\ format(template_vm.qid) return False @@ -1299,7 +1298,7 @@ class QubesDisposableVm(QubesVm): assert template_vm is not None, "Missing template_vm for DisposableVM!" if not template_vm.is_template(): - print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ + print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\ format(new_vm.template_vm.qid) return False @@ -1612,7 +1611,7 @@ class QubesVmCollection(dict): return vm def set_default_template_vm(self, vm): - assert vm.is_template(), "VM {0} is not a TempleteVM!".format(vm.name) + assert vm.is_template(), "VM {0} is not a TemplateVM!".format(vm.name) self.default_template_qid = vm.qid def get_default_template_vm(self): From 8928e55215759b791b655c5f7a4815636850af19 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 01:55:29 +0100 Subject: [PATCH 58/72] Swap COW for all CowVMs, not only AppVM --- dom0/qvm-core/qubes.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1df7857d..54464226 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -862,6 +862,7 @@ class QubesCowVm(QubesVm): private_img if private_img is not None else default_private_img) self.rootcow_img = dir_path + "/" + default_rootcow_img + self.swapcow_img = dir_path + "/" + default_swapcow_img def set_updateable(self): if self.is_updateable(): @@ -872,6 +873,7 @@ class QubesCowVm(QubesVm): if not self.template_vm.is_updateable(): self.updateable = True self.reset_cow_storage() + self.reset_swap_cow_storage() else: # Temaplate VM is Updatable itself --> can't make the AppVM updateable too # as this would cause COW-backed storage incoherency @@ -957,6 +959,8 @@ class QubesCowVm(QubesVm): if not self.is_updateable(): self.reset_cow_storage() + self.reset_swap_cow_storage() + return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) def reset_cow_storage (self): @@ -977,6 +981,16 @@ class QubesCowVm(QubesVm): f_cow.close () f_root.close() + def reset_swap_cow_storage (self): + print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img) + if os.path.exists (self.swapcow_img): + os.remove (self.swapcow_img) + + f_swap_cow = open (self.swapcow_img, "w") + f_swap_cow.truncate (swap_cow_sz) + f_swap_cow.close() + + def remove_from_disk(self): if dry_run: return @@ -1331,8 +1345,6 @@ class QubesAppVm(QubesCowVm): super(QubesAppVm, self).__init__(**kwargs) dir_path = self.dir_path - self.swapcow_img = dir_path + "/" + default_swapcow_img - if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None: kwargs["firewall_conf"] = dir_path + "/" + default_firewall_conf_file @@ -1345,7 +1357,6 @@ class QubesAppVm(QubesCowVm): def set_updateable(self): super(QubesAppVm, self).set_updateable() - self.reset_swap_cow_storage() def create_on_disk(self, verbose): if dry_run: @@ -1467,15 +1478,6 @@ class QubesAppVm(QubesCowVm): return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - def reset_swap_cow_storage (self): - print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img) - if os.path.exists (self.swapcow_img): - os.remove (self.swapcow_img) - - f_swap_cow = open (self.swapcow_img, "w") - f_swap_cow.truncate (swap_cow_sz) - f_swap_cow.close() - class QubesVmCollection(dict): """ A collection of Qubes VMs indexed by Qubes id (qid) From a3d87788412b6a8f5a87e58d875b8aeebd9b0911 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 01:59:56 +0100 Subject: [PATCH 59/72] arameters for add_new_*, variables loaded from qubes.xml Cow based VMs doesn't have root_img param, but private_img. --- dom0/qvm-core/qubes.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 54464226..e8214927 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1573,14 +1573,14 @@ class QubesVmCollection(dict): def add_new_netvm(self, name, template_vm, dir_path = None, conf_file = None, - root_img = None): + private_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm, netid=netid, - dir_path=dir_path, conf_file=conf_file, - root_img=root_img) + private_img=private_img, + dir_path=dir_path, conf_file=conf_file) if not self.verify_new_vm (vm): assert False, "Wrong VM description!" @@ -1593,15 +1593,15 @@ class QubesVmCollection(dict): def add_new_proxyvm(self, name, template_vm, dir_path = None, conf_file = None, - root_img = None): + private_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm, netid=netid, + private_img=private_img, dir_path=dir_path, conf_file=conf_file, - netvm_vm = self.get_default_fw_netvm_vm(), - root_img=root_img) + netvm_vm = self.get_default_fw_netvm_vm()) if not self.verify_new_vm (vm): assert False, "Wrong VM description!" @@ -1827,7 +1827,7 @@ class QubesVmCollection(dict): try: kwargs = {} attr_list = ("qid", "netid", "name", "dir_path", "conf_file", - "private_img", "root_img", "template_qid", + "private_img", "template_qid", "updateable", ) for attribute in attr_list: @@ -1859,8 +1859,8 @@ class QubesVmCollection(dict): for element in tree.findall("QubesProxyVm"): try: kwargs = {} - attr_list = ("qid", "netid", "name", "dir_path", "conf_file", - "private_img", "root_img", "netvm_qid", "template_qid") + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", "updateable", + "private_img", "template_qid") for attribute in attr_list: kwargs[attribute] = element.get(attribute) From 5c2e676fa1eec98d8139621c11d115421eae03b6 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:00:42 +0100 Subject: [PATCH 60/72] Set netvm reference only after NetVMs/ProxyVMs load - ProxyVM --- dom0/qvm-core/qubes.py | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index e8214927..be785d2e 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1783,7 +1783,7 @@ class QubesVmCollection(dict): attr_list = ("qid", "name", "dir_path", "conf_file", "appvms_conf_file", "private_img", "root_img", "installed_by_rpm", "updateable", - "uses_default_netvm", "netvm_qid") + "uses_default_netvm") for attribute in attr_list: kwargs[attribute] = element.get(attribute) @@ -1793,26 +1793,6 @@ class QubesVmCollection(dict): if kwargs["updateable"] is not None: kwargs["updateable"] = True if kwargs["updateable"] == "True" else False - if "uses_default_netvm" not in kwargs: - kwargs["uses_default_netvm"] = True - else: - kwargs["uses_default_netvm"] = True if kwargs["uses_default_netvm"] == "True" else False - if kwargs["uses_default_netvm"] is True: - netvm_vm = self.get_default_netvm_vm() - kwargs.pop("netvm_qid") - else: - if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: - netvm_vm = None - kwargs.pop("netvm_qid") - else: - netvm_qid = int(kwargs.pop("netvm_qid")) - if netvm_qid not in self: - netvm_vm = None - else: - netvm_vm = self[netvm_qid] - - kwargs["netvm_vm"] = netvm_vm - vm = QubesTemplateVm(**kwargs) self[vm.qid] = vm @@ -1878,18 +1858,6 @@ class QubesVmCollection(dict): kwargs["template_vm"] = template_vm kwargs["netid"] = int(kwargs["netid"]) - if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: - netvm_vm = None - kwargs.pop("netvm_qid") - else: - netvm_qid = int(kwargs.pop("netvm_qid")) - if netvm_qid not in self: - netvm_vm = None - else: - netvm_vm = self[netvm_qid] - - kwargs["netvm_vm"] = netvm_vm - vm = QubesProxyVm(**kwargs) self[vm.qid] = vm From de5e06e46272efc726892b94f6902f9740a79c18 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:02:13 +0100 Subject: [PATCH 61/72] Remove duplicated entry in core-dom0.spec --- rpm_spec/core-dom0.spec | 1 - 1 file changed, 1 deletion(-) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 389cfb61..e770b66c 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -300,7 +300,6 @@ fi /usr/lib64/pm-utils/sleep.d/01qubes-swap-pci-devs /usr/lib64/pm-utils/sleep.d/02qubes-pause-vms /usr/bin/xenstore-watch -/usr/bin/qvm-create-default-dvm /usr/lib/qubes/qubes_restore /usr/lib/qubes/qubes_prepare_saved_domain.sh /etc/xen/scripts/block.qubes From 41800eb879d8d9a6315d4810fe02e6b911fe0b9d Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:09:32 +0100 Subject: [PATCH 62/72] Store default_fw_netvm in qubes.xml --- dom0/qvm-core/qubes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index be785d2e..9342109a 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1724,6 +1724,9 @@ class QubesVmCollection(dict): default_netvm=str(self.default_netvm_qid) \ if self.default_netvm_qid is not None else "None" + + default_fw_netvm=str(self.default_fw_netvm_qid) \ + if self.default_fw_netvm_qid is not None else "None" ) for vm in self.values(): @@ -1774,6 +1777,12 @@ class QubesVmCollection(dict): if default_netvm != "None" else None #assert self.default_netvm_qid is not None + default_fw_netvm = element.get("default_netvm") + if default_fw_netvm is not None: + self.default_fw_netvm_qid = int(default_fw_netvm) \ + if default_fw_netvm != "None" else None + #assert self.default_netvm_qid is not None + # Then, read in the TemplateVMs, because a reference to template VM # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): From 48613fb911121bcac9d607c831ff0a6b3e7ca299 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:11:05 +0100 Subject: [PATCH 63/72] Check if netvm is set for ProxyVM before using it... --- dom0/qvm-core/qubes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 9342109a..0b4b92eb 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1104,7 +1104,8 @@ class QubesProxyVm(QubesNetVm): def __init__(self, **kwargs): super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) self.rules_applied = None - self.netvm_vm.add_external_ip_permission(self.get_xid()) + if self.netvm_vm is not None: + self.netvm_vm.add_external_ip_permission(self.get_xid()) @property def type(self): From 344b257d873447c8da9f1431d3afb6d5483d6eb8 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:12:23 +0100 Subject: [PATCH 64/72] Missing coma --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 0b4b92eb..1171501e 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1724,7 +1724,7 @@ class QubesVmCollection(dict): if self.default_template_qid is not None else "None", default_netvm=str(self.default_netvm_qid) \ - if self.default_netvm_qid is not None else "None" + if self.default_netvm_qid is not None else "None", default_fw_netvm=str(self.default_fw_netvm_qid) \ if self.default_fw_netvm_qid is not None else "None" From 53b8e5aacf416e7924809754bd3131e61ca395fb Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:22:26 +0100 Subject: [PATCH 65/72] Requiest external_ip permission at start, not create --- dom0/qvm-core/qubes.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1171501e..30c13fa7 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1104,6 +1104,13 @@ class QubesProxyVm(QubesNetVm): def __init__(self, **kwargs): super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) self.rules_applied = None + + def start(self, debug_console = False, verbose = False, preparing_dvm = False): + if dry_run: + return + + super(QubesProxyVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) + if self.netvm_vm is not None: self.netvm_vm.add_external_ip_permission(self.get_xid()) From 2a72b293c40e18ae759a48438940ba922b6cf826 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 02:44:11 +0100 Subject: [PATCH 66/72] ProxyVM type in qvm-ls --- dom0/qvm-tools/qvm-ls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 7516af1e..14d3e95f 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -39,7 +39,7 @@ fields = { + ('}' if vm.is_netvm() else '')"}, "type": {"func": "'Tpl' if vm.is_template() else \ - (' Prox' if vm.is_proxyvm() else \ + ('Proxy' if vm.is_proxyvm() else \ (' Net' if vm.is_netvm() else ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, From dc8325f56490e1d1c315ca8b050e6915ef9a4d86 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Fri, 11 Mar 2011 19:39:26 +0100 Subject: [PATCH 67/72] Use DNS IPs in firewall rules --- dom0/qvm-core/qubes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 6a19ea5f..d095d713 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1077,7 +1077,9 @@ class QubesFirewallVm(QubesNetVm): iptables += " -j {0}\n".format(rules_action) if conf["allowDns"]: - iptables += "-A FORWARD -i vif{0}.0 -p udp --dport 53 -j ACCEPT\n".format(xid) + # PREROUTING does DNAT to NetVM DNSes, so we need self.netvm_vm. properties + iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.gateway) + iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.secondary_dns) iptables += "-A FORWARD -i vif{0}.0 -j {1}\n".format(xid, default_action) From bba0e6e1efde7488120a996aee4173158af20100 Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Fri, 11 Mar 2011 19:39:52 +0100 Subject: [PATCH 68/72] FwVM network changes watcher script --- fwvm/bin/qubes_netwatcher | 27 +++++++++++++++++++++++ fwvm/init.d/qubes_netwatcher | 42 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100755 fwvm/bin/qubes_netwatcher create mode 100755 fwvm/init.d/qubes_netwatcher diff --git a/fwvm/bin/qubes_netwatcher b/fwvm/bin/qubes_netwatcher new file mode 100755 index 00000000..8f9d2e9c --- /dev/null +++ b/fwvm/bin/qubes_netwatcher @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +PIDFILE=/var/run/qubes/qubes_netwatcher.pid +CURR_NETCFG="" + +# PIDfile handling +[[ -e $PIDFILE ]] && kill -s 0 $(<$PIDFILE) 2>/dev/null && exit 0 +echo $$ >$PIDFILE + +trap 'exit 0' SIGTERM + +while true; do + NET_DOMID=$(/usr/bin/xenstore-read qubes_netvm_domid) + if [[ -n "$NET_DOMID" ]] && [[ $NET_DOMID -gt 0 ]]; then + NETCFG=$(/usr/bin/xenstore-read /local/domain/$NET_DOMID/qubes_netvm_external_ip) + if [[ "$NETCFG" != "$CURR_NETCFG" ]]; then + /sbin/service qubes_firewall stop + /sbin/service qubes_firewall start + CURR_NETCFG="$NETCFG" + fi + + /usr/bin/xenstore-watch /local/domain/$NET_DOMID/qubes_netvm_external_ip + else + /usr/bin/xenstore-watch qubes_netvm_domid + fi +done diff --git a/fwvm/init.d/qubes_netwatcher b/fwvm/init.d/qubes_netwatcher new file mode 100755 index 00000000..c322c823 --- /dev/null +++ b/fwvm/init.d/qubes_netwatcher @@ -0,0 +1,42 @@ +#!/bin/sh +# +# chkconfig: 345 92 92 +# description: Starts Qubes Network monitor +# +# Source function library. +. /etc/rc.d/init.d/functions + +PIDFILE=/var/run/qubes/qubes_netwatcher.pid + +start() +{ + echo -n $"Starting Qubes Network monitor:" + /sbin/ethtool -K eth0 sg off + /usr/bin/qubes_netwatcher & + success + echo "" + return 0 +} + +stop() +{ + echo -n "Stopping Qubes Network monitor:" + kill -9 $(cat $PIDFILE) 2>/dev/null && success || failure + echo "" + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL From e2d52a27e810522c41720bb17b1f4f52f1fe2e6a Mon Sep 17 00:00:00 2001 From: Tomasz Sterna Date: Fri, 11 Mar 2011 19:40:23 +0100 Subject: [PATCH 69/72] Use SIGKILL to stop qubes_firewall service --- fwvm/init.d/qubes_firewall | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fwvm/init.d/qubes_firewall b/fwvm/init.d/qubes_firewall index f970734f..c23fb2b4 100755 --- a/fwvm/init.d/qubes_firewall +++ b/fwvm/init.d/qubes_firewall @@ -21,7 +21,7 @@ start() stop() { echo -n "Stopping Qubes Firewall monitor:" - kill $(cat $PIDFILE) 2>/dev/null && success || failure + kill -9 $(cat $PIDFILE) 2>/dev/null && success || failure echo "" return 0 } From 65a758029e89338aa377ab19cef812fb89caa74f Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 23:21:23 +0100 Subject: [PATCH 70/72] Revert "Requiest external_ip permission at start, not create" This reverts commit 53b8e5aacf416e7924809754bd3131e61ca395fb. --- dom0/qvm-core/qubes.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 30c13fa7..1171501e 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -1104,13 +1104,6 @@ class QubesProxyVm(QubesNetVm): def __init__(self, **kwargs): super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) self.rules_applied = None - - def start(self, debug_console = False, verbose = False, preparing_dvm = False): - if dry_run: - return - - super(QubesProxyVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - if self.netvm_vm is not None: self.netvm_vm.add_external_ip_permission(self.get_xid()) From 3d845e4f6100a14eb5d73a4afaa300ae80a0faa1 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 23:33:15 +0100 Subject: [PATCH 71/72] Add qubes_netwatcher to proxyvm spec --- rpm_spec/core-proxyvm.spec | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rpm_spec/core-proxyvm.spec b/rpm_spec/core-proxyvm.spec index c5a5ad30..13c92535 100644 --- a/rpm_spec/core-proxyvm.spec +++ b/rpm_spec/core-proxyvm.spec @@ -49,8 +49,10 @@ The Qubes core files for installation inside a Qubes ProxyVM in addition to NetV mkdir -p $RPM_BUILD_ROOT/etc/init.d cp init.d/qubes_firewall $RPM_BUILD_ROOT/etc/init.d/ +cp init.d/qubes_netwatcher $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/usr/sbin cp bin/qubes_firewall $RPM_BUILD_ROOT/usr/sbin/ +cp bin/qubes_netwatcher $RPM_BUILD_ROOT/usr/sbin/ %post @@ -62,10 +64,14 @@ fi chkconfig --add qubes_firewall || echo "WARNING: Cannot add service qubes_core!" chkconfig qubes_firewall on || echo "WARNING: Cannot enable service qubes_core!" +chkconfig --add qubes_netwatcher || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_netwatcher on || echo "WARNING: Cannot enable service qubes_core!" + %preun if [ "$1" = 0 ] ; then # no more packages left chkconfig qubes_firewall off + chkconfig qubes_netwatcher off fi %clean @@ -74,4 +80,6 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) /etc/init.d/qubes_firewall +/etc/init.d/qubes_netwatcher /usr/sbin/qubes_firewall +/usr/sbin/qubes_netwatcher From b04b36af2ccc31b49415d81ac23458e0aaa0fa28 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Fri, 11 Mar 2011 23:42:49 +0100 Subject: [PATCH 72/72] Register VM services also on update --- rpm_spec/core-appvm.spec | 6 +++--- rpm_spec/core-netvm.spec | 6 ------ rpm_spec/core-proxyvm.spec | 5 ----- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index a6eea0d8..91fd13c6 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -88,6 +88,9 @@ chown 500:500 $RPM_BUILD_ROOT/home_volatile/user %post +chkconfig --add qubes_core_appvm || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core_appvm on || echo "WARNING: Cannot enable service qubes_core!" + if [ "$1" != 1 ] ; then # do this whole %post thing only when updating for the first time... exit 0 @@ -95,9 +98,6 @@ fi usermod -L user -chkconfig --add qubes_core_appvm || echo "WARNING: Cannot add service qubes_core!" -chkconfig qubes_core_appvm on || echo "WARNING: Cannot enable service qubes_core!" - %preun if [ "$1" = 0 ] ; then # no more packages left diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index f813e373..346c548e 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -68,15 +68,9 @@ cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts /usr/lib/qubes/qubes_fix_nm_conf.sh -if [ "$1" != 1 ] ; then -# do this whole %post thing only when updating for the first time... -exit 0 -fi - chkconfig --add qubes_core_netvm || echo "WARNING: Cannot add service qubes_core!" chkconfig qubes_core_netvm on || echo "WARNING: Cannot enable service qubes_core!" - %preun if [ "$1" = 0 ] ; then # no more packages left diff --git a/rpm_spec/core-proxyvm.spec b/rpm_spec/core-proxyvm.spec index 13c92535..52bc17f8 100644 --- a/rpm_spec/core-proxyvm.spec +++ b/rpm_spec/core-proxyvm.spec @@ -56,11 +56,6 @@ cp bin/qubes_netwatcher $RPM_BUILD_ROOT/usr/sbin/ %post -if [ "$1" != 1 ] ; then -# do this whole %post thing only when updating for the first time... -exit 0 -fi - chkconfig --add qubes_firewall || echo "WARNING: Cannot add service qubes_core!" chkconfig qubes_firewall on || echo "WARNING: Cannot enable service qubes_core!"