Skip to content

Commit

Permalink
Add the feature to prohibit starting a qube
Browse files Browse the repository at this point in the history
Qube Manager part of preventing qubes with `prohibit-start` from being
started. Showing special status icon and disabling start and related
buttons.

Supplements: QubesOS/qubes-issues#9622
  • Loading branch information
alimirjamali committed Feb 21, 2025
1 parent 334419b commit 00dbb84
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 0 deletions.
1 change: 1 addition & 0 deletions icons/ban.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions qubesmanager/qube_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,16 @@ def __init__(self):
"Halted" : QIcon(":/blank")
}
self.outdatedIcons = {
"blocked" : QIcon(":/ban"),
"update" : QIcon(":/updateable"),
"outdated" : QIcon(":/outdated"),
"to-be-outdated" : QIcon(":/outdated"),
"eol": QIcon(':/warning'),
"skipped": QIcon(':/skipped')
}
self.outdatedTooltips = {
"blocked" : self.tr(
"The qube is prohibited from being started"),
"update" : self.tr("Updates available"),
"outdated" : self.tr(
"The qube must be restarted for recent changes in "
Expand Down Expand Up @@ -239,6 +242,12 @@ def update_power_state(self):

self.state['outdated'] = ""
try:
if self.vm.klass != "AdminVM" and manager_utils.get_feature(
self.vm, 'prohibit-start', False):
# Special case where being outdated, eol & skipped is irrelevant
self.state['outdated'] = 'blocked'
return

Check warning on line 249 in qubesmanager/qube_manager.py

View check run for this annotation

Codecov / codecov/patch

qubesmanager/qube_manager.py#L248-L249

Added lines #L248 - L249 were not covered by tests

if manager_utils.is_running(self.vm, False):
if hasattr(self.vm, 'template') and \
manager_utils.is_running(self.vm.template, False):
Expand Down Expand Up @@ -879,6 +888,10 @@ def __init__(self, qt_app, qubes_app, dispatcher, _parent=None):
self.on_domain_updates_available)
dispatcher.add_handler('domain-feature-delete:skip-update',
self.on_domain_updates_available)
dispatcher.add_handler('domain-feature-set:prohibit-start',
self.on_domain_updates_available)
dispatcher.add_handler('domain-feature-delete:prohibit-start',
self.on_domain_updates_available)

self.installEventFilter(self)

Expand Down Expand Up @@ -1125,6 +1138,9 @@ def check_updates(self, info=None):
try:
if info.vm.klass in {'TemplateVM', 'StandaloneVM'}:
if manager_utils.get_feature(
info.vm, 'prohibit-start', False):
info.state['outdated'] = 'blocked'

Check warning on line 1142 in qubesmanager/qube_manager.py

View check run for this annotation

Codecov / codecov/patch

qubesmanager/qube_manager.py#L1142

Added line #L1142 was not covered by tests
elif manager_utils.get_feature(
info.vm, 'skip-update', False):
info.state['outdated'] = 'skipped'
elif manager_utils.get_feature(
Expand Down Expand Up @@ -1352,6 +1368,17 @@ def table_selection_changed(self):
if not vm.updateable and vm.klass != 'AdminVM':
self.action_updatevm.setEnabled(False)

if vm.vm.features.get('prohibit-start', False):
self.action_open_console.setEnabled(False)
self.action_resumevm.setEnabled(False)
self.action_startvm_tools_install.setEnabled(False)
self.action_pausevm.setEnabled(False)
self.action_restartvm.setEnabled(False)
self.action_killvm.setEnabled(False)
self.action_shutdownvm.setEnabled(False)
self.action_updatevm.setEnabled(False)
self.action_run_command_in_vm.setEnabled(False)

Check warning on line 1380 in qubesmanager/qube_manager.py

View check run for this annotation

Codecov / codecov/patch

qubesmanager/qube_manager.py#L1372-L1380

Added lines #L1372 - L1380 were not covered by tests

self.update_template_menu()
self.update_network_menu()

Expand Down
1 change: 1 addition & 0 deletions qubesmanager/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def test_qubes_app():
test_qapp = MockQubesComplete()
test_qapp._qubes['sys-usb'].features[
'supported-feature.keyboard-layout'] = '1'
test_qapp._qubes['sys-usb'].features['prohibit-start'] = None
test_qapp.update_vm_calls()

return test_qapp
33 changes: 33 additions & 0 deletions qubesmanager/tests/test_qube_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1604,3 +1604,36 @@ def test_704_check_later(mock_timer, mock_question):

assert mock_question.call_count == 0
assert mock_timer.call_count == 1


def test_705_prohibit_start_vms(qubes_manager):
# Change `prohibit-start` feature for two qubes
prohibition = (
'test-old',
'admin.vm.feature.Set',
'prohibit-start',
b'Do not start - Qube is compromised by Gremlins!',
)
assert prohibition not in qubes_manager.qubes_app.actual_calls
qubes_manager.qubes_app.expected_calls[prohibition] = b'0\x00'

prohibition = (
'test-standalone',
'admin.vm.feature.Set',
'prohibit-start',
b'Ancient qube which breaks on update',
)
assert prohibition not in qubes_manager.qubes_app.actual_calls
qubes_manager.qubes_app.expected_calls[prohibition] = b'0\x00'

qubes_manager.qubes_app.update_vm_calls()

qubes_manager.qubes_app.domains['test-old'].features[ \
'prohibit-start'] = \
'Do not start - Qube is compromised by Gremlins!'
_select_vm(qubes_manager, 'test-old')

qubes_manager.qubes_app.domains['test-standalone'].features[ \
'prohibit-start'] = \
'Ancient qube which breaks on update'
_select_vm(qubes_manager, 'test-standalone')
1 change: 1 addition & 0 deletions resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<file alias="add">icons/add.svg</file>
<file alias="apps">icons/apps.svg</file>
<file alias="backup">icons/backup.svg</file>
<file alias="ban">icons/ban.svg</file>
<file alias="blank">icons/blank.svg</file>
<file alias="checked">icons/checked.svg</file>
<file alias="checkmark">icons/checkmark.svg</file>
Expand Down

0 comments on commit 00dbb84

Please sign in to comment.