-
Notifications
You must be signed in to change notification settings - Fork 46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add sd-export VMs and basic export flow #259
Conversation
94f0afa
to
440920b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few comments based on initial read-through of the diff. Have not yet begun functional steps listed in the test plan, still to come.
dom0/sd-export-files.sls
Outdated
- source: salt://sd/sd-export/send-to-usb.desktop | ||
- user: root | ||
- group: root | ||
- mode: 755 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mode should be 644 for desktop file
dom0/sd-export-files.sls
Outdated
- source: salt://sd/sd-export/application-x-sd-export.xml | ||
- user: root | ||
- group: root | ||
- mode: 755 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mode should be 644 for XML file
@@ -16,6 +16,9 @@ declare -a sd_workstation_vm_names=( | |||
sd-whonix | |||
sd-svs-disp | |||
sd-svs-disp-template | |||
sd-export-template | |||
sd-export-dvm | |||
sd-export-usb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's prefer sd-export-usb
to sd-export
in all cases, since we also plan to support export methods other than USB. So these lines would become:
sd-export-usb-template
sd-export-usb-dvm
sd-export-usb
And the relevant config lines referring to these VMs should be updated, as well. Discussion in the motivating ticket (#84) indicates that we may want to consolidate all export functionality into a single stateless VM. Given the disparate config needs (such as net/no-net) between just USB export and e.g. Onionshare, let's plan to name explicitly from the start, and consolidate if and only if we identify a sound method of doing so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've renamed sd-export-dvm to sd-export-usb-dvm. I think this makes sense, we want a DispVM template AppVM to not have network access.
In an attempt to minimize the total amount of templates, I think it might make sense to use the same template for USB exports and other exports(networked, OnionShare exports), as to reduce the time to upgrade templates (which is already quite long, as each template must be updated independently). Since we will likely be creating a AppVM/DispVM template for network-specific exports, I think it might make sense for them to share templates.
I don't feel strongly about sharing the template, happy to break up the template further if you think it's warranted.
sd-export/send-to-usb
Outdated
@@ -0,0 +1,84 @@ | |||
#! /usr/bin/python3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove whitespace in shebang line. While it's technically permitted for whitespace to be present, it's inconsistent with the other scripts in the project:
$ grep -rIPoh '^#!\s?+.*$' | sort | uniq -c
10 #!/bin/bash
10 #!/bin/sh
1 #!/usr/bin/env python3
1 #! /usr/bin/python3
Recommend using #!/usr/bin/env python3
, same as in the sd-proxy script. The whitespace in the shebang line also breaks autodetection of the file as a Python script, meaning the flake8 linting didn't catch it. 🙃 Expect a few linting errors once the shebang is updated.
sd-export/send-to-usb
Outdated
out = subprocess.call(["sudo", "chown", "-R", "user:user", MOUNTPOINT]) | ||
out = subprocess.call( | ||
["sudo", "mount", os.path.join("/dev/mapper/", ENCRYPTED_DEVICE), MOUNTPOINT] | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: subprocess.call
returns the exit code from the run command, not output from the command, as implied by "out". As with the subsequent subprocess.call
s, fine to omit the out =
altogether, since we're not using it.
Might be worth using check_call
instead and catching CalledProcessError to exit 0, otherwise we can't know that the commands are succeeding. In general, error handling in all the mimetype handler scripts is tricky, as we don't want to fail over over to an alternative solution.
dom0/sd-export.sls
Outdated
- name: > | ||
qvm-remove --force sd-export-usb || true; | ||
qvm-create --class DispVM --template sd-export-dvm --label red sd-export-usb; | ||
qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a shame to force removal of the VM on every provisioning run. A quick glance at the qvm docs seems to confirm we don't have a Salt-managed method for this, but perhaps we could wrangle it with the Python API. Fine as-is, for now, we'll want to switch wholesale to the Python API for VM creation if necessary (#159).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a patch to improve the error handling somewhat on this rather forceful one-liner, by breaking it up:
Author: Conor Schaefer <[email protected]>
Date: Mon Jun 3 17:23:42 2019 -0700
Breaks up sd-export-dvm salt config
Error handling is difficult in bash one-liners, so let's leverage
Saltstack for qvm configs as far as possible. The `qvm-check` command
returns false if the VM does not exist, so we'll create it only
conditionally.
diff --git a/dom0/sd-export.sls b/dom0/sd-export.sls
index 58cc269..32c879c 100644
--- a/dom0/sd-export.sls
+++ b/dom0/sd-export.sls
@@ -47,16 +47,31 @@ sd-export-template-sync-appmenus:
- onchanges:
- qvm: sd-export-template
-{% import_json "sd/config.json" as d %}
-
# Here we must create as the salt stack does not appear to allow us to create
# VMs with the class DispVM and attach the usb device specified in the config
# permanently to this VM
-create-named-sd-export-dispvm-and-permanently-attach:
+sd-export-create-named-dispvm:
+ cmd.run:
+ - name: >
+ qvm-check sd-export-usb ||
+ qvm-create --class DispVM --template sd-export-usb-dvm --label red sd-export-usb
+ - require:
+ - qvm: sd-export-usb-dvm
+
+{% import_json "sd/config.json" as d %}
+
+sd-export-named-dispvm-permanently-attach-usb:
cmd.run:
- name: >
- qvm-kill sd-export-usb || true;
- qvm-remove --force sd-export-usb || true;
- qvm-create --class DispVM --template sd-export-usb-dvm --label red sd-export-usb;
- qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true;
- qvm-tags sd-export-usb add sd-workstation
+ qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true
+ - require:
+ - cmd: sd-export-create-named-dispvm
+
+sd-export-named-dispvm-add-tags:
+ qvm.vm:
+ - name: sd-export-usb
+ - tags:
+ - add:
+ - sd-workstation
+ - require:
+ - cmd: sd-export-create-named-dispvm
Since I encountered problems during initial review, I'm not appending this commit, to make sure we've working from the same branch as we debug.
Thanks @conorsch for the first pass, I've addressed most of your comments (see inline) |
* Export named disposable VM creation (sd-export-usb based on sd-export-dvm based on sd-export-template) * Permanently attach a config-specified usb port id to sd-export VM * Qubes will always automount anything connected to that port to sd-export * Use mime handler for to handle transfers to sd-export-usb * sd-export named disposable vm was named to sd-export-usb, so we must change the tests to reflect this change.
This is based off of early decryption flow introduced in 9b4c37e Documents x-sd-export used to transfer files from sd-svs to sd-export-usb
* rename sd-export-dvm to rename-export-usb-dvm * remove executable bit on certain files * improved error/exception handling
c0c400a
to
4b047e6
Compare
* Use securedrop-workstation as template for sd-export-template * Fix race when copying files to usb key * Add cryptsetup to sd-export-template
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had some problems using the export flow via manual testing. Commented in-line where the problem seems to lie—happy to discuss further if anything's unclear, @emkll.
See also a patch provided to clean up the new template logic, given the special case of the DispVM class.
In order to test, I reformatted a USB stick on the CLI, using fdisk
, cryptsetup
, and mkfs.ext4
. I'm able to view files in it fine, but I did not use the Gnome Disk Utility for creating the USB, as requested in the test plan. I suspect the problems reported about fs ownership would exist either way, but in case not, wanted to be clear about divergence from the test plan.
nvm = vm.netvm | ||
self.assertTrue(nvm is None) | ||
self.assertTrue('sd-workstation' in vm.tags) | ||
self._check_kernel(vm) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add check for self.assertTrue(vm.klass == "DispVM")
to the sd-export-usb
config, since it's the only VM we expect with that class name.
sd-export/send-to-usb
Outdated
|
||
# move files to drive (overrites files with same filename) and unmount drive | ||
target_folder_path = os.path.join(MOUNTPOINT, target_folder) | ||
subprocess.check_call(["mkdir", target_folder_path]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mkdir command on L83 failed for me reliably:
user@sd-svs:~$ qvm-open-in-vm sd-export-usb submission.sd-export
mkdir: cannot create directory '/media/usb/sd-export-2019-0603-182117': Permission denied
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
OSError: [Errno 9] Bad file descriptor
It appears that the fresh LUKS ext4 USB device I'm using has the root dir owned by root, so all subsequent operations failed. As written, the chown
operation preceding didn't fix, because it ran chown on the host dir, not the filesystem inside the USB device.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even after editing the send-to-usb
script in-place, I'm still unable to open a submission:
user@sd-svs:~$ qvm-open-in-vm sd-export-usb submission.sd-export
mount: /dev/mapper/encrypted_volume is already mounted or /media/usb busy
/dev/mapper/encrypted_volume is already mounted on /media/usb
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
OSError: [Errno 9] Bad file descriptor
So we either need more conditional logic on when to run those mount commands, or we should adjust the shell-out calls to operate essentially as set +e
would in bash, meaning execution errors don't halt the script. A custom function with the try/except logic enclosed within would accommodate, for instance. Let's aim for 1) more informative error output and 2) smarter conditional logic at minimum, then evaluate if we need a bigger reorg.
except (subprocess.CalledProcessError, OSError) as e: | ||
print("An error occurred while mounting disk or copying files to disk:") | ||
print(e.output) | ||
sys.exit(0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The sys.exit(0)
is solid, we want to preserve that—but it appears the print()
statements aren't percolating to the qvm-open-in-vm
command. Perhaps if we used sys.stderr.write()
, we'd be able to view the debugging output here during development. Ideally we'd use proper logging
(#19), but we can follow up on that when ready.
dom0/sd-export.sls
Outdated
- name: > | ||
qvm-remove --force sd-export-usb || true; | ||
qvm-create --class DispVM --template sd-export-dvm --label red sd-export-usb; | ||
qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a patch to improve the error handling somewhat on this rather forceful one-liner, by breaking it up:
Author: Conor Schaefer <[email protected]>
Date: Mon Jun 3 17:23:42 2019 -0700
Breaks up sd-export-dvm salt config
Error handling is difficult in bash one-liners, so let's leverage
Saltstack for qvm configs as far as possible. The `qvm-check` command
returns false if the VM does not exist, so we'll create it only
conditionally.
diff --git a/dom0/sd-export.sls b/dom0/sd-export.sls
index 58cc269..32c879c 100644
--- a/dom0/sd-export.sls
+++ b/dom0/sd-export.sls
@@ -47,16 +47,31 @@ sd-export-template-sync-appmenus:
- onchanges:
- qvm: sd-export-template
-{% import_json "sd/config.json" as d %}
-
# Here we must create as the salt stack does not appear to allow us to create
# VMs with the class DispVM and attach the usb device specified in the config
# permanently to this VM
-create-named-sd-export-dispvm-and-permanently-attach:
+sd-export-create-named-dispvm:
+ cmd.run:
+ - name: >
+ qvm-check sd-export-usb ||
+ qvm-create --class DispVM --template sd-export-usb-dvm --label red sd-export-usb
+ - require:
+ - qvm: sd-export-usb-dvm
+
+{% import_json "sd/config.json" as d %}
+
+sd-export-named-dispvm-permanently-attach-usb:
cmd.run:
- name: >
- qvm-kill sd-export-usb || true;
- qvm-remove --force sd-export-usb || true;
- qvm-create --class DispVM --template sd-export-usb-dvm --label red sd-export-usb;
- qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true;
- qvm-tags sd-export-usb add sd-workstation
+ qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true
+ - require:
+ - cmd: sd-export-create-named-dispvm
+
+sd-export-named-dispvm-add-tags:
+ qvm.vm:
+ - name: sd-export-usb
+ - tags:
+ - add:
+ - sd-workstation
+ - require:
+ - cmd: sd-export-create-named-dispvm
Since I encountered problems during initial review, I'm not appending this commit, to make sure we've working from the same branch as we debug.
Error handling is difficult in bash one-liners, so let's leverage Saltstack for qvm configs as far as possible. The `qvm-check` command returns false if the VM does not exist, so we'll create it only conditionally.
Refactor of the script, with no changes to functionality. Motivation to refactor is two-fold: readability and ensuring we catch all possible exceptions. Highlights: * Added `exit_gracefully` def to log errors and exit 0 * More explicit var names for constants/globals * Breaks logic into multiple discrete functions * Bottles up logic in main block, with enclosing try/except These changes should help to ensure that we're catching as many different exceptions as possible. The script is now slightlier friendlier to new devs, given the slicing into functions.
The `sd-export-usb` VM is a proper named DispVM, the only one in the config to date, so let's add an assertion to the config tests to ensure we don't lose this highly valuable property. I've not added tests asserting that a USB device is attached because a developer likely will not have a USB device configured and physically plugged in at all times.
The recent changes to the script resolved the ownership problems I'd reported. Able to use the removable LUKS-encrypted device reliably. Testing is of course slow. Let's look for opportunities to flesh out functional tests to verify the inter-VM behavior. After confirming the functionality in the script was working as advertised, I tried to make the script more resilient to unexpected non-zero exits. See the new try/except logic. Details in commit messages, but in short, it's mostly slicing into defs, with no changes to core logic. Also tacked on a slight change to the provisioning logic, mentioned in a patch above, and a corresponding test tracking the DispVM status for sd-export-usb. Quite happy with these changes going in immediately. Since I appended changes on top of yours, leaving the PR unmerged so you can sign-off on them, @emkll. |
Feel free to squash/amend as you see fit, as well! |
clean makefile target will now force remove the usb device from sd-export-usb and delete VMs in order. sd-export-dvm should be removed *after* sd-export-usb, as the former is the DispVM template for the latter. sd-export-dvm will no longer persist after a make clean
sd-export-named-dispvm-permanently-attach-usb: | ||
cmd.run: | ||
- name: > | ||
qvm-usb attach --persistent sd-export-usb {{ d.usb.device }} || true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch on the clean behavior, I can confirm that without your commit, I need to run |
This will ensure that only 1 device is attached to the sd-export-usb domain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes @conorsch , this significantly improves the error handling. Your changes LGTM.
I've also added efc1ede to properly handle the removal of the sd-export
VMs, as well as dbe00eb to handle the case where the usb device ID is changed in config.json, to avoid two usb devices from being attached to sd-export-usb
Ran through another round of testing, looks great. The cleanup tweaks in particular are great to have—nice catch. I'm going to merge without adding any additional tests. This test passes in most configurations: Patch of sd-export-usb qvm-device test
It's important to be aware that the USB drive must be attached during initial provisioning. If the device was not connected when the salt tasks ran, then the device will never be attached to the sd-export-usb, even after plugging it in and confirming the device ids match. That install-time limitation has significant implications for the admin/journalist story during the pilot. Thanks for your patience during iterative review, @emkll. The implementation as written is an extremely solid start—looking forward to building on it! |
Description
Towards #84 :
This adds:
sd-export-template
, a TemplateVM to manage every package related to (all?) exportsUSB devices connected to a given USB port will automatically be attached to
sd-export-usb
, andsd-export-usb
has a mime handler that will automatically copy .gpg files to the attached USB storage.There are a few limitations in this current approach:
Dedicated USB port for exporting. It seems like that would less error prone than using the Qubes menus, if there's some sort of labeling on the workstation itself.
Transfer device provisioning is not automatic, it may be difficult to do without risking the data.
Export staging area (Nautilus window in
sd-export-usb
) would provide more user flexibility, but also more complexity.Error handling/feedback may be challenging with the
qvm-open-in-vm
approachTest Plan
config.json
per the docsmake all
andmake test
ormake sd-export
andmake test
complete successfully and all tests pass.sd-export
) insd-svs
containing the file structure described in the docs.qvm-open-in-vm sd-export-usb <file>.sdexport
completes without errorsd-export-usb
is indeed disposable (for example, home directory contents don't persist reboots)Note that the first iteration (85f3d67) was using non-encrypted devices, with symmetrically-encrypted files. Due to the default behavior or most tools extracting and storing the plaintext version next to the encrypted version on the filesystem, using a disk that was not full disk encrypted is likely not a very good idea, should the USB be misplaced.
Demo