Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(wip) Add boot mode support #653

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions qubes/ext/core_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import qubes.ext

_version_re = re.compile(r"[0-9]{1,3}\.[0-9]{1,3}")
_bootmode_name_key_re = re.compile(r"boot-mode\.[-a-z_]*\.name$")
_bootmode_kernelopts_key_re = re.compile(r"boot-mode\.[-a-z_]*\.kernelopts$")


class CoreFeatures(qubes.ext.Extension):
Expand Down Expand Up @@ -104,6 +106,34 @@ async def qubes_features_request(self, vm, event, untrusted_features):
untrusted_value = untrusted_features["qubes-agent-version"]
if _version_re.fullmatch(untrusted_value):
vm.features["qubes-agent-version"] = untrusted_value

# handle boot mode advertisement
for _, (
untrusted_feature_key, untrusted_feature_value
) in enumerate(untrusted_features.items()):
if not all(
c in string.printable for c in untrusted_feature_value
):
continue
if _bootmode_name_key_re.match(untrusted_feature_key):
bootmode_feature = untrusted_feature_key
bootmode_value = untrusted_feature_value
vm.features[bootmode_feature] = bootmode_value
elif _bootmode_kernelopts_key_re.match(untrusted_feature_key):
# Don't allow setting the kernelopts for the default mode,
# these are ignored anyway
if untrusted_feature_key == \
"boot-mode.default.kernelopts":
continue
bootmode_feature = untrusted_feature_key
bootmode_value = untrusted_feature_value
vm.features[bootmode_feature] = bootmode_value
elif untrusted_feature_key == "boot-mode.active" or \
untrusted_feature_key == "boot-mode.appvm-default":
bootmode_feature = untrusted_feature_key
bootmode_value = untrusted_feature_value
if bootmode_feature not in vm.features:
vm.features[bootmode_feature] = bootmode_value
del untrusted_features

# default user for qvm-run etc
Expand Down
20 changes: 19 additions & 1 deletion qubes/vm/appvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ def template_changed_update_storage(self):
self.storage.init_volume(volume_name, config)


def template_changed_update_bootmode(self):
"""Update boot mode configuration for TemplateVM changes"""
if self.template.features.get("boot-mode.appvm-default", None) is None:
self.features["boot-mode.active"] = "default"
else:
active_boot_mode = self.template.features["boot-mode.appvm-default"]
try:
_ = self.features.check_with_template(
f"boot-mode.{active_boot_mode}.kernelopts"
)
self.features["boot-mode.active"] = active_boot_mode
except KeyError:
self.template.features["boot-mode.appvm-default"] = "default"
self.features["boot-mode.active"] = "default"


class AppVM(
qubes.vm.mix.dvmtemplate.DVMTemplateMixin, qubes.vm.qubesvm.QubesVM
):
Expand Down Expand Up @@ -149,8 +165,10 @@ def on_property_pre_set_template(
@qubes.events.handler("property-set:template")
def on_property_set_template(self, event, name, newvalue, oldvalue=None):
"""Adjust root (and possibly other snap_on_start=True) volume
on template change.
on template change. Also switch active boot mode to match that set by
the template.
""" # pylint: disable=unused-argument
template_changed_update_storage(self)
template_changed_update_bootmode(self)
for vm in self.dispvms:
vm.on_property_set_template(event, name, newvalue, oldvalue)
31 changes: 31 additions & 0 deletions qubes/vm/qubesvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,28 @@ def _setter_kbd_layout(self, prop, value):
return value


def _bootmode_kernelopts(self):
"""
Return the kernel options specified by the currently active boot mode,
with a leading space. Force the VM's boot mode to 'default' if the active
boot mode does not exist. If the active boot mode is (or ends up set to)
'default', return an empty string.
"""
if self.features.get("boot-mode.active", None) is None:
return ""
if self.features["boot-mode.active"] == "default":
return ""
active_boot_mode = self.features["boot-mode.active"]
try:
kernelopts_value = self.features.check_with_template(
f"boot-mode.{active_boot_mode}.kernelopts"
)
return f" {kernelopts_value}"
except KeyError:
self.features["boot-mode.active"] = "default"
return ""


def _default_virt_mode(self):
if list(self.devices["pci"].get_assigned_devices()):
return "hvm"
Expand Down Expand Up @@ -659,6 +681,15 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM):
#
# properties loaded from XML
#
bootmode_kernelopts = qubes.property(
"bootmode_kernelopts",
type=str,
load_stage=4,
default=_bootmode_kernelopts,
doc="Additional kernel command line segment passed to domain, set by "
"the domain's active boot mode."
)

guivm = qubes.VMProperty(
"guivm",
load_stage=4,
Expand Down
12 changes: 8 additions & 4 deletions templates/libvirt/xen.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,19 @@
{% endif %}
{% if vm.kernel %}
{% if vm.features.check_with_template('no-default-kernelopts', False) -%}
<cmdline>{{ vm.kernelopts }}</cmdline>
<cmdline>{{ vm.kernelopts }}{{ vm.bootmode_kernelopts }}</cmdline>
{% elif vm.features.check_with_template('apparmor', '0') == '1' -%}
<cmdline>{{ vm.kernelopts_common }}{{ vm.kernelopts }} apparmor=1 security=apparmor</cmdline>
<cmdline>{{ vm.kernelopts_common }}{{ vm.kernelopts }} apparmor=1 security=apparmor{{ vm.bootmode_kernelopts }}</cmdline>
{% elif vm.features.check_with_template('selinux', '0') == '1' -%}
<cmdline>{{ vm.kernelopts_common }}{{ vm.kernelopts }} selinux=1 security=selinux</cmdline>
<cmdline>{{ vm.kernelopts_common }}{{ vm.kernelopts }} selinux=1 security=selinux{{ vm.bootmode_kernelopts }}</cmdline>
{% else -%}
<cmdline>{{ vm.kernelopts_common }}{{ vm.kernelopts }}</cmdline>
<cmdline>{{ vm.kernelopts_common }}{{ vm.kernelopts }}{{ vm.bootmode_kernelopts }}</cmdline>
{% endif -%}
{% endif %}
<!--
Support for appending vm.bootmode_kernelopts to the kernel
commandline for in-VM kernels should be added here.
-->
{% endblock %}
</os>

Expand Down