diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a0d856b..e527bba0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: lint: runs-on: ubuntu-latest container: - image: registry.fedoraproject.org/fedora:32 + image: registry.fedoraproject.org/fedora:37 steps: - run: dnf install -y make - uses: actions/checkout@v4 @@ -20,7 +20,7 @@ jobs: build-rpm: runs-on: ubuntu-latest container: - image: registry.fedoraproject.org/fedora:32 + image: registry.fedoraproject.org/fedora:37 steps: - run: dnf install -y make git - uses: actions/checkout@v4 @@ -29,13 +29,15 @@ jobs: make install-deps pip install --require-hashes -r requirements/dev-requirements.txt - name: Build RPM - run: make build-rpm + run: | + git config --global --add safe.directory '*' + make build-rpm - name: Check reproducibility run: make reprotest launcher-tests: runs-on: ubuntu-latest container: - image: registry.fedoraproject.org/fedora:32 + image: registry.fedoraproject.org/fedora:37 steps: - run: dnf install -y make - uses: actions/checkout@v4 diff --git a/.github/workstation-ci.yml b/.github/workstation-ci.yml index 5747bdce..e1b019d4 100644 --- a/.github/workstation-ci.yml +++ b/.github/workstation-ci.yml @@ -1,2 +1,2 @@ # for -qubes: "4.1" +qubes: "4.2" diff --git a/Makefile b/Makefile index bb3d89ee..d9420a87 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,11 @@ DEFAULT_GOAL: help -# We prefer to use python3.8 if it's availabe, as that is the version shipped -# with Fedora 32, but we're also OK with just python3 if that's all we've got -PYTHON3 := $(if $(shell bash -c "command -v python3.8"), python3.8, python3) -# If we're on anything but Fedora 32, execute some commands in a container -CONTAINER := $(if $(shell grep "Thirty Two" /etc/fedora-release),,./scripts/container.sh) +# We prefer to use python3.11 if it's availabe, as that is the version shipped +# with Fedora 37, but we're also OK with just python3 if that's all we've got +PYTHON3 := $(if $(shell bash -c "command -v python3.11"), python3.8, python3) +# If we're on anything but Fedora 37, execute some commands in a container +# Note: if your development environment is Fedora 37 based, you may want to +# manually prepend ./scripts/container.sh to commands you want to execute +CONTAINER := $(if $(shell grep "Thirty Seven" /etc/fedora-release),,./scripts/container.sh) HOST=$(shell hostname) @@ -34,9 +36,12 @@ build-rpm: ## Build RPM package .PHONY: reprotest reprotest: ## Check RPM package reproducibility - TERM=xterm-256color $(CONTAINER) bash -c "sudo ln -s $$PWD/scripts/fake-setarch.py /usr/local/bin/setarch && sudo reprotest 'make build-rpm' 'rpm-build/RPMS/noarch/*.rpm' --variations '+all,+kernel,-fileordering,-domain_host'" + TERM=xterm-256color $(CONTAINER) bash -c "sudo ln -s $$PWD/scripts/fake-setarch.py /usr/local/bin/setarch && sudo reprotest 'make build-rpm' 'rpm-build/RPMS/noarch/*.rpm' --variations '+all,+kernel,-time,-fileordering,-domain_host'" + @echo + @echo Warning! Temporarily removed time variations for reprotest. + @echo Suspecting upstream issues in rpm land is causing issues with 1 file\'s modification time not being clamped correctly only in a reprotest environment. -# Installs Fedora 32 package dependencies, to build RPMs and run tests, +# Installs Fedora 37 package dependencies, to build RPMs and run tests, # primarily useful in CI/containers .PHONY: install-deps install-deps: diff --git a/bootstrap/Dockerfile b/bootstrap/Dockerfile index a7deb544..06693e0c 100644 --- a/bootstrap/Dockerfile +++ b/bootstrap/Dockerfile @@ -1,6 +1,6 @@ -FROM registry.fedoraproject.org/fedora:32 +FROM registry.fedoraproject.org/fedora:37 LABEL org="Freedom of the Press" -LABEL image_name="securedrop-workstation-qubes-4.1" +LABEL image_name="securedrop-workstation-qubes-4.2" ARG USER_NAME ENV USER_NAME ${USER_NAME:-root} diff --git a/dom0/sd-app.sls b/dom0/sd-app.sls index 1a412604..811d0ab4 100644 --- a/dom0/sd-app.sls +++ b/dom0/sd-app.sls @@ -47,7 +47,7 @@ sd-app-template-sync-appmenus: cmd.run: - name: > qvm-start --skip-if-running sd-small-{{ sdvars.distribution }}-template && - qvm-sync-appmenus sd-small-{{ sdvars.distribution }}-template + qvm-sync-appmenus --force-root sd-small-{{ sdvars.distribution }}-template - require: - qvm: sd-small-{{ sdvars.distribution }}-template - onchanges: diff --git a/dom0/sd-clean-all.sls b/dom0/sd-clean-all.sls index c012fd1b..ccc86a59 100644 --- a/dom0/sd-clean-all.sls +++ b/dom0/sd-clean-all.sls @@ -5,7 +5,7 @@ set-fedora-as-default-dispvm: cmd.run: - - name: qvm-check fedora-38-dvm && qubes-prefs default_dispvm fedora-38-dvm || qubes-prefs default_dispvm '' + - name: qvm-check default-dvm && qubes-prefs default_dispvm default-dvm || qubes-prefs default_dispvm '' {% set gui_user = salt['cmd.shell']('groupmems -l -g qubes') %} @@ -23,7 +23,7 @@ restore-sys-usb-dispvm-halt-wait: restore-sys-usb-dispvm: qvm.prefs: - name: sys-usb - - template: fedora-38-dvm + - template: default-dvm - require: - cmd: restore-sys-usb-dispvm-halt-wait - cmd: set-fedora-as-default-dispvm @@ -34,11 +34,11 @@ restore-sys-usb-dispvm-start: - require: - qvm: restore-sys-usb-dispvm -# autoattach modifications are only present in sd-fedora-38-dvm +# autoattach modifications are only present in sd-fedora-39-dvm # so no more sd-usb-autoattach-remove necessary remove-sd-fedora-dispvm: qvm.absent: - - name: sd-fedora-38-dvm + - name: sd-fedora-39-dvm - require: - qvm: restore-sys-usb-dispvm {% else %} diff --git a/dom0/sd-clean-default-dispvm.sls b/dom0/sd-clean-default-dispvm.sls index d94c0bfb..5d5c8bde 100644 --- a/dom0/sd-clean-default-dispvm.sls +++ b/dom0/sd-clean-default-dispvm.sls @@ -3,4 +3,4 @@ set-fedora-as-default-dispvm: cmd.run: - - name: qvm-check fedora-38-dvm && qubes-prefs default_dispvm fedora-38-dvm || qubes-prefs default_dispvm '' + - name: qvm-check default-dvm && qubes-prefs default_dispvm default-dvm || qubes-prefs default_dispvm '' diff --git a/dom0/sd-default-config.sls b/dom0/sd-default-config.sls index 7e8960f3..403983d1 100644 --- a/dom0/sd-default-config.sls +++ b/dom0/sd-default-config.sls @@ -28,6 +28,6 @@ {% endif %} # Append repo URL with appropriate dom0 Fedora version -{% set fedora_repo = "f32" %} -{% set _ = sdvars.update({"distribution": "bullseye"}) %} +{% set fedora_repo = "f37" %} +{% set _ = sdvars.update({"distribution": "bookworm"}) %} {% set _ = sdvars.update({"dom0_yum_repo_url": sdvars["dom0_yum_repo_url"] + fedora_repo}) %} diff --git a/dom0/sd-devices.sls b/dom0/sd-devices.sls index 248a22fa..78f24039 100644 --- a/dom0/sd-devices.sls +++ b/dom0/sd-devices.sls @@ -36,11 +36,12 @@ sd-devices-dvm: # Ensure the Qubes menu is populated with relevant app entries, # so that Nautilus/Files can be started via GUI interactions. +# TODO: debian-12-minimal doesn't have passwordless root - do we want to add it? sd-devices-template-sync-appmenus: cmd.run: - name: > qvm-start --skip-if-running sd-large-{{ sdvars.distribution }}-template && - qvm-sync-appmenus sd-large-{{ sdvars.distribution }}-template + qvm-sync-appmenus --force-root sd-large-{{ sdvars.distribution }}-template - require: - qvm: sd-large-{{ sdvars.distribution }}-template - onchanges: diff --git a/dom0/sd-dom0-files.sls b/dom0/sd-dom0-files.sls index ba17e91c..1d4837bb 100644 --- a/dom0/sd-dom0-files.sls +++ b/dom0/sd-dom0-files.sls @@ -46,31 +46,11 @@ dom0-workstation-rpm-repo: - require: - file: dom0-rpm-test-key -dom0-workstation-templates-repo: - # Using file.blockreplace because /etc/qubes/repo-templates/ is not a .d - # style directory, and qvm.template_installed:fromrepo seems to only support - # using a repo from this file. Installing manually via a cli-command-instead? - file.blockreplace: - - name: /etc/qubes/repo-templates/qubes-templates.repo - - append_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - [securedrop-workstation-templates] - gpgcheck=1 - gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-securedrop-workstation - enabled=1 - baseurl={{ sdvars.dom0_yum_repo_url }} - name=SecureDrop Workstation Templates repository - - require: - - file: dom0-rpm-test-key - -dom0-install-securedrop-workstation-template: +# TODO: trying out debian-12-minimal - this should be parameterised +dom0-install-debian-base-template: cmd.run: - name: > - qvm-template info --machine-readable securedrop-workstation-{{ sdvars.distribution }} | grep -q "installed|securedrop-workstation-{{ sdvars.distribution }}|" || qvm-template install securedrop-workstation-{{ sdvars.distribution }} - - require: - - file: dom0-workstation-rpm-repo + qvm-template info --machine-readable debian-12-minimal | grep -q "installed|debian-12-minimal|" || qvm-template install debian-12-minimal # Create directory for storing SecureDrop-specific icons dom0-securedrop-icons-directory: diff --git a/dom0/sd-dom0-qvm-rpc.sls b/dom0/sd-dom0-qvm-rpc.sls index bb23e846..9b94c506 100644 --- a/dom0/sd-dom0-qvm-rpc.sls +++ b/dom0/sd-dom0-qvm-rpc.sls @@ -6,105 +6,8 @@ # As a general strategy, in addition to explicit grants, we provide # catch-all deny policies for SDW-provisioned VMs. Where possible, # we prefer to prepend SDW policies, in order to support overrides -# for the general system. We use the 'blockreplace' Salt state -# to achieve this for the 4.0-style grants, and order the policy -# files numerically for the 4.1-style grants. +# for the general system. # -## - -# Certain policies use the legacy format (i.e. in /etc/qubes-rpc/policy/) -# under both Qubes 4.0 & 4.1. Under 4.1, we continue to use the legacy path, -# because the backwards-compatibility logic loads those files first, -# via /etc/qubes/policy.d/35-compat.policy. Since first match wins, -# we want our overrides to be present early, during the backwards compat loading. -dom0-rpc-qubes.ClipboardPaste: - file.blockreplace: - - name: /etc/qubes-rpc/policy/qubes.ClipboardPaste - - prepend_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - @tag:sd-send-app-clipboard sd-app ask - sd-app @tag:sd-receive-app-clipboard ask - @anyvm @tag:sd-workstation deny - @tag:sd-workstation @anyvm deny - -dom0-rpc-qubes.PdfConvert: - file.blockreplace: - - name: /etc/qubes-rpc/policy/qubes.PdfConvert - - prepend_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - @anyvm @tag:sd-workstation deny - @tag:sd-workstation @anyvm deny - -dom0-rpc-qubes.USB: - file.blockreplace: - - name: /etc/qubes-rpc/policy/qubes.USB - - prepend_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - sd-devices sys-usb allow - @anyvm @tag:sd-workstation deny - @tag:sd-workstation @anyvm deny - -dom0-rpc-qubes.ensure.USBAttach: - file.managed: - - name: /etc/qubes-rpc/policy/qubes.USBAttach - - contents: | - @anyvm @anyvm ask - - replace: false - -dom0-rpc-qubes.USBAttach: - file.blockreplace: - - name: /etc/qubes-rpc/policy/qubes.USBAttach - - prepend_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - sys-usb sd-devices allow,user=root - @anyvm @tag:sd-workstation deny - @tag:sd-workstation @anyvm deny - - require: - - file: dom0-rpc-qubes.ensure.USBAttach - -# The GPG policies still exist in the legacy location on 4.1, -# and the legacy locations take precedence over SDW rules due -# to the import in `/etc/qubes/policy.d/35-compat.policy`, -# so we'll maintain them in the old location. -dom0-rpc-qubes.Gpg: - file.blockreplace: - - name: /etc/qubes-rpc/policy/qubes.Gpg - - prepend_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - @tag:sd-client sd-gpg allow - @anyvm @tag:sd-workstation deny - @tag:sd-workstation @anyvm deny - -dom0-rpc-qubes.GpgImportKey: - file.blockreplace: - - name: /etc/qubes-rpc/policy/qubes.GpgImportKey - - prepend_if_not_found: True - - marker_start: "### BEGIN securedrop-workstation ###" - - marker_end: "### END securedrop-workstation ###" - - content: | - @tag:sd-client sd-gpg allow - @anyvm @tag:sd-workstation deny - @tag:sd-workstation @anyvm deny - - -# Permit the SecureDrop Proxy to manage Client connections -dom0-rpc-securedrop.Proxy: - file.prepend: - - name: /etc/qubes-rpc/policy/securedrop.Proxy - - text: | - sd-app sd-proxy allow - @anyvm @anyvm deny - # Qubes suggests using files starting with 70- to be the allow policies # and 60- deny policies, but due to the way SDW policies are stacked at the # moment, we reverse this suggested order @@ -114,6 +17,27 @@ dom0-rpc-qubes.r5-format-deny: - contents: | securedrop.Log * @anyvm @anyvm deny + securedrop.Proxy * @anyvm @anyvm deny + + qubes.GpgImportKey * @anyvm @tag:sd-workstation deny + qubes.GpgImportKey * @tag:sd-workstation @anyvm deny + + qubes.Gpg * @anyvm @tag:sd-workstation deny + qubes.Gpg * @tag:sd-workstation @anyvm deny + + qubes.USBAttach * @anyvm @tag:sd-workstation deny + qubes.USBAttach * @tag:sd-workstation @anyvm deny + + qubes.USB * @anyvm @tag:sd-workstation deny + qubes.USB * @tag:sd-workstation @anyvm deny + + qubes.PdfConvert * @anyvm @tag:sd-workstation deny + qubes.PdfConvert * @tag:sd-workstation @anyvm deny + + # TODO: should this be handled with the new Global Config UI instead? + qubes.ClipboardPaste * @anyvm @tag:sd-workstation deny + qubes.ClipboardPaste * @tag:sd-workstation @anyvm deny + qubes.FeaturesRequest * @anyvm @tag:sd-workstation deny qubes.FeaturesRequest * @tag:sd-workstation @anyvm deny @@ -146,10 +70,23 @@ dom0-rpc-qubes.r5-format-ask-allow: securedrop.Log * sd-log sd-log deny notify=no securedrop.Log * @tag:sd-workstation sd-log allow + securedrop.Proxy * sd-app sd-proxy allow + + qubes.Gpg * @tag:sd-client sd-gpg allow + + qubes.USBAttach * sys-usb sd-devices allow user=root + qubes.USBAttach * @anyvm @anyvm ask + + qubes.USB * sd-devices sys-usb allow + + # TODO: should this be handled with the new Global Config UI instead? + qubes.ClipboardPaste * @tag:sd-send-app-clipboard sd-app ask + qubes.ClipboardPaste * sd-app @tag:sd-receive-app-clipboard ask + qubes.Filecopy * sd-log @default ask qubes.Filecopy * sd-log @tag:sd-receive-logs ask qubes.Filecopy * sd-proxy @tag:sd-client allow qubes.OpenInVM * @tag:sd-client @dispvm:sd-viewer allow qubes.OpenInVM * @tag:sd-client sd-devices allow - qubes.OpenInVM * sd-devices @dispvm:sd-viewer allow \ No newline at end of file + qubes.OpenInVM * sd-devices @dispvm:sd-viewer allow diff --git a/dom0/sd-logging-setup.sls b/dom0/sd-logging-setup.sls index e5e1d80d..7cdee475 100644 --- a/dom0/sd-logging-setup.sls +++ b/dom0/sd-logging-setup.sls @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # vim: set syntax=yaml ts=2 sw=2 sts=2 et : -{% if grains['id'] in ["securedrop-workstation-bullseye", "sd-small-bullseye-template", "sd-large-bullseye-template"] %} +# TODO: parametrise this +{% if grains['id'] in ["sd-small-bookworm-template", "sd-large-bookworm-template"] %} include: - fpf-apt-repo diff --git a/dom0/sd-sys-vms.sls b/dom0/sd-sys-vms.sls index 6dc718ff..2ceb3962 100644 --- a/dom0/sd-sys-vms.sls +++ b/dom0/sd-sys-vms.sls @@ -9,22 +9,23 @@ include: # DispVM is created - qvm.default-dispvm -{% set sd_supported_fedora_version = 'fedora-38' %} - +# 4.2 fedora template is fedora-NN-xfce, but let's keep the dvm names to +# follow simple - like sd-fedora-NN-dvm +{% set sd_supported_fedora_version = 'fedora-39' %} +{% set sd_fedora_base_template = sd_supported_fedora_version + '-xfce' %} # Install latest templates required for SDW VMs. dom0-install-fedora-template: cmd.run: - name: > - qvm-template info --machine-readable {{ sd_supported_fedora_version }} | grep -q "installed|{{ sd_supported_fedora_version }}|" || qvm-template install {{ sd_supported_fedora_version }} + qvm-template info --machine-readable {{ sd_fedora_base_template }} | grep -q "installed|{{ sd_fedora_base_template }}|" || qvm-template install {{ sd_fedora_base_template }} # Update the mgmt VM before updating the new Fedora VM. The order is required -# and listed in the release notes for F32 & F33. set-fedora-template-as-default-mgmt-dvm: cmd.run: - name: > qvm-shutdown --wait default-mgmt-dvm && - qvm-prefs default-mgmt-dvm template {{ sd_supported_fedora_version }} + qvm-prefs default-mgmt-dvm template {{ sd_fedora_base_template }} - require: - cmd: dom0-install-fedora-template @@ -44,7 +45,7 @@ update-fedora-template-if-new: # is sets the default_dispvm to the DispVM based on the wanted Fedora version. set-fedora-default-template-version: cmd.run: - - name: qubes-prefs default_template {{ sd_supported_fedora_version }} + - name: qubes-prefs default_template {{ sd_fedora_base_template }} - require: - cmd: dom0-install-fedora-template - sls: qvm.default-dispvm @@ -56,6 +57,7 @@ set-fedora-default-template-version: # sys-usb is also disposable by default but a special case as we want to # customize the underlying DispVM template for usability purposes: we want to # consistently auto-attach USB devices to our sd-devices qube +# {% set required_dispvms = [ sd_supported_fedora_version + '-dvm' ] %} {% if salt['pillar.get']('qvm:sys-usb:disposable', true) %} {% set _ = required_dispvms.append("sd-" + sd_supported_fedora_version + "-dvm") %} @@ -66,10 +68,10 @@ create-{{ required_dispvm }}: qvm.vm: - name: {{ required_dispvm }} - present: - - template: {{ sd_supported_fedora_version }} + - template: {{ sd_fedora_base_template }} - label: red - prefs: - - template: {{ sd_supported_fedora_version }} + - template: {{ sd_fedora_base_template }} - template_for_dispvms: True {% if required_dispvm == 'sd-' + sd_supported_fedora_version + '-dvm' %} - netvm: "" @@ -91,7 +93,7 @@ create-{{ required_dispvm }}: {% set sd_supported_fedora_template = sd_supported_fedora_version + '-dvm' %} {% endif %} {% else %} - {% set sd_supported_fedora_template = sd_supported_fedora_version %} + {% set sd_supported_fedora_template = sd_fedora_base_template %} {% endif %} {% if salt['cmd.shell']('qvm-prefs ' + sys_vm + ' template') != sd_supported_fedora_template %} sd-{{ sys_vm }}-fedora-version-halt: diff --git a/dom0/sd-workstation-template.sls b/dom0/sd-workstation-template.sls index 39b4fa04..52b38c30 100644 --- a/dom0/sd-workstation-template.sls +++ b/dom0/sd-workstation-template.sls @@ -7,24 +7,8 @@ include: - sd-dom0-files -# Sets virt_mode and kernel to use custom hardened kernel. -sd-workstation-template: - qvm.vm: - - name: securedrop-workstation-{{ sdvars.distribution }} - - prefs: - - virt-mode: hvm - - kernel: '' - - tags: - - add: - - sd-workstation - - sd-{{ sdvars.distribution }} - - features: - - enable: - - service.paxctld - - require: - - cmd: dom0-install-securedrop-workstation-template - # Installs consolidated templateVMs: +# Sets virt_mode and kernel to use custom hardened kernel. # - sd-small-{{ sdvars.distribution }}-template, to be used for # sd-app, sd-gpg, sd-log, and sd-proxy # - sd-large-{{ sdvars.distribution }}-template, to be used for @@ -33,24 +17,38 @@ sd-small-{{ sdvars.distribution }}-template: qvm.vm: - name: sd-small-{{ sdvars.distribution }}-template - clone: - - source: securedrop-workstation-{{ sdvars.distribution }} + - source: debian-12-minimal - label: red +# TODO: add HVM and/or custom kernel support back. Previously added in base template +# - prefs: +# - virt-mode: hvm +# - kernel: '' - tags: - add: - sd-workstation - sd-{{ sdvars.distribution }} + - features: + - enable: + - service.paxctld - require: - - qvm: sd-workstation-template + - cmd: dom0-install-debian-base-template sd-large-{{ sdvars.distribution }}-template: qvm.vm: - name: sd-large-{{ sdvars.distribution }}-template - clone: - - source: securedrop-workstation-{{ sdvars.distribution }} + - source: debian-12-minimal - label: red +# TODO: add HVM and/or custom kernel support back. Previously added in base template +# - prefs: +# - virt-mode: hvm +# - kernel: '' - tags: - add: - sd-workstation - sd-{{ sdvars.distribution }} + - features: + - enable: + - service.paxctld - require: - - qvm: sd-workstation-template + - cmd: dom0-install-debian-base-template diff --git a/dom0/sd-workstation.top b/dom0/sd-workstation.top index 9c5610b7..db5b5e51 100644 --- a/dom0/sd-workstation.top +++ b/dom0/sd-workstation.top @@ -20,12 +20,12 @@ base: - sd-whonix - sd-remove-unused-templates - sd-small-bullseye-template: + sd-small-bookworm-template: - sd-logging-setup - sd-workstation-template-files - sd-app-files - sd-proxy-template-files - sd-large-bullseye-template: + sd-large-bookworm-template: - sd-logging-setup - sd-workstation-template-files - sd-devices-files @@ -38,10 +38,7 @@ base: - sd-mime-handling sd-whonix: - sd-whonix-hidserv-key - securedrop-workstation-bullseye: - - sd-workstation-template-files - - sd-logging-setup - 'sd-fedora-38-dvm,sys-usb': + 'sd-fedora-39-dvm,sys-usb': - match: list - sd-usb-autoattach-add sd-log: diff --git a/dom0/securedrop-handle-upgrade b/dom0/securedrop-handle-upgrade index 127333c2..f5191839 100755 --- a/dom0/securedrop-handle-upgrade +++ b/dom0/securedrop-handle-upgrade @@ -15,9 +15,9 @@ if [[ $TASK == "prepare" ]]; then # sd-app, we simply shutdown the machine as we want to preserve the data if qvm-check sd-app --quiet; then BASE_TEMPLATE=$(qvm-prefs sd-app template) - if [[ ! $BASE_TEMPLATE =~ "small-bullseye" ]]; then + if [[ ! $BASE_TEMPLATE =~ "small-bookworm" ]]; then if qvm-check --running sd-app; then - qvm-shutdown --wait sd-app + qvm-shutdown --wait sd-app || true fi fi fi @@ -30,18 +30,18 @@ if [[ $TASK == "prepare" ]]; then # provisioning process runs again and sets that value to sd-viewer if qvm-check --quiet sd-viewer; then BASE_TEMPLATE=$(qvm-prefs sd-viewer template) - if [[ ! $BASE_TEMPLATE =~ "large-bullseye" ]]; then + if [[ ! $BASE_TEMPLATE =~ "large-bookworm" ]]; then qubes-prefs default_dispvm '' - qvm-shutdown --wait sd-viewer + qvm-shutdown --wait sd-viewer || true qvm-remove -f sd-viewer fi fi if qvm-check --quiet sd-devices; then BASE_TEMPLATE=$(qvm-prefs sd-devices-dvm template) - if [[ ! $BASE_TEMPLATE =~ "large-bullseye" ]]; then - qvm-shutdown --wait sd-devices - qvm-shutdown --wait sd-devices-dvm + if [[ ! $BASE_TEMPLATE =~ "large-bookworm" ]]; then + qvm-shutdown --wait sd-devices || true + qvm-shutdown --wait sd-devices-dvm || true qvm-remove -f sd-devices qvm-remove -f sd-devices-dvm fi @@ -52,8 +52,8 @@ if [[ $TASK == "prepare" ]]; then # shutdown if a client is connected. if qvm-check --quiet sd-proxy; then BASE_TEMPLATE=$(qvm-prefs sd-proxy template) - if [[ ! $BASE_TEMPLATE =~ "large-bullseye" ]]; then - qvm-shutdown --wait sd-proxy + if [[ ! $BASE_TEMPLATE =~ "large-bookworm" ]]; then + qvm-shutdown --wait sd-proxy || true fi fi @@ -63,8 +63,8 @@ if [[ $TASK == "prepare" ]]; then if qvm-check --quiet sd-whonix; then BASE_TEMPLATE=$(qvm-prefs sd-whonix template) if [[ ! $BASE_TEMPLATE =~ "17" ]]; then - qvm-shutdown --wait sd-proxy - qvm-shutdown --wait sd-whonix + qvm-shutdown --wait sd-proxy || true + qvm-shutdown --wait sd-whonix || true fi fi @@ -83,21 +83,23 @@ if [[ $TASK == "prepare" ]]; then # For sd-gpg, we simply shutdown the machine if qvm-check --quiet sd-gpg; then BASE_TEMPLATE=$(qvm-prefs sd-gpg template) - if [[ ! $BASE_TEMPLATE =~ "small-bullseye" ]]; then - qvm-shutdown --wait sd-gpg + if [[ ! $BASE_TEMPLATE =~ "small-bookworm" ]]; then + qvm-shutdown --wait sd-gpg || true fi fi # Shut down sd-log last, since other VMs will autostart it by sending logs if qvm-check --quiet sd-log; then BASE_TEMPLATE=$(qvm-prefs sd-log template) - if [[ ! $BASE_TEMPLATE =~ "small-bullseye" ]]; then - qvm-shutdown --wait sd-log + if [[ ! $BASE_TEMPLATE =~ "small-bookworm" ]]; then + qvm-shutdown --wait sd-log || true fi fi elif [[ $TASK == "remove" ]]; then # For each template, ensure the TemplateVM exists, that it is shut down # before deleting it. + # TODO: clean this up, we don't have separate templates anymore and nobody + # will be upgrading from the original setup for template in sd-app-template sd-viewer-template sd-devices-template sd-proxy-template \ sd-svs-template sd-svs-disp-template sd-export-template sd-proxy-template \ sd-svs-bullseye-template sd-export-bullseye-template sd-svs-disp-bullseye-template sd-app-bullseye-template \ @@ -105,7 +107,7 @@ elif [[ $TASK == "remove" ]]; then do if qvm-check "${template}" --quiet; then if qvm-check --running "${template}"; then - qvm-shutdown --wait "${template}" + qvm-shutdown --wait "${template}" || true fi qvm-remove -f "${template}" fi diff --git a/files/provision-all b/files/provision-all index 5dd3b140..30864b9d 100755 --- a/files/provision-all +++ b/files/provision-all @@ -31,8 +31,8 @@ echo "Provision all SecureDrop Workstation VMs with service-specific configs" sudo qubesctl --show-output --max-concurrency "$max_concurrency" --skip-dom0 --targets "$all_sdw_vms_target" state.highstate echo "Add SecureDrop export device handling to sys-usb" -# If sd-fedora-38-dvm exists it's because salt determined that sys-usb was disposable -qvm-check --quiet sd-fedora-38-dvm 2> /dev/null && \ - sudo qubesctl --show-output --skip-dom0 --targets sd-fedora-38-dvm state.highstate && \ +# If sd-fedora-39-dvm exists it's because salt determined that sys-usb was disposable +qvm-check --quiet sd-fedora-39-dvm 2> /dev/null && \ + sudo qubesctl --show-output --skip-dom0 --targets sd-fedora-39-dvm state.highstate && \ qvm-shutdown --wait sys-usb && qvm-start sys-usb || \ sudo qubesctl --show-output --skip-dom0 --targets sys-usb state.highstate diff --git a/launcher/Makefile b/launcher/Makefile index 814a0215..4398c80d 100644 --- a/launcher/Makefile +++ b/launcher/Makefile @@ -21,7 +21,7 @@ bandit: .PHONY: test test: - xvfb-run python -m pytest --cov-report term-missing --cov=sdw_notify --cov=sdw_updater_gui/ --cov=sdw_util -v tests/ + xvfb-run python3 -m pytest --cov-report term-missing --cov=sdw_notify --cov=sdw_updater_gui/ --cov=sdw_util -v tests/ black: ## Runs the black code formatter on the launcher code black --check --exclude .venv --line-length=100 . diff --git a/launcher/README.md b/launcher/README.md index acf457cc..68589a83 100644 --- a/launcher/README.md +++ b/launcher/README.md @@ -4,8 +4,6 @@ The Updater ensures that the SecureDrop Workstation is up-to-date by checking fo ## Running the Updater -Qubes 4.1.1 uses an end-of-life Fedora template in dom0 (fedora-32). See rationale here: https://www.qubes-os.org/doc/supported-releases/#note-on-dom0-and-eol. - To run the preflight updater: 1. Open a `dom0` terminal 2. Run `/opt/securedrop/launcher/sdw-launcher.py --skip-delta 0` @@ -25,4 +23,4 @@ To run the preflight updater outside of `dom0`: 3. Set up your virtual environment by running `make venv && source .venv/bin/activate` 4. Now you can run the updater: `./sdw-launcher.py` (it won't actually update VMs unless you are in `dom0`) 5. You can also run the notifier: `./sdw-notify.py` -6. And, finally, tests and linters by running: `sudo apt install xvfb`, then `make check` \ No newline at end of file +6. And, finally, tests and linters by running: `sudo apt install xvfb`, then `make check` diff --git a/launcher/dev-requirements.txt b/launcher/dev-requirements.txt index 62658b88..5ca51238 100644 --- a/launcher/dev-requirements.txt +++ b/launcher/dev-requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # pip-compile --allow-unsafe --generate-hashes --output-file=dev-requirements.txt dev-requirements.in # @@ -145,40 +145,58 @@ pytest-cov==3.0.0 \ --hash=sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6 \ --hash=sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470 # via -r dev-requirements.in -pyyaml==6.0 \ - --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ - --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ - --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ - --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ - --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ - --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ - --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ - --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ - --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ - --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ - --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ - --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ - --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ - --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ - --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ - --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ - --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ - --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ - --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ - --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ - --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ - --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ - --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ - --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ - --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ - --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ - --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ - --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ - --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ - --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ - --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ - --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ - --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 +pyyaml==6.0.1 \ + --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ + --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ + --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ + --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ + --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ + --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ + --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ + --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ + --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ + --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ + --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ + --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ + --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ + --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ + --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ + --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ + --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ + --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ + --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ + --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ + --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ + --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ + --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ + --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ + --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ + --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ + --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ + --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ + --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ + --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \ + --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ + --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ + --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ + --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ + --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ + --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ + --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ + --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ + --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ + --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ + --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ + --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ + --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ + --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ + --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ + --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ + --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ + --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ + --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ + --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ + --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f # via bandit rich==13.7.0 \ --hash=sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa \ diff --git a/launcher/sdw_updater_gui/Updater.py b/launcher/sdw_updater_gui/Updater.py index c42f0a79..670fb0bf 100644 --- a/launcher/sdw_updater_gui/Updater.py +++ b/launcher/sdw_updater_gui/Updater.py @@ -31,7 +31,7 @@ # logic to leverage the Qubes Python API. MIGRATION_DIR = "/tmp/sdw-migrations" # nosec -DEBIAN_VERSION = "bullseye" +DEBIAN_VERSION = "bookworm" sdlog = Util.get_logger(module=__name__) detail_log = Util.get_logger(prefix=DETAIL_LOGGER_PREFIX, module=__name__) @@ -40,7 +40,7 @@ # as well as their associated TemplateVMs. # In the future, we could use qvm-prefs to extract this information. current_vms = { - "fedora": "fedora-38", + "fedora": "fedora-39-xfce", "sd-viewer": "sd-large-{}-template".format(DEBIAN_VERSION), "sd-app": "sd-small-{}-template".format(DEBIAN_VERSION), "sd-log": "sd-small-{}-template".format(DEBIAN_VERSION), @@ -104,40 +104,54 @@ def migration_is_required(): return result -def apply_updates(vms=current_templates, progress_start=15, progress_end=75): +def apply_updates_dom0(): """ - Apply updates to all TemplateVMs. - - Returns a tuple of (vm_name, percentage_progress, upgrade_results), - for use in updating the GUI progress bar. + Apply updates to dom0 """ - sdlog.info("Applying all updates to VMs: {}".format(vms)) - # Figure out how much each completed VM should bump the progress bar. - assert progress_end > progress_start - progress_step = (progress_end - progress_start) // len(vms) + sdlog.info("Applying all updates to dom0") - progress_current = progress_start - - for vm in vms: - upgrade_results = UpdateStatus.UPDATES_FAILED - if vm == "dom0": - dom0_status = _check_updates_dom0() - if dom0_status == UpdateStatus.UPDATES_REQUIRED: - upgrade_results = _apply_updates_dom0() - else: - upgrade_results = UpdateStatus.UPDATES_OK - else: - upgrade_results = _apply_updates_vm(vm) + dom0_status = _check_updates_dom0() + if dom0_status == UpdateStatus.UPDATES_REQUIRED: + upgrade_results = _apply_updates_dom0() + else: + upgrade_results = UpdateStatus.UPDATES_OK + return upgrade_results - progress_current += progress_step - # constrain progress_current to given progress range - def clamp(val, min_val, max_val): - return max(min(max_val, val), min_val) +def apply_updates_templates(templates=current_templates): + """ + Apply updates to all TemplateVMs. + """ + sdlog.info("Applying all updates to VMs: {}".format(templates)) + try: + update_cmd = [ + "qubes-vm-update", + "--restart", # Enforce app qube restarts + "--no-progress", # Progress does not play nicely with text-logging + "--show-output", # Debug information on updated packages and success status + "--targets", + ",".join(templates) + ] + update_cmd_output = subprocess.check_output( + update_cmd, stderr=subprocess.STDOUT + ) + update_status = UpdateStatus.UPDATES_OK + sdlog.info("Update successful.") + except subprocess.CalledProcessError as e: + update_cmd_output = e.output + sdlog.error( + "An error has occurred updating templates. Please contact your administrator." + " See {} for details.".format(DETAIL_LOG_FILE) + ) + sdlog.error(str(e)) + update_status = UpdateStatus.UPDATES_FAILED - progress_current = clamp(progress_current, progress_start, progress_end) + if update_cmd_output: + cmd_for_log = " ".join(update_cmd) + clean_output = Util.strip_ansi_colors(update_cmd_output.decode("utf-8").strip()) + detail_log.info("Output from update command: {}\n{}".format(cmd_for_log, clean_output)) - yield vm, progress_current, upgrade_results + return update_status def _check_updates_dom0(): @@ -177,33 +191,6 @@ def _apply_updates_dom0(): return UpdateStatus.REBOOT_REQUIRED -def _apply_updates_vm(vm): - """ - Apply updates to a given TemplateVM. Any update to the base fedora template - will require a reboot after the upgrade. - """ - sdlog.info("Updating {}".format(vm)) - - # We run custom Salt logic for our own Debian-based TemplateVMs - if vm.startswith("fedora") or vm.startswith("whonix"): - salt_state = "update.qubes-vm" - else: - salt_state = "fpf-apt-repo" - - try: - subprocess.check_call( - ["sudo", "qubesctl", "--skip-dom0", "--targets", vm, "state.sls", salt_state] - ) - except subprocess.CalledProcessError as e: - sdlog.error( - "An error has occurred updating {}. Please contact your administrator.".format(vm) - ) - sdlog.error(str(e)) - return UpdateStatus.UPDATES_FAILED - sdlog.info("{} update successful".format(vm)) - return UpdateStatus.UPDATES_OK - - def _write_last_updated_flags_to_disk(): """ Writes the time of last successful upgrade to dom0 and sd-app @@ -393,81 +380,6 @@ def apply_dom0_state(): return UpdateStatus.UPDATES_FAILED -def shutdown_and_start_vms(): - """ - Power cycles the vms to ensure. we should do them all in one shot to reduce complexity - and likelihood of failure. Rebooting the VMs will ensure the TemplateVM - updates are picked up by the AppVM. We must first shut all VMs down to ensure - correct order of operations, as sd-whonix cannot shutdown if sd-proxy is powered - on, for example. - - All system AppVMs (sys-net, sys-firewall and sys-usb) need to be restarted. - We use qvm-kill for sys-firewall and sys-net, because a shutdown may fail - if they are currently in use as NetVMs by any of the user's other VMs. - """ - - sdw_vms_in_order = ["sd-app", "sd-proxy", "sd-whonix", "sd-gpg", "sd-log"] - - sdlog.info("Shutting down SDW TemplateVMs for updates") - for vm in sorted(current_templates): - _safely_shutdown_vm(vm) - - sdlog.info("Shutting down SDW AppVMs for updates") - for vm in sdw_vms_in_order: - _safely_shutdown_vm(vm) - - # System VMs that can be safely shut down (order should not matter, but will - # be respected). - safe_sys_vms_in_order = ["sys-usb", "sys-whonix"] - for vm in safe_sys_vms_in_order: - sdlog.info("Safely shutting down system VM: {}".format(vm)) - _safely_shutdown_vm(vm) - - # TODO: Use of qvm-kill should be considered unsafe and may have unexpected - # side effects. We should aim for a more graceful shutdown strategy. - unsafe_sys_vms_in_order = ["sys-firewall", "sys-net"] - for vm in unsafe_sys_vms_in_order: - sdlog.info("Killing system VM: {}".format(vm)) - try: - subprocess.check_output(["qvm-kill", vm], stderr=subprocess.PIPE) - except subprocess.CalledProcessError as e: - sdlog.error("Error while killing system VM: {}".format(vm)) - sdlog.error(str(e)) - sdlog.error(str(e.stderr)) - - all_sys_vms_in_order = safe_sys_vms_in_order + unsafe_sys_vms_in_order - sdlog.info("Starting fedora-based system VMs after updates") - for vm in reversed(all_sys_vms_in_order): - _safely_start_vm(vm) - - sdlog.info("Starting SDW VMs after updates") - for vm in reversed(sdw_vms_in_order): - _safely_start_vm(vm) - - -def _safely_shutdown_vm(vm): - try: - subprocess.check_output(["qvm-shutdown", "--wait", vm], stderr=subprocess.PIPE) - except subprocess.CalledProcessError as e: - sdlog.error("Failed to shut down {}".format(vm)) - sdlog.error(str(e)) - sdlog.error(str(e.stderr)) - return UpdateStatus.UPDATES_FAILED - - -def _safely_start_vm(vm): - try: - running_vms = subprocess.check_output( - ["qvm-ls", "--running", "--raw-list"], stderr=subprocess.PIPE - ) - sdlog.info("VMs running before start of {}: {}".format(vm, running_vms)) - subprocess.check_output(["qvm-start", "--skip-if-running", vm], stderr=subprocess.PIPE) - except subprocess.CalledProcessError as e: - sdlog.error("Error while starting {}".format(vm)) - sdlog.error(str(e)) - sdlog.error(str(e.stderr)) - - def should_launch_updater(interval): status = read_dom0_update_flag_from_disk(with_timestamp=True) diff --git a/launcher/sdw_updater_gui/UpdaterApp.py b/launcher/sdw_updater_gui/UpdaterApp.py index ddd9c165..fb8292c6 100644 --- a/launcher/sdw_updater_gui/UpdaterApp.py +++ b/launcher/sdw_updater_gui/UpdaterApp.py @@ -222,16 +222,12 @@ def __init__(self): QThread.__init__(self) def run(self): + results = {} # Update dom0 first, then apply dom0 state. If full state run # is required, the dom0 state will drop a flag. self.progress_signal.emit(5) - upgrade_generator = Updater.apply_updates(vms=["dom0"], progress_start=5, progress_end=10) - - results = {} - for vm, progress, result in upgrade_generator: - results[vm] = result - self.progress_signal.emit(progress) + results["dom0"] = Updater.apply_updates_dom0() # apply dom0 state self.progress_signal.emit(10) @@ -239,22 +235,17 @@ def run(self): results["apply_dom0"] = Updater.apply_dom0_state() self.progress_signal.emit(15) - # rerun full config if dom0 checks determined it's required, - # otherwise proceed with per-VM package updates + # rerun full config if dom0 checks determined it's required if Updater.migration_is_required(): # Progress bar will freeze for ~15m during full state run self.progress_signal.emit(35) # add to results dict, if it fails it will show error message results["apply_all"] = Updater.run_full_install() - self.progress_signal.emit(75) - else: - upgrade_generator = Updater.apply_updates(progress_start=15, progress_end=75) - for vm, progress, result in upgrade_generator: - results[vm] = result - self.progress_signal.emit(progress) + self.progress_signal.emit(60) - # reboot vms - Updater.shutdown_and_start_vms() + # Update all VMs regardless of migrations + results["templates"] = Updater.apply_updates_templates() + self.progress_signal.emit(75) # write flags to disk run_results = Updater.overall_update_status(results) diff --git a/launcher/tests/test_updater.py b/launcher/tests/test_updater.py index 2ce29bb1..9975f698 100644 --- a/launcher/tests/test_updater.py +++ b/launcher/tests/test_updater.py @@ -12,8 +12,6 @@ path_to_script = os.path.join(os.path.dirname(os.path.abspath(__file__)), relpath_updater_script) updater = SourceFileLoader("Updater", path_to_script).load_module() from Updater import UpdateStatus # noqa: E402 -from Updater import current_templates # noqa: E402 -from Updater import current_vms # noqa: E402 temp_dir = TemporaryDirectory().name @@ -68,82 +66,72 @@ def test_updater_templatevms_present(): @mock.patch("Updater._write_updates_status_flag_to_disk") @mock.patch("Updater._write_last_updated_flags_to_disk") -@mock.patch("Updater._apply_updates_vm") @mock.patch("Updater._apply_updates_dom0", return_value=UpdateStatus.UPDATES_OK) @mock.patch("Updater._check_updates_dom0", return_value=UpdateStatus.UPDATES_REQUIRED) @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") def test_apply_updates_dom0_updates_available( - mocked_info, mocked_error, check_dom0, apply_dom0, apply_vm, write_updated, write_status + mocked_info, mocked_error, check_dom0, apply_dom0, write_updated, write_status ): - upgrade_generator = updater.apply_updates(["dom0"]) - results = {} - - for vm, progress, result in upgrade_generator: - results[vm] = result - assert progress is not None - - assert updater.overall_update_status(results) == UpdateStatus.UPDATES_OK + assert updater.apply_updates_dom0() == UpdateStatus.UPDATES_OK assert not mocked_error.called # Ensure we check for updates, and apply them (with no parameters) check_dom0.assert_called_once_with() apply_dom0.assert_called_once_with() - assert not apply_vm.called @mock.patch("Updater._write_updates_status_flag_to_disk") @mock.patch("Updater._write_last_updated_flags_to_disk") -@mock.patch("Updater._apply_updates_vm") @mock.patch("Updater._apply_updates_dom0") @mock.patch("Updater._check_updates_dom0", return_value=UpdateStatus.UPDATES_OK) @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") def test_apply_updates_dom0_no_updates( - mocked_info, mocked_error, check_dom0, apply_dom0, apply_vm, write_updated, write_status + mocked_info, mocked_error, check_dom0, apply_dom0, write_updated, write_status ): - upgrade_generator = updater.apply_updates(["dom0"]) - results = {} - - for vm, progress, result in upgrade_generator: - results[vm] = result - assert progress is not None - - assert updater.overall_update_status(results) == UpdateStatus.UPDATES_OK + assert updater.apply_updates_dom0() == UpdateStatus.UPDATES_OK assert not mocked_error.called # We check for updates, but do not attempt to apply them check_dom0.assert_called_once_with() assert not apply_dom0.called - assert not apply_vm.called @mock.patch("Updater._write_updates_status_flag_to_disk") @mock.patch("Updater._write_last_updated_flags_to_disk") -@mock.patch( - "Updater._apply_updates_vm", - side_effect=[UpdateStatus.UPDATES_OK, UpdateStatus.UPDATES_REQUIRED], -) -@mock.patch("Updater._apply_updates_dom0") +@mock.patch("subprocess.check_output", return_value="".encode()) @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") -def test_apply_updates_required( - mocked_info, mocked_error, apply_dom0, apply_vm, write_updated, write_status +def test_apply_templates_success( + mocked_info, mocked_error, mocked_check, write_updated, write_status ): - upgrade_generator = updater.apply_updates(["fedora", "sd-app"]) - results = {} - - for vm, progress, result in upgrade_generator: - results[vm] = result - assert progress is not None - - assert results == {"fedora": UpdateStatus.UPDATES_OK, "sd-app": UpdateStatus.UPDATES_REQUIRED} - calls = [call("fedora"), call("sd-app")] - apply_vm.assert_has_calls(calls) + templates = ["fedora", "debian"] + result = updater.apply_updates_templates(templates=templates) + + mocked_check.assert_called_once_with( + [ + "qubes-vm-update", + "--restart", + "--no-progress", + "--show-output", + "--targets", + ",".join(templates), + ], + stderr=subprocess.STDOUT, + ) + assert result == UpdateStatus.UPDATES_OK + assert not mocked_error.called - assert results == {"fedora": UpdateStatus.UPDATES_OK, "sd-app": UpdateStatus.UPDATES_REQUIRED} - assert updater.overall_update_status(results) == UpdateStatus.UPDATES_REQUIRED - assert not mocked_error.called - assert not apply_dom0.called +@mock.patch( + "subprocess.check_output", side_effect=subprocess.CalledProcessError(cmd="", returncode=1) +) +@mock.patch("Updater.sdlog.error") +@mock.patch("Updater.sdlog.info") +def test_apply_templates_failure(mocked_info, mocked_error, mocked_check): + templates = ["fedora", "debian"] + result = updater.apply_updates_templates(templates=templates) + assert result == UpdateStatus.UPDATES_FAILED + assert mocked_error.called @pytest.mark.parametrize("status", UpdateStatus) @@ -265,28 +253,19 @@ def test_write_last_updated_flags_dom0_folder_creation_fail( @mock.patch("subprocess.check_call") -@mock.patch("Updater._write_updates_status_flag_to_disk") -@mock.patch("Updater._write_last_updated_flags_to_disk") -@mock.patch("Updater.shutdown_and_start_vms") @mock.patch("Updater._check_updates_dom0", return_value=UpdateStatus.UPDATES_REQUIRED) -@mock.patch("Updater._apply_updates_vm") @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") def test_apply_updates_dom0_updates_applied( mocked_info, mocked_error, - apply_vm, check_dom0, - shutdown, - write_updated, - write_status, mocked_call, ): result = updater._apply_updates_dom0() assert result == UpdateStatus.REBOOT_REQUIRED mocked_call.assert_called_once_with(["sudo", "qubes-dom0-update", "-y"]) assert not mocked_error.called - assert not apply_vm.called @mock.patch("subprocess.check_call", side_effect=subprocess.CalledProcessError(1, "check_call")) @@ -304,41 +283,6 @@ def test_apply_updates_dom0_failure(mocked_info, mocked_error, mocked_call): mocked_error.assert_has_calls(error_log) -@pytest.mark.parametrize("vm", current_templates) -@mock.patch("subprocess.check_call", side_effect="0") -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_apply_updates_vms(mocked_info, mocked_error, mocked_call, vm): - if vm != "dom0": - result = updater._apply_updates_vm(vm) - assert result == UpdateStatus.UPDATES_OK - - if vm.startswith("fedora") or vm.startswith("whonix"): - expected_salt_state = "update.qubes-vm" - else: - expected_salt_state = "fpf-apt-repo" - - mocked_call.assert_called_once_with( - ["sudo", "qubesctl", "--skip-dom0", "--targets", vm, "state.sls", expected_salt_state] - ) - assert not mocked_error.called - - -@pytest.mark.parametrize("vm", current_templates) -@mock.patch("subprocess.check_call", side_effect=subprocess.CalledProcessError(1, "check_call")) -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_apply_updates_vms_fails(mocked_info, mocked_error, mocked_call, vm): - error_calls = [ - call("An error has occurred updating {}. Please contact your administrator.".format(vm)), - call("Command 'check_call' returned non-zero exit status 1."), - ] - result = updater._apply_updates_vm(vm) - assert result == UpdateStatus.UPDATES_FAILED - - mocked_error.assert_has_calls(error_calls) - - @mock.patch("subprocess.check_call", side_effect=subprocess.CalledProcessError(1, "check_call")) @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") @@ -417,148 +361,6 @@ def test_overall_update_status_reboot_not_done_previously( assert not mocked_error.called -@pytest.mark.parametrize("vm", current_vms.keys()) -@mock.patch("subprocess.check_output") -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_safely_shutdown(mocked_info, mocked_error, mocked_output, vm): - call_list = [call(["qvm-shutdown", "--wait", "{}".format(vm)], stderr=-1)] - - updater._safely_shutdown_vm(vm) - mocked_output.assert_has_calls(call_list) - assert not mocked_error.called - - -@pytest.mark.parametrize("vm", current_vms.keys()) -@mock.patch("subprocess.check_output", side_effect=["0", "0", "0"]) -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_safely_start(mocked_info, mocked_error, mocked_output, vm): - call_list = [ - call(["qvm-ls", "--running", "--raw-list"], stderr=-1), - call(["qvm-start", "--skip-if-running", vm], stderr=-1), - ] - - updater._safely_start_vm(vm) - mocked_output.assert_has_calls(call_list) - assert not mocked_error.called - - -@pytest.mark.parametrize("vm", current_vms.keys()) -@mock.patch("subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "check_output")) -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_safely_start_fails(mocked_info, mocked_error, mocked_output, vm): - call_list = [ - call("Error while starting {}".format(vm)), - call("Command 'check_output' returned non-zero exit status 1."), - ] - - updater._safely_start_vm(vm) - mocked_error.assert_has_calls(call_list) - - -@pytest.mark.parametrize("vm", current_vms.keys()) -@mock.patch("subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "check_output")) -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_safely_shutdown_fails(mocked_info, mocked_error, mocked_call, vm): - call_list = [ - call("Failed to shut down {}".format(vm)), - call("Command 'check_output' returned non-zero exit status 1."), - ] - - updater._safely_shutdown_vm(vm) - mocked_error.assert_has_calls(call_list) - - -@mock.patch("subprocess.check_output") -@mock.patch("Updater._safely_start_vm") -@mock.patch("Updater._safely_shutdown_vm") -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_shutdown_and_start_vms( - mocked_info, mocked_error, mocked_shutdown, mocked_start, mocked_output -): - sys_vm_kill_calls = [ - call(["qvm-kill", "sys-firewall"], stderr=-1), - call(["qvm-kill", "sys-net"], stderr=-1), - ] - sys_vm_shutdown_calls = [call("sys-usb"), call("sys-whonix")] - sys_vm_start_calls = [ - call("sys-net"), - call("sys-firewall"), - call("sys-whonix"), - call("sys-usb"), - ] - template_vm_calls = [ - call("fedora-38"), - call("sd-large-{}-template".format(DEBIAN_VERSION)), - call("sd-small-{}-template".format(DEBIAN_VERSION)), - call("whonix-gateway-17"), - ] - app_vm_calls = [ - call("sd-app"), - call("sd-proxy"), - call("sd-whonix"), - call("sd-gpg"), - call("sd-log"), - ] - updater.shutdown_and_start_vms() - mocked_output.assert_has_calls(sys_vm_kill_calls) - mocked_shutdown.assert_has_calls(template_vm_calls + app_vm_calls + sys_vm_shutdown_calls) - app_vm_calls_reversed = list(reversed(app_vm_calls)) - mocked_start.assert_has_calls(sys_vm_start_calls + app_vm_calls_reversed) - assert not mocked_error.called - - -@mock.patch("subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "check_output")) -@mock.patch("Updater._safely_start_vm") -@mock.patch("Updater._safely_shutdown_vm") -@mock.patch("Updater.sdlog.error") -@mock.patch("Updater.sdlog.info") -def test_shutdown_and_start_vms_sysvm_fail( - mocked_info, mocked_error, mocked_shutdown, mocked_start, mocked_output -): - sys_vm_kill_calls = [ - call(["qvm-kill", "sys-firewall"], stderr=-1), - call(["qvm-kill", "sys-net"], stderr=-1), - ] - sys_vm_start_calls = [ - call("sys-net"), - call("sys-firewall"), - call("sys-whonix"), - call("sys-usb"), - ] - app_vm_calls = [ - call("sd-app"), - call("sd-proxy"), - call("sd-whonix"), - call("sd-gpg"), - call("sd-log"), - ] - template_vm_calls = [ - call("fedora-38"), - call("sd-large-{}-template".format(DEBIAN_VERSION)), - call("sd-small-{}-template".format(DEBIAN_VERSION)), - call("whonix-gateway-17"), - ] - error_calls = [ - call("Error while killing system VM: sys-firewall"), - call("Command 'check_output' returned non-zero exit status 1."), - call("None"), - call("Error while killing system VM: sys-net"), - call("Command 'check_output' returned non-zero exit status 1."), - call("None"), - ] - updater.shutdown_and_start_vms() - mocked_output.assert_has_calls(sys_vm_kill_calls) - mocked_shutdown.assert_has_calls(template_vm_calls + app_vm_calls) - app_vm_calls_reversed = list(reversed(app_vm_calls)) - mocked_start.assert_has_calls(sys_vm_start_calls + app_vm_calls_reversed) - mocked_error.assert_has_calls(error_calls) - - @pytest.mark.parametrize("status", UpdateStatus) @mock.patch("subprocess.check_call") @mock.patch("os.path.expanduser", return_value=temp_dir) diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index 5c562e8b..56f9b08a 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -11,11 +11,11 @@ attrs==22.2.0 \ bandit==1.7.7 \ --hash=sha256:17e60786a7ea3c9ec84569fd5aee09936d116cb0cb43151023258340dbffb7ed \ --hash=sha256:527906bec6088cb499aae31bc962864b4e77569e9d529ee51df3a93b4b8ab28a - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in black==21.12b0 \ --hash=sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3 \ --hash=sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in build==0.10.0 \ --hash=sha256:af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171 \ --hash=sha256:d5b71264afdb5951d6704482aac78de887c80691c52b88a9ad195983ca2c9269 @@ -28,7 +28,7 @@ click==8.0.3 \ # pip-tools diffoscope==256 \ --hash=sha256:9937e3d70358d5cc00aa5fa8a3db9dd6be532a0d3cd2d064add2cda736a97c2a - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in distro==1.8.0 \ --hash=sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8 \ --hash=sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff @@ -40,7 +40,7 @@ exceptiongroup==1.1.0 \ flake8==4.0.1 \ --hash=sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d \ --hash=sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in gitdb==4.0.10 \ --hash=sha256:6eb990b69df4e15bad899ea868dc46572c3f75339735663b81de79b06f17eb9a \ --hash=sha256:c286cf298426064079ed96a9e4a9d39e7f3e9bf15ba60701e95f5492f28415c7 @@ -48,7 +48,7 @@ gitdb==4.0.10 \ gitpython==3.1.32 \ --hash=sha256:8d9b8cb1e80b9735e8717c9362079d3ce4c6e5ddeebedd0361b228c3a67a62f6 \ --hash=sha256:e3d59b1c2c6ebb9dfa7a184daf3b6dd4914237e7488a1730a6d8f6f5d0b4187f - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 @@ -56,7 +56,7 @@ iniconfig==2.0.0 \ isort==5.12.0 \ --hash=sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504 \ --hash=sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6 - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in libarchive-c==4.0 \ --hash=sha256:a5b41ade94ba58b198d778e68000f6b7de41da768de7140c984f71d7fa8416e5 \ --hash=sha256:b7306dceaeeac199dd53a471ea9d39ed4be8e81962df99d628db7a12efbb9b52 @@ -100,7 +100,7 @@ mypy==1.0.0 \ --hash=sha256:e0626db16705ab9f7fa6c249c017c887baf20738ce7f9129da162bb3075fc1af \ --hash=sha256:f34495079c8d9da05b183f9f7daec2878280c2ad7cc81da686ef0b484cea2ecf \ --hash=sha256:fe523fcbd52c05040c7bee370d66fee8373c5972171e4fbc323153433198592d - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in mypy-extensions==0.4.3 \ --hash=sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d \ --hash=sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8 @@ -124,7 +124,7 @@ pbr==5.11.1 \ pip-tools==6.12.2 \ --hash=sha256:6a51f4fd67140d5e83703ebfa9610fb61398727151f56a1be02a972d062e4679 \ --hash=sha256:8b903696df4598b10d469026ef9995c5f9a874b416e88e7a214884ebe4a70245 - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in platformdirs==2.4.1 \ --hash=sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca \ --hash=sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda @@ -152,56 +152,67 @@ pyproject-hooks==1.0.0 \ pytest==7.2.1 \ --hash=sha256:c7c6ca206e93355074ae32f7403e8ea12163b1163c976fee7d4d84027c162be5 \ --hash=sha256:d45e0952f3727241918b8fd0f376f5ff6b301cc0777c6f9a556935c92d8a7d42 - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in python-magic==0.4.27 \ --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \ --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3 # via diffoscope -pyyaml==6.0 \ - --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \ - --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \ - --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \ - --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \ - --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \ - --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \ - --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \ - --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \ - --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \ - --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \ - --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \ - --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \ - --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782 \ - --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ - --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ - --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ - --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ - --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ - --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1 \ - --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ - --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ - --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ - --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ - --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ - --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ - --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d \ - --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ - --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ - --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7 \ - --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ - --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ - --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ - --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358 \ - --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ - --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ - --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ - --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ - --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \ - --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ - --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 +pyyaml==6.0.1 \ + --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ + --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ + --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ + --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ + --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ + --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ + --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ + --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ + --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ + --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ + --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ + --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ + --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ + --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ + --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ + --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ + --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ + --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ + --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ + --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ + --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ + --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ + --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ + --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ + --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ + --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ + --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ + --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ + --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ + --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \ + --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ + --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ + --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ + --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ + --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ + --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ + --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ + --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ + --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ + --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ + --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ + --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ + --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ + --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ + --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ + --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ + --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ + --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ + --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ + --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ + --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f # via bandit reprotest==0.7.23 \ --hash=sha256:c3508ee796219906a11eeda6f59897e631cc796fff6ea85d61d77d28c88f5c7f - # via -r dev-requirements.in + # via -r requirements/dev-requirements.in rich==13.7.0 \ --hash=sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa \ --hash=sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235 @@ -243,7 +254,7 @@ pip==23.0.1 \ --hash=sha256:236bcb61156d76c4b8a05821b988c7b8c35bf0da28a4b614e8d6ab5212c25c6f \ --hash=sha256:cd015ea1bfb0fcef59d8a286c1f8bebcb983f6317719d415dc5351efb7cd7024 # via - # -r dev-requirements.in + # -r requirements/dev-requirements.in # pip-tools setuptools==67.4.0 \ --hash=sha256:e5fd0a713141a4a105412233c63dc4e17ba0090c8e8334594ac790ec97792330 \ diff --git a/rpm-build/SPECS/securedrop-workstation-dom0-config.spec b/rpm-build/SPECS/securedrop-workstation-dom0-config.spec index 7f42426e..2efb2369 100644 --- a/rpm-build/SPECS/securedrop-workstation-dom0-config.spec +++ b/rpm-build/SPECS/securedrop-workstation-dom0-config.spec @@ -15,11 +15,9 @@ Summary: SecureDrop Workstation # For reproducibility we'll keep everything %define _changelog_trimtime 0 %define _changelog_trimage 0 -# * _buildhost varies based on environment, we build via Docker but ensure -# this is the same regardless +# * _buildhost varies based on environment, we build with containers but +# ensure this is the same regardless %global _buildhost %{name} -# * compiling Python bytecode is not reproducible at the time of writing -%undefine py_auto_byte_compile License: AGPLv3 URL: https://github.com/freedomofpress/securedrop-workstation @@ -53,6 +51,9 @@ configuration over time. %install %{python3} -m pip install --no-compile --no-index --no-build-isolation --root %{buildroot} . +# direct_url.json is is not reproducible and not strictly needed +rm %{buildroot}/%{python3_sitelib}/*%{version}.dist-info/direct_url.json +sed -i "/\.dist-info\/direct_url\.json,/d" %{buildroot}/%{python3_sitelib}/*%{version}.dist-info/RECORD install -m 755 -d %{buildroot}/opt/securedrop/launcher/sdw_updater_gui install -m 755 -d %{buildroot}/opt/securedrop/launcher/sdw_notify install -m 755 -d %{buildroot}/opt/securedrop/launcher/sdw_util diff --git a/scripts/build-rpm.sh b/scripts/build-rpm.sh index d52e2687..2d67b2c0 100755 --- a/scripts/build-rpm.sh +++ b/scripts/build-rpm.sh @@ -7,12 +7,8 @@ set -o pipefail source "$(dirname "$0")/common.sh" # Prepare tarball, rpmbuild will use it -mkdir -p dist/ -git clean -fdX rpm-build/ dist/ -/usr/bin/python3 setup.py sdist - -# Place tarball where rpmbuild will find it -cp dist/*.tar.gz rpm-build/SOURCES/ +git clean -fdX rpm-build/ +/usr/bin/python3 setup.py sdist -d rpm-build/SOURCES/ rpmbuild \ --quiet \ diff --git a/scripts/prep-dev b/scripts/prep-dev index e3dde65b..22585f46 100755 --- a/scripts/prep-dev +++ b/scripts/prep-dev @@ -12,7 +12,7 @@ dom0_dev_dir="$HOME/securedrop-workstation" function find_latest_rpm() { # Look up which version of dom0 we're using. - # Qubes 4.0 is fedora-25, Qubes 4.1 will be fedora-32. + # Qubes 4.1 is fedora-32, Qubes 4.2 fedora-37. fedora_version="$(rpm --eval '%{fedora}')" find "${dom0_dev_dir}/rpm-build/RPMS/" -type f -iname "*fc${fedora_version}.noarch.rpm" -print0 | xargs -r -0 ls -t | head -n 1 } @@ -35,5 +35,7 @@ sudo dnf install -y "$latest_rpm" echo "Copying config secrets into place..." for f in config.json sd-journalist.sec ; do sudo cp -v "$f" /usr/share/securedrop-workstation-dom0-config/ + chmod ugo+r /usr/share/securedrop-workstation-dom0-config/$f sudo cp -v "$f" /srv/salt/sd/ + chmod ugo+r /srv/salt/sd/$f done diff --git a/tests/base.py b/tests/base.py index c0810c6c..52126835 100644 --- a/tests/base.py +++ b/tests/base.py @@ -7,8 +7,8 @@ # Reusable constant for DRY import across tests WANTED_VMS = ["sd-gpg", "sd-log", "sd-proxy", "sd-app", "sd-viewer", "sd-whonix", "sd-devices"] -CURRENT_FEDORA_VERSION = "38" -CURRENT_FEDORA_TEMPLATE = "fedora-" + CURRENT_FEDORA_VERSION +CURRENT_FEDORA_VERSION = "39" +CURRENT_FEDORA_TEMPLATE = "fedora-" + CURRENT_FEDORA_VERSION + "-xfce" CURRENT_WHONIX_VERSION = "17" diff --git a/tests/test_dom0_rpm_repo.py b/tests/test_dom0_rpm_repo.py index 5e462c44..b99500ca 100644 --- a/tests/test_dom0_rpm_repo.py +++ b/tests/test_dom0_rpm_repo.py @@ -3,7 +3,7 @@ DEBIAN_VERSION = "bullseye" -FEDORA_VERSION = "f32" +FEDORA_VERSION = "f37" class SD_Dom0_Rpm_Repo_Tests(unittest.TestCase):