Skip to content

Commit

Permalink
WIP: Update apt cache for airgapped vms in background
Browse files Browse the repository at this point in the history
Open qubes-updater-gui after run completes.
  • Loading branch information
emkll committed Jan 6, 2020
1 parent 0a073ef commit 2e3d863
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 12 deletions.
50 changes: 46 additions & 4 deletions dom0/securedrop-updater-cli
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,15 @@ import subprocess
import sys

sdlog = logging.getLogger(__name__)
supported_vms = ["dom0", "sd-svs-disp", "fedora"]
# these are the TemplateVMs that require full patch level at boot in order to start the client
possible_vms = ["dom0", "sd-svs-disp", "fedora", "airgapped"]
# these are the TemplateVMs that for which the AppVMs are airgapped.
# The Qubes Update manager cannot check for updates, we must do so manually.
airgapped_vms = [
"sd-svs-buster-template",
"sd-log-buster-template",
"securedrop-workstation-buster",
]
current_fedora_template = "fedora-30"
current_sd_svs_disp_template = "sd-svs-disp-buster-template"

Expand All @@ -36,6 +44,18 @@ def main(argv):
elif "fedora" in args.check:
sdlog.debug("Checking for fedora updates")
sys.exit(check_updates_fedora())
elif "airgapped" in args.check:
sdlog.debug("Checking for updates for airgapped VMs")
rc = check_updates_airgapped()
try:
subprocess.Popen("qubes-update-gui")
except CalledProcessError as e:
sdlog.debug("Error while opening qubes-update-gui")
if rc == 0:
return UPDATES_OK
else:
return UPDATES_FAILED

elif args.update:
if "dom0" in args.update:
sdlog.debug("Updating dom0")
Expand Down Expand Up @@ -104,6 +124,20 @@ def check_updates_sd_svs_disp():
return UPDATES_OK


def check_updates_airgapped():
for vm in airgapped_vms:
try:
subprocess.check_call(["qvm-run", vm, "sudo apt update"])
subprocess.check_call(["qvm-shutdown", vm])
except subprocess.CalledProcessError as e:
sdlog.info("Failed to update apt cache for {}".format(vm))
return UPDATES_FAILED

sdlog.info("Updated apt cache for {}".format(vm))

return UPDATES_OK


def apply_updates_dom0():
sdlog.info("Updating dom0")
try:
Expand All @@ -122,7 +156,15 @@ def apply_updates_vm(vm):
sdlog.info("Updating {}".format(vm))
try:
subprocess.check_call(
["qubesctl", "--skip-dom0", "--targets", vm, "state.sls", "update.qubes-vm"]
[
"sudo",
"qubesctl",
"--skip-dom0",
"--targets",
vm,
"state.sls",
"update.qubes-vm",
]
)
except subprocess.CalledProcessError as e:
sdlog.info(
Expand Down Expand Up @@ -161,15 +203,15 @@ def parse_argv(argv):
"--check",
action="append",
type=str,
choices=supported_vms,
choices=possible_vms,
help="Checks updates on the target VM",
)
parser.add_argument(
"-u",
"--update",
action="append",
type=str,
choices=supported_vms,
choices=possible_vms,
help="Runs update for target VM",
)
parser.add_argument(
Expand Down
59 changes: 51 additions & 8 deletions launcher/sdw_updater_gui/UpdaterApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
VMS = "fedora", "dom0", "sd-svs-disp"
UPDATE_FLAGFILE = "/home/user/.securedrop_client/securedrop-update-required-flag"


class UpdaterApp(QtGui.QMainWindow, Ui_UpdaterDialog):
def __init__(self, parent=None):
super(UpdaterApp, self).__init__(parent)
Expand All @@ -30,18 +31,28 @@ def __init__(self, parent=None):
try:
subprocess.Popen(["qvm-start", "--skip-if-running", "sd-svs"])
except subprocess.CalledProcessError as e:
self.proposedActionLabel.setText("Error starting securedrop-client in sd-svs.")
self.proposedActionLabel.setText(
"Error starting securedrop-client in sd-svs."
)
self.logOutputText += "Securedrop-client has failed to start, please contact your administrator"
self.logOutput.setText(self.logOutputText)

self.progress += 1
self.progressBar.setProperty("value", self.progress)

# update thread is for critical VMs (in the foreground and requiring
# upgrades before launch)
self.update_thread = UpdateThread()
self.update_thread.start()
self.update_thread.signal.connect(self.update_status)

self.vms_to_update = []

# background update thread will be used for non-critical VMs, and the
# qubes update gui will be displayed after
self.background_update_thread = BackgroundUpdaterThread()
self.background_update_thread.start()
self.background_update_thread.signal.connect(self.update_status)

# This slot will receive update signals from UpdateThread
def update_status(self, result):
self.logOutputText += result["output"]
Expand All @@ -63,26 +74,34 @@ def update_status(self, result):
)
# write a wile to sd-svs, for client to parse
try:
subprocess.check_call(["qvm-run", "sd-svs", "touch {}".format(UPDATE_FLAGFILE)])
subprocess.check_call(
["qvm-run", "sd-svs", "touch {}".format(UPDATE_FLAGFILE)]
)
self.applyUpdatesButton.setEnabled(True)
except subprocess.CalledProcessError as e:
self.proposedActionLabel.setText("Error writing file to sd-svs.")
self.proposedActionLabel.setText(
"Error writing file to sd-svs."
)
self.logOutputText += "Failed to write file to sd-svs, please contact your administrator"
self.logOutput.setText(self.logOutputText)
else:
self.proposedActionLabel.setText("System up-to-date.")
# remove the flag file to indicate to sd-svs that critical VMs are up-to-date
try:
subprocess.check_call(["qvm-run", "sd-svs", "rm -f {}".format(UPDATE_FLAGFILE)])
subprocess.check_call(
["qvm-run", "sd-svs", "rm -f {}".format(UPDATE_FLAGFILE)]
)
self.clientOpenButton.setEnabled(True)
except subprocess.CalledProcessError as e:
self.proposedActionLabel.setText("Error writing file to sd-svs.")
self.proposedActionLabel.setText(
"Error writing file to sd-svs."
)
self.logOutputText += "Failed to write file to sd-svs, please contact your administrator"
self.logOutput.setText(self.logOutputText)
elif result["action"] == "upgrade":
self.proposedActionLabel.setText(
"Updates have been applied, system is now up-to-date."
#TODO: Reboot if xen updates, don't remove flag for now
# TODO: Reboot if xen updates, don't remove flag for now
)
self.progressBar.setProperty("value", 100)
self.clientOpenButton.setEnabled(True)
Expand Down Expand Up @@ -166,6 +185,7 @@ def run(self):
}
self.signal.emit(result)


# This thread will upgrade VMs that require upgrades
class UpgradeThread(QThread):
signal = pyqtSignal("PyQt_PyObject")
Expand Down Expand Up @@ -211,4 +231,27 @@ def run(self):
"fedora_upgrade_success": results.get("fedora", True),
"output": "Update check complete.",
}
self.signal.emit(result) # if reboot not needed, self.clientOpenButton.setEnabled(True)
self.signal.emit(result)


# This thread will upgrade VMs that don't require upgrades, and opens the
# qubes GUI updater afterwards
class BackgroundUpdaterThread(QThread):
signal = pyqtSignal("PyQt_PyObject")

def __init__(self):
QThread.__init__(self)

def run(self):
try:
p = subprocess.Popen(
["securedrop-updater-cli", "--check", "airgapped"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = p.communicate()
progress = {"output": str(out), "run_completed": False}
self.signal.emit(progress)
except subprocess.CalledProcessError as e:
progress = {"output": str(e), "run_completed": False}
self.signal.emit(progress)

0 comments on commit 2e3d863

Please sign in to comment.