diff --git a/MANIFEST.in b/MANIFEST.in index 199f0ba2..4bfdbeb6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -8,6 +8,7 @@ include dom0/sdw-admin include dom0/securedrop-login include dom0/securedrop-launcher.desktop include dom0/securedrop-handle-upgrade +include dom0/securedrop-check-migration include dom0/update-xfce-settings include config.json.example include README.md diff --git a/Makefile b/Makefile index ad05f3a0..122a1673 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ sd-workstation-template: prep-dev ## Provisions base template for SDW AppVMs sd-proxy: prep-dev ## Provisions SD Proxy VM sudo qubesctl --show-output state.sls sd-proxy - sudo qubesctl --show-output --skip-dom0 --targets sd-proxy-buster-template,sd-proxy state.highstate + sudo qubesctl --show-output --skip-dom0 --targets sd-small-buster-template,sd-proxy state.highstate sd-gpg: prep-dev ## Provisions SD GPG keystore VM sudo qubesctl --show-output state.sls sd-gpg @@ -56,7 +56,7 @@ sd-gpg: prep-dev ## Provisions SD GPG keystore VM sd-app: prep-dev ## Provisions SD APP VM sudo qubesctl --show-output state.sls sd-app - sudo qubesctl --show-output --skip-dom0 --targets sd-app-buster-template,sd-app state.highstate + sudo qubesctl --show-output --skip-dom0 --targets sd-small-buster-template,sd-app state.highstate sd-whonix: prep-dev ## Provisions SD Whonix VM sudo qubesctl --show-output state.sls sd-whonix @@ -72,7 +72,7 @@ sd-devices: prep-dev ## Provisions SD Export VM sd-log: prep-dev ## Provisions SD logging VM sudo qubesctl --show-output state.sls sd-log - sudo qubesctl --show-output --skip-dom0 --targets sd-log-buster-template,sd-log state.highstate + sudo qubesctl --show-output --skip-dom0 --targets sd-small-buster-template,sd-log state.highstate prep-dev: assert-dom0 ## Configures Salt layout for SD workstation VMs @./scripts/prep-dev diff --git a/VERSION b/VERSION index 1d0ba9ea..04c1660d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.5.0-rc1 diff --git a/dom0/sd-app.sls b/dom0/sd-app.sls index e3c2da04..ffe4023d 100644 --- a/dom0/sd-app.sls +++ b/dom0/sd-app.sls @@ -12,28 +12,13 @@ include: - sd-workstation-template - sd-upgrade-templates -sd-app-template: - qvm.vm: - - name: sd-app-buster-template - - clone: - - source: securedrop-workstation-buster - - label: yellow - - tags: - - add: - - sd-workstation - - sd-buster - - sd-workstation-updates - - require: - - sls: sd-workstation-template - - sls: sd-upgrade-templates - sd-app: qvm.vm: - name: sd-app - present: - label: yellow - prefs: - - template: sd-app-buster-template + - template: sd-small-buster-template - netvm: "" - tags: - add: @@ -43,7 +28,7 @@ sd-app: - enable: - service.paxctld - require: - - qvm: sd-app-buster-template + - qvm: sd-small-buster-template {% import_json "sd/config.json" as d %} @@ -60,9 +45,9 @@ sd-app-private-volume-size: sd-app-template-sync-appmenus: cmd.run: - name: > - qvm-start --skip-if-running sd-app-buster-template && - qvm-sync-appmenus sd-app-buster-template + qvm-start --skip-if-running sd-small-buster-template && + qvm-sync-appmenus sd-small-buster-template - require: - - qvm: sd-app-buster-template + - qvm: sd-small-buster-template - onchanges: - - qvm: sd-app-buster-template + - qvm: sd-small-buster-template diff --git a/dom0/sd-devices.sls b/dom0/sd-devices.sls index ade91803..24183252 100644 --- a/dom0/sd-devices.sls +++ b/dom0/sd-devices.sls @@ -9,28 +9,14 @@ include: - sd-workstation-template - sd-upgrade-templates -sd-devices-template: - qvm.vm: - - name: sd-devices-buster-template - - clone: - - source: securedrop-workstation-buster - - label: red - - tags: - - add: - - sd-workstation - - sd-workstation-updates - - require: - - sls: sd-workstation-template - - sls: sd-upgrade-templates - sd-devices-dvm: qvm.vm: - name: sd-devices-dvm - present: - - template: sd-devices-buster-template + - template: sd-large-buster-template - label: red - prefs: - - template: sd-devices-buster-template + - template: sd-large-buster-template - netvm: "" - template_for_dispvms: True - tags: @@ -41,19 +27,19 @@ sd-devices-dvm: - enable: - service.paxctld - require: - - qvm: sd-devices-buster-template + - qvm: sd-large-buster-template # Ensure the Qubes menu is populated with relevant app entries, # so that Nautilus/Files can be started via GUI interactions. sd-devices-template-sync-appmenus: cmd.run: - name: > - qvm-start --skip-if-running sd-devices-buster-template && - qvm-sync-appmenus sd-devices-buster-template + qvm-start --skip-if-running sd-large-buster-template && + qvm-sync-appmenus sd-large-buster-template - require: - - qvm: sd-devices-buster-template + - qvm: sd-large-buster-template - onchanges: - - qvm: sd-devices-buster-template + - qvm: sd-large-buster-template sd-devices-create-named-dispvm: qvm.vm: diff --git a/dom0/sd-gpg.sls b/dom0/sd-gpg.sls index ae5bd410..41706c65 100644 --- a/dom0/sd-gpg.sls +++ b/dom0/sd-gpg.sls @@ -17,10 +17,10 @@ sd-gpg: qvm.vm: - name: sd-gpg - present: - - template: securedrop-workstation-buster + - template: sd-small-buster-template - label: purple - prefs: - - template: securedrop-workstation-buster + - template: sd-small-buster-template - netvm: "" - autostart: true - tags: diff --git a/dom0/sd-log.sls b/dom0/sd-log.sls index bf93f04c..82bf5776 100644 --- a/dom0/sd-log.sls +++ b/dom0/sd-log.sls @@ -10,25 +10,14 @@ include: - sd-workstation-template - sd-upgrade-templates -sd-log-template: - qvm.vm: - - name: sd-log-buster-template - - clone: - - source: securedrop-workstation-buster - - label: red - - tags: - - add: - - sd-workstation - - require: - - sls: sd-workstation-template - sd-log: qvm.vm: - name: sd-log - present: - - template: sd-log-buster-template + - template: sd-small-buster-template - label: red - prefs: + - template: sd-small-buster-template - netvm: "" - autostart: true - tags: @@ -40,7 +29,7 @@ sd-log: - service.redis - service.securedrop-log - require: - - qvm: sd-log-buster-template + - qvm: sd-small-buster-template # Allow any SecureDrop VM to log to the centralized log VM sd-log-dom0-securedrop.Log: diff --git a/dom0/sd-logging-setup.sls b/dom0/sd-logging-setup.sls index 1be9fe30..0e5fa919 100644 --- a/dom0/sd-logging-setup.sls +++ b/dom0/sd-logging-setup.sls @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # vim: set syntax=yaml ts=2 sw=2 sts=2 et : -{% if "template" in grains['id'] or grains['id'] in ["securedrop-workstation-buster", "whonix-gw-15"] %} +{% if "template" in grains['id'] or grains['id'] in ["securedrop-workstation-buster", "sd-small-buster-template", "sd-large-buster-template", "whonix-gw-15"] %} include: - fpf-apt-test-repo @@ -14,15 +14,12 @@ install-securedrop-log-package: - sls: fpf-apt-test-repo {% endif %} -{% if grains['id'] == "sd-log-buster-template" %} +{% if grains['id'] in ["sd-small-buster-template", "sd-large-buster-template"] %} install-redis-for-sd-log-template: pkg.installed: - pkgs: - redis-server - redis -remove-sd-rsyslog-config-for-logserver: - file.absent: - - name: /etc/rsyslog.d/sdlog.conf {% elif grains['id'] == "sd-log" %} # Only for the "sd-log" AppVM, configure /rw/config to disable @@ -43,6 +40,9 @@ sd-log-remove-rsyslog-qubes-plugin: - name: /rw/config/rc.local - require: - file: sd-log-remove-rsyslog-qubes-plugin +remove-sd-rsyslog-config-for-logserver: + file.absent: + - name: /etc/rsyslog.d/sdlog.conf {% elif grains['id'] == "sd-gpg" %} # For sd-gpg, we disable logging altogether, since access diff --git a/dom0/sd-mime-handling.sls b/dom0/sd-mime-handling.sls index e0ca2808..ac827340 100644 --- a/dom0/sd-mime-handling.sls +++ b/dom0/sd-mime-handling.sls @@ -12,12 +12,34 @@ # respective AppVMs. ## +sd-private-volume-mimeapps-config-dir: + file.directory: + - name: /home/user/.local/share/applications + - user: user + - group: user + - makedirs: True + - mode: "0755" + {% if grains['id'] in ["sd-viewer", "sd-app", "sd-devices-dvm"] %} sd-private-volume-mimeapps-handling: file.symlink: - name: /home/user/.local/share/applications/mimeapps.list - target: /opt/sdw/mimeapps.list.{{ grains['id'] }} - - makedirs: True + - user: user + - group: user + - require: + - file: sd-private-volume-mimeapps-config-dir + +{% else %} + +sd-private-volume-mimeapps-handling: + file.symlink: + - name: /home/user/.local/share/applications/mimeapps.list + - target: /opt/sdw/mimeapps.list.default + - user: user + - group: user + - require: + - file: sd-private-volume-mimeapps-config-dir {% endif %} diff --git a/dom0/sd-proxy-files.sls b/dom0/sd-proxy-files.sls new file mode 100644 index 00000000..cca71a79 --- /dev/null +++ b/dom0/sd-proxy-files.sls @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# vim: set syntax=yaml ts=2 sw=2 sts=2 et : +{% import_json "sd/config.json" as d %} + +install-securedrop-proxy-yaml-config: + file.managed: + - name: /home/user/.securedrop_proxy/sd-proxy.yaml + - source: salt://sd/sd-proxy/sd-proxy.yaml + - makedirs: True + - template: jinja + - user: user + - group: user + - context: + hostname: {{ d.hidserv.hostname }} + - mode: 0644 diff --git a/dom0/sd-proxy-template-files.sls b/dom0/sd-proxy-template-files.sls index 286ee7c6..bf4c03d3 100644 --- a/dom0/sd-proxy-template-files.sls +++ b/dom0/sd-proxy-template-files.sls @@ -4,42 +4,6 @@ include: - fpf-apt-test-repo - sd-logging-setup -sd-proxy-do-not-open-here-script: - file.managed: - - name: /usr/bin/do-not-open-here - - source: salt://sd/sd-proxy/do-not-open-here - - user: root - - group: root - - mode: 755 - -sd-proxy-do-not-open-here-desktop-file: - file.managed: - - name: /usr/share/applications/do-not-open.desktop - - source: salt://sd/sd-proxy/do-not-open.desktop - - user: root - - group: root - - mode: 644 - - makedirs: True - -sd-proxy-configure-mimetypes: - file.managed: - - name: /usr/share/applications/mimeapps.list - - source: salt://sd/sd-proxy/mimeapps.list - - user: user - - group: user - - mode: 644 - - makedirs: True - cmd.run: - - name: sudo update-desktop-database /usr/share/applications - - require: - - file: sd-proxy-configure-mimetypes - - file: sd-proxy-do-not-open-here-desktop-file - - file: sd-proxy-do-not-open-here-script - - onchanges: - - file: sd-proxy-do-not-open-here-script - - file: sd-proxy-do-not-open-here-desktop-file - - file: sd-proxy-configure-mimetypes - # Depends on FPF-controlled apt repo, already present # in underlying "securedrop-workstation" base template. install-securedrop-proxy-package: @@ -49,14 +13,8 @@ install-securedrop-proxy-package: - require: - sls: fpf-apt-test-repo - -{% import_json "sd/config.json" as d %} - -install-securedrop-proxy-yaml-config: - file.managed: - - name: /etc/sd-proxy.yaml - - source: salt://sd/sd-proxy/sd-proxy.yaml - - template: jinja - - context: - hostname: {{ d.hidserv.hostname }} - - mode: 0644 +# Remove the legacy config file location +remove-legacy-sd-proxy-config: + file.absent: + - names: + - /etc/sd-proxy.yaml diff --git a/dom0/sd-proxy.sls b/dom0/sd-proxy.sls index ca2ee5a1..2309e281 100644 --- a/dom0/sd-proxy.sls +++ b/dom0/sd-proxy.sls @@ -13,25 +13,13 @@ include: - sd-whonix - sd-upgrade-templates -sd-proxy-template: - qvm.vm: - - name: sd-proxy-buster-template - - clone: - - source: securedrop-workstation-buster - - label: blue - - tags: - - add: - - sd-workstation - - sd-buster - - sd-workstation-updates - sd-proxy: qvm.vm: - name: sd-proxy - present: - label: blue - prefs: - - template: sd-proxy-buster-template + - template: sd-small-buster-template - netvm: sd-whonix - autostart: true - tags: @@ -40,7 +28,7 @@ sd-proxy: - sd-buster - require: - qvm: sd-whonix - - qvm: sd-proxy-template + - qvm: sd-small-buster-template # Permit the SecureDrop Proxy to manage Client connections sd-proxy-dom0-securedrop.Proxy: diff --git a/dom0/sd-remove-unused-templates.sls b/dom0/sd-remove-unused-templates.sls index 226a0fa8..ca519582 100644 --- a/dom0/sd-remove-unused-templates.sls +++ b/dom0/sd-remove-unused-templates.sls @@ -1,7 +1,27 @@ # -*- coding: utf-8 -*- # vim: set syntax=yaml ts=2 sw=2 sts=2 et : + +# Make sure the "prepare" step has run first, otherwise there's +# a race between migration and removal. +include: + - sd-upgrade-templates + - sd-log + - sd-devices + - sd-gpg + - sd-proxy + - sd-viewer + - sd-app + run-remove-upgrade-scripts: cmd.script: - name: salt://securedrop-handle-upgrade - args: remove + - require: + - sls: sd-upgrade-templates + - sls: sd-log + - sls: sd-devices + - sls: sd-gpg + - sls: sd-proxy + - sls: sd-viewer + - sls: sd-app diff --git a/dom0/sd-upgrade-templates.sls b/dom0/sd-upgrade-templates.sls index 7b3f276b..e399de94 100644 --- a/dom0/sd-upgrade-templates.sls +++ b/dom0/sd-upgrade-templates.sls @@ -1,7 +1,16 @@ # -*- coding: utf-8 -*- # vim: set syntax=yaml ts=2 sw=2 sts=2 et : +# If the entire config must be reapplied, the "check-migration" script +# will drop a flag on disk in dom0 that the GUI updater will find, +# conditionally enabling a longer run. +determine-whether-migration-required: + cmd.script: + - name: salt://securedrop-check-migration + run-prep-upgrade-scripts: cmd.script: - name: salt://securedrop-handle-upgrade - args: prepare + - require: + - cmd: determine-whether-migration-required diff --git a/dom0/sd-viewer-files.sls b/dom0/sd-viewer-files.sls index 3503b7c6..5b5a9bf8 100644 --- a/dom0/sd-viewer-files.sls +++ b/dom0/sd-viewer-files.sls @@ -14,11 +14,10 @@ include: - fpf-apt-test-repo - sd-logging-setup -sd-viewer-install-mimetype-handler-package: +sd-viewer-install-metapackage: pkg.installed: - pkgs: - - securedrop-workstation-svs-disp - - evince + - securedrop-workstation-viewer - require: - sls: fpf-apt-test-repo diff --git a/dom0/sd-viewer.sls b/dom0/sd-viewer.sls index 608dcfde..dc078967 100644 --- a/dom0/sd-viewer.sls +++ b/dom0/sd-viewer.sls @@ -15,28 +15,14 @@ include: - sd-workstation-template - sd-upgrade-templates -sd-viewer-template: - qvm.vm: - - name: sd-viewer-buster-template - - clone: - - source: securedrop-workstation-buster - - label: green - - tags: - - add: - - sd-workstation - - sd-workstation-updates - - require: - - sls: sd-workstation-template - - sls: sd-upgrade-templates - sd-viewer: qvm.vm: - name: sd-viewer - present: - - template: sd-viewer-buster-template + - template: sd-large-buster-template - label: green - prefs: - - template: sd-viewer-buster-template + - template: sd-large-buster-template - netvm: "" - template_for_dispvms: True - tags: @@ -48,7 +34,7 @@ sd-viewer: - enable: - service.paxctld - require: - - qvm: sd-viewer-buster-template + - qvm: sd-large-buster-template sd-viewer-default-dispvm: cmd.run: diff --git a/dom0/sd-workstation-template.sls b/dom0/sd-workstation-template.sls index 91e35a49..846b51ba 100644 --- a/dom0/sd-workstation-template.sls +++ b/dom0/sd-workstation-template.sls @@ -21,3 +21,36 @@ sd-workstation-template: - service.paxctld - require: - pkg: dom0-install-securedrop-workstation-template + +# Installs consolidated templateVMs: +# - sd-small-buster-template, to be used for +# sd-app, sd-gpg, sd-log, and sd-proxy +# - sd-large-buster-template, to be used for +# sd-export and sd-viewer +sd-small-buster-template: + qvm.vm: + - name: sd-small-buster-template + - clone: + - source: securedrop-workstation-buster + - label: red + - tags: + - add: + - sd-workstation + - sd-buster + - sd-workstation-updates + - require: + - qvm: sd-workstation-template + +sd-large-buster-template: + qvm.vm: + - name: sd-large-buster-template + - clone: + - source: securedrop-workstation-buster + - label: red + - tags: + - add: + - sd-workstation + - sd-buster + - sd-workstation-updates + - require: + - qvm: sd-workstation-template diff --git a/dom0/sd-workstation.top b/dom0/sd-workstation.top index b8c6d2ac..9b25a818 100644 --- a/dom0/sd-workstation.top +++ b/dom0/sd-workstation.top @@ -20,22 +20,22 @@ base: - sd-whonix - sd-remove-unused-templates - sd-log-buster-template: + sd-small-buster-template: - sd-logging-setup - sd-devices-buster-template: + - sd-workstation-template-files + - sd-app-files + - sd-proxy-template-files + sd-large-buster-template: + - sd-logging-setup + - sd-workstation-template-files - sd-devices-files + - sd-viewer-files sd-gpg: - sd-gpg-files - sd-logging-setup - sd-proxy-buster-template: - - sd-proxy-template-files sd-app: - sd-app-config - sd-mime-handling - sd-viewer-buster-template: - - sd-viewer-files - sd-app-buster-template: - - sd-app-files sys-firewall: - sd-sys-firewall-files sd-whonix: @@ -52,8 +52,13 @@ base: - sd-logging-setup sd-viewer: - sd-mime-handling + sd-devices: + - sd-mime-handling sd-devices-dvm: - sd-mime-handling + sd-proxy: + - sd-proxy-files + - sd-mime-handling # "Placeholder" config to trigger TemplateVM boots, # so upgrades can be applied automatically via cron. diff --git a/dom0/securedrop-check-migration b/dom0/securedrop-check-migration new file mode 100755 index 00000000..6a2abffe --- /dev/null +++ b/dom0/securedrop-check-migration @@ -0,0 +1,32 @@ +#!/bin/bash +# Utility script to check whether the entire config should be reapplied +# as part of the GUI updater run, essentially re-running the "install" +# action from sdw-admin. Adds about ~20m to an update, so we'll only +# do it conditionally. + +set -e +set -u +set -o pipefail + + +# Location for empty files to signal to GUI updater that salt states +# should be re-run. +flag_dir="/tmp/sdw-migrations" +function request_migration() { + local reason + reason="$1" + shift 1 + mkdir -p "$flag_dir" + touch "${flag_dir}/${reason}" +} + +# Template consolidation. If old template names are found, +# then we must rerun the full states to re-apply. +if [[ -n "$(qvm-ls --tags sd-workstation --raw-list | perl -nE '/sd-(?!small|large).*-template/ and print $_')" ]] ; then + reason="template-consolidation" + echo "Migration required for ${reason}, will re-run Salt states." + request_migration "$reason" +else + echo "No migration required, skipping full state run against all VMs." + rm -rf "${flag_dir}" +fi diff --git a/dom0/securedrop-handle-upgrade b/dom0/securedrop-handle-upgrade index 3c3ec8e9..34c8fb95 100755 --- a/dom0/securedrop-handle-upgrade +++ b/dom0/securedrop-handle-upgrade @@ -15,7 +15,7 @@ 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 =~ "buster" ]]; then + if [[ ! $BASE_TEMPLATE =~ "small-buster" ]]; then if qvm-check --running sd-app; then qvm-shutdown --wait sd-app fi @@ -30,7 +30,7 @@ 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 =~ "buster" ]]; then + if [[ ! $BASE_TEMPLATE =~ "large-buster" ]]; then qubes-prefs default_dispvm '' qvm-shutdown --wait sd-viewer qvm-remove -f sd-viewer @@ -39,7 +39,7 @@ if [[ $TASK == "prepare" ]]; then if qvm-check --quiet sd-devices; then BASE_TEMPLATE=$(qvm-prefs sd-devices-dvm template) - if [[ ! $BASE_TEMPLATE =~ "buster" ]]; then + if [[ ! $BASE_TEMPLATE =~ "large-buster" ]]; then qvm-shutdown --wait sd-devices qvm-shutdown --wait sd-devices-dvm qvm-remove -f sd-devices @@ -52,7 +52,7 @@ 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 =~ "buster" ]]; then + if [[ ! $BASE_TEMPLATE =~ "large-buster" ]]; then qvm-shutdown --wait sd-proxy fi fi @@ -80,17 +80,28 @@ if [[ $TASK == "prepare" ]]; then fi fi - # Finally for sd-gpg, we simply shutdown the machine + # 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 =~ "buster" ]]; then + if [[ ! $BASE_TEMPLATE =~ "small-buster" ]]; then qvm-shutdown --wait sd-gpg 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-buster" ]]; then + qvm-shutdown --wait sd-log + fi + fi elif [[ $TASK == "remove" ]]; then # For each template, ensure the TemplateVM exists, that it is shut down # before deleting it. - for template in sd-app-template sd-viewer-template sd-devices-template sd-proxy-template + 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-buster-template sd-export-buster-template sd-svs-disp-buster-template sd-app-buster-template \ + sd-viewer-buster-template sd-proxy-buster-template sd-devices-buster-template sd-log-buster-template do if qvm-check "${template}" --quiet; then if qvm-check --running "${template}"; then diff --git a/launcher/sdw_updater_gui/Updater.py b/launcher/sdw_updater_gui/Updater.py index a83f682d..6dfa65e4 100644 --- a/launcher/sdw_updater_gui/Updater.py +++ b/launcher/sdw_updater_gui/Updater.py @@ -23,35 +23,71 @@ LOCK_FILE = "sdw-launcher.lock" LOG_FILE = "launcher.log" + +# We use a hardcoded temporary directory path in dom0. As dom0 is not +# a multi-user environment, we can safely assume that only the Updater is +# managing that filepath. Later on, we should consider porting the check-migration +# logic to leverage the Qubes Python API. +MIGRATION_DIR = "/tmp/sdw-migrations" # nosec + sdlog = logging.getLogger(__name__) # The are the TemplateVMs that require full patch level at boot in order to start the client, # as well as their associated TemplateVMs. # In the future, we could use qvm-prefs to extract this information. -current_templates = { - "dom0": "dom0", +current_vms = { "fedora": "fedora-31", - "sd-viewer": "sd-viewer-buster-template", - "sd-app": "sd-app-buster-template", - "sd-log": "sd-log-buster-template", - "sd-devices": "sd-devices-buster-template", - "sd-proxy": "sd-proxy-buster-template", + "sd-viewer": "sd-large-buster-template", + "sd-app": "sd-small-buster-template", + "sd-log": "sd-small-buster-template", + "sd-devices": "sd-large-buster-template", + "sd-proxy": "sd-small-buster-template", "sd-whonix": "whonix-gw-15", - "sd-gpg": "securedrop-workstation-buster", + "sd-gpg": "sd-small-buster-template", } +current_templates = set([val for key, val in current_vms.items() if key != "dom0"]) + def get_dom0_path(folder): return os.path.join(os.path.expanduser("~"), folder) -def apply_updates(vms=current_templates.keys()): +def run_full_install(): + """ + Re-apply the entire Salt config via sdw-admin. Required to enforce + VM state during major migrations, such as template consolidation. + """ + sdlog.info("Running sdw-admin apply") + cmd = ["sdw-admin", "--apply"] + subprocess.check_call(cmd) + + # Clean up flag requesting migration. Shell out since root created it. + subprocess.check_call(["sudo", "rm", "-rf", MIGRATION_DIR]) + + +def migration_is_required(): + """ + Check whether a full run of the Salt config via sdw-admin is required. + """ + result = False + if os.path.exists(MIGRATION_DIR): + if len(os.listdir(MIGRATION_DIR)) > 0: + sdlog.info("Migration is required, will enforce full config during update") + result = True + return result + + +def apply_updates(vms=current_templates): """ Apply updates to all TemplateVMs """ + # The updater thread sets 15% progress before the per-VM + # updates start, we'll base progress on that. + progress_start = 15 sdlog.info("Applying all updates") - for progress_current, vm in enumerate(vms): + for progress_current, vm in enumerate(vms, 1): upgrade_results = UpdateStatus.UPDATES_FAILED if vm == "dom0": @@ -63,7 +99,9 @@ def apply_updates(vms=current_templates.keys()): else: upgrade_results = _apply_updates_vm(vm) - progress_percentage = int(((progress_current + 1) / len(vms)) * 100 - 5) + progress_percentage = int(progress_start + ((progress_current) / len(vms)) * 100 - 25) + if progress_percentage < progress_start: + progress_percentage = progress_start yield vm, progress_percentage, upgrade_results @@ -109,28 +147,18 @@ 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, current_templates[vm])) + sdlog.info("Updating {}".format(vm)) try: subprocess.check_call( - [ - "sudo", - "qubesctl", - "--skip-dom0", - "--targets", - current_templates[vm], - "state.sls", - "update.qubes-vm", - ] + ["sudo", "qubesctl", "--skip-dom0", "--targets", vm, "state.sls", "update.qubes-vm"] ) except subprocess.CalledProcessError as e: sdlog.error( - "An error has occurred updating {}. Please contact your administrator.".format( - current_templates[vm] - ) + "An error has occurred updating {}. Please contact your administrator.".format(vm) ) sdlog.error(str(e)) return UpdateStatus.UPDATES_FAILED - sdlog.info("{} update successful".format(current_templates[vm])) + sdlog.info("{} update successful".format(vm)) return UpdateStatus.UPDATES_OK @@ -338,11 +366,8 @@ def shutdown_and_start_vms(): "sd-log", ] - # All TemplateVMs minus dom0 - sdw_templates = [val for key, val in current_templates.items() if key != "dom0"] - sdlog.info("Shutting down SDW TemplateVMs for updates") - for vm in sdw_templates: + for vm in sorted(current_templates): _safely_shutdown_vm(vm) sdlog.info("Shutting down SDW AppVMs for updates") diff --git a/launcher/sdw_updater_gui/UpdaterApp.py b/launcher/sdw_updater_gui/UpdaterApp.py index c0348e0c..b84cd941 100644 --- a/launcher/sdw_updater_gui/UpdaterApp.py +++ b/launcher/sdw_updater_gui/UpdaterApp.py @@ -172,17 +172,34 @@ def __init__(self): QThread.__init__(self) def run(self): - upgrade_generator = Updater.apply_updates() - results = {} - for vm, progress, result in upgrade_generator: - results[vm] = result - self.progress_signal.emit(progress) + # 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(["dom0"]) + results = {} # apply dom0 state + self.progress_signal.emit(10) result = Updater.apply_dom0_state() # add to results dict, if it fails it will show error message results["apply_dom0"] = result.value + + self.progress_signal.emit(15) + # rerun full config if dom0 checks determined it's required, + # otherwise proceed with per-VM package updates + if Updater.migration_is_required(): + # Progress bar will freeze for ~15m during full state run + self.progress_signal.emit(35) + Updater.run_full_install() + self.progress_signal.emit(75) + else: + upgrade_generator = Updater.apply_updates() + results = {} + for vm, progress, result in upgrade_generator: + results[vm] = result + self.progress_signal.emit(progress) + # reboot vms Updater.shutdown_and_start_vms() diff --git a/launcher/tests/test_updater.py b/launcher/tests/test_updater.py index 603db767..a73da940 100644 --- a/launcher/tests/test_updater.py +++ b/launcher/tests/test_updater.py @@ -13,6 +13,7 @@ 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 @@ -56,7 +57,11 @@ def test_updater_vms_present(): - assert len(updater.current_templates) == 9 + assert len(updater.current_vms) == 8 + + +def test_updater_templatevms_present(): + assert len(updater.current_templates) == 4 @mock.patch("Updater._write_updates_status_flag_to_disk") @@ -303,7 +308,7 @@ 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.keys()) +@pytest.mark.parametrize("vm", current_templates) @mock.patch("subprocess.check_call", side_effect="0") @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") @@ -313,30 +318,18 @@ def test_apply_updates_vms(mocked_info, mocked_error, mocked_call, vm): assert result == UpdateStatus.UPDATES_OK mocked_call.assert_called_once_with( - [ - "sudo", - "qubesctl", - "--skip-dom0", - "--targets", - current_templates[vm], - "state.sls", - "update.qubes-vm", - ] + ["sudo", "qubesctl", "--skip-dom0", "--targets", vm, "state.sls", "update.qubes-vm"] ) assert not mocked_error.called -@pytest.mark.parametrize("vm", current_templates.keys()) +@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( - current_templates[vm] - ) - ), + 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) @@ -423,7 +416,7 @@ def test_overall_update_status_reboot_not_done_previously( assert not mocked_error.called -@pytest.mark.parametrize("vm", current_templates.keys()) +@pytest.mark.parametrize("vm", current_vms.keys()) @mock.patch("subprocess.check_output") @mock.patch("Updater.sdlog.error") @mock.patch("Updater.sdlog.info") @@ -435,7 +428,7 @@ def test_safely_shutdown(mocked_info, mocked_error, mocked_output, vm): assert not mocked_error.called -@pytest.mark.parametrize("vm", current_templates.keys()) +@pytest.mark.parametrize("vm", current_vms.keys()) @mock.patch( "subprocess.check_output", side_effect=["0", "0", "0"], ) @@ -452,7 +445,7 @@ def test_safely_start(mocked_info, mocked_error, mocked_output, vm): assert not mocked_error.called -@pytest.mark.parametrize("vm", current_templates.keys()) +@pytest.mark.parametrize("vm", current_vms.keys()) @mock.patch( "subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "check_output"), ) @@ -468,7 +461,7 @@ def test_safely_start_fails(mocked_info, mocked_error, mocked_output, vm): mocked_error.assert_has_calls(call_list) -@pytest.mark.parametrize("vm", current_templates.keys()) +@pytest.mark.parametrize("vm", current_vms.keys()) @mock.patch( "subprocess.check_output", side_effect=subprocess.CalledProcessError(1, "check_output"), ) @@ -508,13 +501,9 @@ def test_shutdown_and_start_vms( ] template_vm_calls = [ call("fedora-31"), - call("sd-viewer-buster-template"), - call("sd-app-buster-template"), - call("sd-log-buster-template"), - call("sd-devices-buster-template"), - call("sd-proxy-buster-template"), + call("sd-large-buster-template"), + call("sd-small-buster-template"), call("whonix-gw-15"), - call("securedrop-workstation-buster"), ] app_vm_calls = [ call("sd-app"), @@ -560,13 +549,9 @@ def test_shutdown_and_start_vms_sysvm_fail( ] template_vm_calls = [ call("fedora-31"), - call("sd-viewer-buster-template"), - call("sd-app-buster-template"), - call("sd-log-buster-template"), - call("sd-devices-buster-template"), - call("sd-proxy-buster-template"), + call("sd-large-buster-template"), + call("sd-small-buster-template"), call("whonix-gw-15"), - call("securedrop-workstation-buster"), ] error_calls = [ call("Error while killing system VM: sys-firewall"), diff --git a/rpm-build/SPECS/securedrop-workstation-dom0-config.spec b/rpm-build/SPECS/securedrop-workstation-dom0-config.spec index e06fc1d4..db0718fa 100644 --- a/rpm-build/SPECS/securedrop-workstation-dom0-config.spec +++ b/rpm-build/SPECS/securedrop-workstation-dom0-config.spec @@ -1,12 +1,12 @@ Name: securedrop-workstation-dom0-config -Version: 0.4.0 -Release: 1%{?dist} +Version: 0.5.0 +Release: 0.rc1.1%{?dist} Summary: SecureDrop Workstation Group: Library License: GPLv3+ URL: https://github.com/freedomofpress/securedrop-workstation -Source0: securedrop-workstation-dom0-config-0.4.0.tar.gz +Source0: securedrop-workstation-dom0-config-0.5.0rc1.tar.gz BuildArch: noarch BuildRequires: python3-setuptools @@ -28,7 +28,7 @@ configuration over time. %undefine py_auto_byte_compile %prep -%setup -n securedrop-workstation-dom0-config-0.4.0 +%setup -n securedrop-workstation-dom0-config-0.5.0rc1 %build %{__python3} setup.py build @@ -62,6 +62,7 @@ install -m 644 dom0/*.conf %{buildroot}/srv/salt/ install -m 755 dom0/remove-tags %{buildroot}/srv/salt/ install -m 644 dom0/securedrop-login %{buildroot}/srv/salt/ install -m 644 dom0/securedrop-launcher.desktop %{buildroot}/srv/salt/ +install -m 755 dom0/securedrop-check-migration %{buildroot}/srv/salt/ install -m 755 dom0/securedrop-handle-upgrade %{buildroot}/srv/salt/ install -m 755 dom0/update-xfce-settings %{buildroot}/srv/salt/ install -m 755 scripts/sdw-admin.py %{buildroot}/%{_bindir}/sdw-admin @@ -106,6 +107,10 @@ find /srv/salt -maxdepth 1 -type f -iname '*.top' \ | xargs qubesctl top.enable > /dev/null %changelog +* Tue Oct 27 2020 SecureDrop Team - 0.5.0 +- Consolidates templates into small and large +- Modifies updater UI to rerun full state if required + * Tue Jul 07 2020 SecureDrop Team - 0.4.0 - Consolidates updates from two stages into one - Makes the updater UI more compact diff --git a/scripts/prep-dev b/scripts/prep-dev index 34bb458d..5db90647 100755 --- a/scripts/prep-dev +++ b/scripts/prep-dev @@ -22,6 +22,7 @@ fi echo "Deploying Salt config..." echo "Uninstalling any previous RPM versions..." +sudo dnf clean all sudo dnf remove -y securedrop-workstation-dom0-config || true echo "Installing RPM at $latest_rpm ..." sudo dnf install -y "$latest_rpm" diff --git a/scripts/provision-all b/scripts/provision-all index cb595c09..9838b3dc 100755 --- a/scripts/provision-all +++ b/scripts/provision-all @@ -18,7 +18,7 @@ echo "Set up dom0 config files, including RPC policies, and create VMs" sudo qubesctl --show-output state.highstate echo "Set up logging VMs early" -sudo qubesctl --show-output --skip-dom0 --targets sd-log,sd-log-buster-template state.highstate +sudo qubesctl --show-output --skip-dom0 --targets sd-log,sd-small-buster-template state.highstate # Reboot sd-log so it's ready to receive logs from other VMs about to be configured qvm-shutdown --wait sd-log && qvm-start sd-log # Provision whonix-gw-15 with log additions because it isn't tagged with sd-workstation (we don't want it removed after a make clean) diff --git a/sd-proxy/do-not-open-here b/sd-proxy/do-not-open-here deleted file mode 100755 index 982a6ece..00000000 --- a/sd-proxy/do-not-open-here +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import sys -from PyQt5 import Qt - - -a = Qt.QApplication(sys.argv) - -nope = Qt.QLabel( - "Please do not use this VM to open any files" " aside from those downloaded from SecureDrop." -) - -nope.show() -a.exec_() diff --git a/sd-proxy/do-not-open.desktop b/sd-proxy/do-not-open.desktop deleted file mode 100644 index 0d5e188d..00000000 --- a/sd-proxy/do-not-open.desktop +++ /dev/null @@ -1,4 +0,0 @@ -[Desktop Entry] -Type=Application -Name=Do Not Open On This VM -Exec=/usr/bin/do-not-open-here diff --git a/tests/base.py b/tests/base.py index 7c440aff..4a732403 100644 --- a/tests/base.py +++ b/tests/base.py @@ -67,8 +67,14 @@ def _package_is_installed(self, pkg): Confirms that a given package is installed inside the VM. """ # dpkg --verify will exit non-zero for a non-installed pkg, - # and dom0 will percolate that error code - subprocess.check_call(["qvm-run", "-a", "-q", self.vm_name, "dpkg --verify {}".format(pkg)]) + # catch that and return False + try: + subprocess.check_call( + ["qvm-run", "-a", "-q", self.vm_name, "dpkg --verify {}".format(pkg)] + ) + except subprocess.CalledProcessError: + return False + return True def assertFilesMatch(self, remote_path, local_path): @@ -133,19 +139,12 @@ def qubes_gpg_domain_configured(self, vmname=False): sd-app should have it set to sd-gpg. All other AppVMs should not have this configured. """ - expected_profile_app = 'if [ "$(qubesdb-read /name)" = "sd-app" ]; then export QUBES_GPG_DOMAIN="sd-gpg"; fi\n' # noqa: E501 - expected_env_app = "sd-gpg" - expected_env_all = "" - profile_path = "/etc/profile.d/sd-app-qubes-gpg-domain.sh" - env_cmd = 'echo "$QUBES_GPG_DOMAIN"' env_contents = self._run(env_cmd) if vmname == "sd-app": - self.assertTrue(self._fileExists(profile_path)) - profile_contents = self._get_file_contents(profile_path) - self.assertEqual(profile_contents, expected_profile_app) - self.assertEqual(env_contents, expected_env_app) + expected_env = "sd-gpg" else: - self.assertFalse(self._fileExists(profile_path)) - self.assertEqual(env_contents, expected_env_all) + expected_env = "" + + self.assertEqual(env_contents, expected_env) diff --git a/tests/test_dom0_config.py b/tests/test_dom0_config.py index 3f10887f..2c596f9c 100644 --- a/tests/test_dom0_config.py +++ b/tests/test_dom0_config.py @@ -10,13 +10,16 @@ "sd-svs-buster-template", "sd-export-buster-template", "sd-svs-disp-buster-template", -] - -VMS_TO_UPDATE = [ "sd-app-buster-template", "sd-viewer-buster-template", "sd-proxy-buster-template", "sd-devices-buster-template", + "sd-log-buster-template", +] + +VMS_TO_UPDATE = [ + "sd-large-buster-template", + "sd-small-buster-template", "whonix-ws-15", "whonix-gw-15", "securedrop-workstation-buster", diff --git a/tests/test_proxy_vm.py b/tests/test_proxy_vm.py index f85a1c21..13b01e56 100644 --- a/tests/test_proxy_vm.py +++ b/tests/test_proxy_vm.py @@ -1,5 +1,6 @@ import unittest import json +import subprocess from base import SD_VM_Local_Test @@ -10,7 +11,11 @@ def setUp(self): super(SD_Proxy_Tests, self).setUp() def test_do_not_open_here(self): - self.assertFilesMatch("/usr/bin/do-not-open-here", "sd-proxy/do-not-open-here") + """ + The do-not-open here script has been removed from sd-proxy. + All VMs now default to using open-in-dvm. + """ + assert not self._fileExists("/usr/bin/do-not-open-here") def test_sd_proxy_package_installed(self): self.assertTrue(self._package_is_installed("securedrop-proxy")) @@ -20,6 +25,9 @@ def test_sd_proxy_yaml_config(self): config = json.load(c) hostname = config["hidserv"]["hostname"] + # Config file moved to private volume during template consolidation + assert not self._fileExists("/etc/sd-proxy.yaml") + wanted_lines = [ "host: {}".format(hostname), "scheme: http", @@ -28,7 +36,25 @@ def test_sd_proxy_yaml_config(self): "dev: False", ] for line in wanted_lines: - self.assertFileHasLine("/etc/sd-proxy.yaml", line) + self.assertFileHasLine("/home/user/.securedrop_proxy/sd-proxy.yaml", line) + + def test_sd_proxy_writable_config_dir(self): + # Directory must be writable by normal user. If owned by root, + # sd-proxy can't write logs, and will fail, blocking client logins. + result = False + try: + self._run("test -w /home/user/.securedrop_proxy") + result = True + except subprocess.CalledProcessError: + pass + self.assertTrue(result) + + def test_sd_proxy_rpc_spec(self): + wanted_lines = [ + "/usr/bin/sd-proxy /home/user/.securedrop_proxy/sd-proxy.yaml", + ] + for line in wanted_lines: + self.assertFileHasLine("/etc/qubes-rpc/securedrop.Proxy", line) def test_whonix_ws_repo_absent(self): """ @@ -43,14 +69,12 @@ def test_logging_configured(self): self.logging_configured() def test_mime_types(self): - with open("sd-proxy/mimeapps.list", "r") as f: - lines = f.readlines() - for line in lines: - if line != "[Default Applications]\n" and not line.startswith("#"): - mime_type = line.split("=")[0] - expected_app = line.split("=")[1].split(";")[0] - actual_app = self._run("xdg-mime query default {}".format(mime_type)) - self.assertEqual(actual_app, expected_app) + cmd = "perl -F= -lane 'print $F[0]' /usr/share/applications/mimeapps.list" + results = self._run(cmd) + for line in results.split("\n"): + if line != "[Default Applications]" and not line.startswith("#"): + actual_app = self._run("xdg-mime query default {}".format(line)) + self.assertEqual(actual_app, "open-in-dvm.desktop") def test_gpg_domain_configured(self): self.qubes_gpg_domain_configured(self.vm_name) diff --git a/tests/test_viewer.py b/tests/test_viewer.py index 8e4e4a23..55309600 100644 --- a/tests/test_viewer.py +++ b/tests/test_viewer.py @@ -9,9 +9,9 @@ def setUp(self): self.vm_name = "sd-viewer" super(SD_Viewer_Tests, self).setUp() - def test_sd_svs_disp_config_package_installed(self): - pkg = "securedrop-workstation-svs-disp" - self.assertTrue(self._package_is_installed(pkg)) + def test_sd_viewer_metapackage_installed(self): + self.assertTrue(self._package_is_installed("securedrop-workstation-viewer")) + self.assertFalse(self._package_is_installed("securedrop-workstation-svs-disp")) def test_sd_viewer_evince_installed(self): pkg = "evince" diff --git a/tests/test_vms_exist.py b/tests/test_vms_exist.py index 4e4f1cc2..070ad06f 100644 --- a/tests/test_vms_exist.py +++ b/tests/test_vms_exist.py @@ -68,7 +68,7 @@ def test_sd_proxy_config(self): vm = self.app.domains["sd-proxy"] nvm = vm.netvm self.assertTrue(nvm.name == "sd-whonix") - self.assertTrue(vm.template == "sd-proxy-buster-template") + self.assertTrue(vm.template == "sd-small-buster-template") self.assertTrue(vm.autostart is True) self.assertFalse(vm.provides_network) self.assertFalse(vm.template_for_dispvms) @@ -78,7 +78,7 @@ def test_sd_app_config(self): vm = self.app.domains["sd-app"] nvm = vm.netvm self.assertTrue(nvm is None) - self.assertTrue(vm.template == "sd-app-buster-template") + self.assertTrue(vm.template == "sd-small-buster-template") self.assertFalse(vm.provides_network) self.assertFalse(vm.template_for_dispvms) self._check_kernel(vm) @@ -96,7 +96,7 @@ def test_sd_viewer_config(self): vm = self.app.domains["sd-viewer"] nvm = vm.netvm self.assertTrue(nvm is None) - self.assertTrue(vm.template == "sd-viewer-buster-template") + self.assertTrue(vm.template == "sd-large-buster-template") self.assertFalse(vm.provides_network) self.assertTrue(vm.template_for_dispvms) self._check_kernel(vm) @@ -108,7 +108,7 @@ def test_sd_gpg_config(self): nvm = vm.netvm self.assertTrue(nvm is None) # No sd-gpg-template, since keyring is managed in $HOME - self.assertTrue(vm.template == "securedrop-workstation-buster") + self.assertTrue(vm.template == "sd-small-buster-template") self.assertTrue(vm.autostart is True) self.assertFalse(vm.provides_network) self.assertFalse(vm.template_for_dispvms) @@ -119,7 +119,7 @@ def test_sd_log_config(self): vm = self.app.domains["sd-log"] nvm = vm.netvm self.assertTrue(nvm is None) - self.assertTrue(vm.template == "sd-log-buster-template") + self.assertTrue(vm.template == "sd-small-buster-template") self.assertTrue(vm.autostart is True) self.assertFalse(vm.provides_network) self.assertFalse(vm.template_for_dispvms) @@ -146,27 +146,27 @@ def test_sd_workstation_template(self): self._check_service_running(vm, "paxctld") def test_sd_proxy_template(self): - vm = self.app.domains["sd-proxy-buster-template"] + vm = self.app.domains["sd-small-buster-template"] nvm = vm.netvm self.assertTrue(nvm is None) self.assertTrue("sd-workstation" in vm.tags) def sd_app_template(self): - vm = self.app.domains["sd-app-buster-template"] + vm = self.app.domains["sd-small-buster-template"] nvm = vm.netvm self.assertTrue(nvm is None) self.assertTrue("sd-workstation" in vm.tags) self._check_kernel(vm) def sd_viewer_template(self): - vm = self.app.domains["sd-viewer-buster-template"] + vm = self.app.domains["sd-large-buster-template"] nvm = vm.netvm self.assertTrue(nvm is None) self.assertTrue("sd-workstation" in vm.tags) self.assertTrue(vm.template_for_dispvms) def sd_export_template(self): - vm = self.app.domains["sd-devices-buster-template"] + vm = self.app.domains["sd-large-buster-template"] nvm = vm.netvm self.assertTrue(nvm is None) self.assertTrue("sd-workstation" in vm.tags) @@ -189,8 +189,16 @@ def sd_export(self): self.assertTrue("sd-workstation" in vm.tags) self._check_kernel(vm) - def sd_log_template(self): - vm = self.app.domains["sd-log-buster-template"] + def sd_small_template(self): + vm = self.app.domains["sd-small-buster-template"] + nvm = vm.netvm + self.assertTrue(nvm is None) + self.assertTrue("sd-workstation" in vm.tags) + self.assertFalse(vm.template_for_dispvms) + self._check_kernel(vm) + + def sd_large_template(self): + vm = self.app.domains["sd-large-buster-template"] nvm = vm.netvm self.assertTrue(nvm is None) self.assertTrue("sd-workstation" in vm.tags) diff --git a/utils/qa-switch.sh b/utils/qa-switch.sh new file mode 100644 index 00000000..e73f519f --- /dev/null +++ b/utils/qa-switch.sh @@ -0,0 +1,29 @@ +#!/usr/bin/bash + +# +# Updates securedrop templates and dom0 to use QA repos and +# template-consolidation component. +# +# + +if [[ $(id -u) -ne 0 ]] ; then echo "Please run as root" ; exit 1 ; fi + +cp -R `dirname "$0"`/qa-switch/ /srv/salt/ + +cd /srv/salt +echo Updating dom0... +qubesctl --show-output --targets dom0 state.apply qa-switch.dom0 + +export template_list="sd-app-buster-template sd-devices-buster-template sd-log-buster-template sd-proxy-buster-template sd-viewer-buster-template securedrop-workstation-buster whonix-gw-15" + +echo Updating Debian-based templates: +for t in $template_list; do echo Updating $t...; qubesctl --show-output --skip-dom0 --targets $t state.apply switch.buster; done + +echo Replacing prod config YAML... + +if [ ! -f "/srv/salt/qa-switcher/sd-default-config.yml.orig" ]; then + cp sd-default-config.yml qa-switch/sd-default-config.yml.orig +fi +cp qa-switch/sd-qa-config.yml sd-default-config.yml + +echo "Done! (Run this script after 'sudo qubes-dom0-update -y' to reapply") diff --git a/utils/qa-switch/buster.sls b/utils/qa-switch/buster.sls new file mode 100644 index 00000000..0b519004 --- /dev/null +++ b/utils/qa-switch/buster.sls @@ -0,0 +1,10 @@ +remove-prod-apt-repo: + pkgrepo.absent: + - name: "deb [arch=amd64] https://apt.freedom.press buster main" + +add-test-apt-repo: + pkgrepo.managed: + - name: "deb [arch=amd64] https://apt-test.freedom.press buster main" + - file: /etc/apt/sources.list.d/securedrop_workstation.list + - key_url: "salt://sd/sd-workstation/apt-test-pubkey.asc" + - clean_file: True diff --git a/utils/qa-switch/dom0.sls b/utils/qa-switch/dom0.sls new file mode 100644 index 00000000..250115be --- /dev/null +++ b/utils/qa-switch/dom0.sls @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# vim: set syntax=yaml ts=2 sw=2 sts=2 et : + +## +# Installs dom0 config scripts specific to tracking updates +# over time. These scripts should be ported to an RPM package. +## + +dom0-rpm-test-key: + file.managed: + # We write the pubkey to the repos config location, because the repos + # config location is automatically sent to dom0's UpdateVM. Otherwise, + # we must place the GPG key inside the fedora-31 TemplateVM, then + # restart sys-firewall. + - name: /etc/pki/rpm-gpg/RPM-GPG-KEY-securedrop-workstation + - source: "salt://sd/sd-workstation/apt-test-pubkey.asc" + - user: root + - group: root + - mode: 644 + +dom0-rpm-test-key-import: + cmd.run: + - name: sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-securedrop-workstation + - require: + - file: dom0-rpm-test-key + +dom0-workstation-rpm-repo: + # We use file.managed rather than pkgrepo.managed, because Qubes dom0 + # settings write new repos to /etc/yum.real.repos.d/, but only /etc/yum.repos.d/ + # is copied to the UpdateVM for fetching dom0 packages. + file.managed: + - name: /etc/yum.repos.d/securedrop-workstation-dom0.repo + - user: root + - group: root + - mode: 644 + - contents: | + [securedrop-workstation-dom0] + gpgcheck=1 + gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-securedrop-workstation + enabled=1 + baseurl=https://yum-test.securedrop.org/workstation/dom0/f25 + name=SecureDrop Workstation Qubes dom0 repo + - require: + - file: dom0-rpm-test-key diff --git a/utils/qa-switch/sd-qa-config.yml b/utils/qa-switch/sd-qa-config.yml new file mode 100644 index 00000000..4afde585 --- /dev/null +++ b/utils/qa-switch/sd-qa-config.yml @@ -0,0 +1,11 @@ +--- +# Production variables, for use with real-world installs +prod: + dom0_yum_repo_url: "https://yum-test.securedrop.org/workstation/dom0/f25" + apt_repo_url: "https://apt-test.freedom.press" + signing_key_filename: "apt-test-pubkey.asc" +# Development variables, suited for use during local development +dev: + dom0_yum_repo_url: "https://yum-test.securedrop.org/workstation/dom0/f25" + apt_repo_url: "https://apt-test.freedom.press" + signing_key_filename: "apt-test-pubkey.asc" diff --git a/utils/qa-switch/top.sls b/utils/qa-switch/top.sls new file mode 100644 index 00000000..249ebb43 --- /dev/null +++ b/utils/qa-switch/top.sls @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# vim: set syntax=yaml ts=2 sw=2 sts=2 et : + +base: + dom0: + - sd-dom0-switch + + sd-log-buster-template: + - sd-buster-switch + sd-devices-buster-template: + - sd-buster-switch + sd-gpg: + - sd-buster-switch + sd-proxy-buster-template: + - sd-buster-switch + sd-app: + - sd-buster-switch + sd-viewer-buster-template: + - sd-buster-switch + sd-app-buster-template: + - sd-buster-switch + sys-firewall: + - sd-buster-switch + sd-whonix: + - sd-buster-switch + securedrop-workstation-buster: + - sd-buster-switch + sys-usb: + - sd-buster-switch + whonix-gw-15: + - sd-buster-switch + sd-log: + - sd-buster-switch