Open piotrbartman opened 3 months ago
It will be great to have some autoattach controls :) Right now on SecureDrop Workstation we hack together some autoattach behaviour via udev rules, but it's a blunt instrument - I'm chiming in after a nudge from marek in case any of this info about our use-case is helpful.
Some last notes about user feedback:
Hope this information is helpful!
On the specific USB port side, the problem is that if you don't limit attaching to a specific USB port, we can have a huge security issue - devices can lie about their identity, a USB hub complicates things etc. I think the security team had a lot of opinions on why we can't have "any port" here.
For now, a device is represented by the pair (backend-vm, ident), which aligns more with a Port. In the user's mental model, we equate a Device with what is plugged into the Port. The issue is that only (backend-vm, ident) can be trusted, while all information about the device is obtained from the Device itself, which is less trustworthy.
Proposed solution:
A set of rules as follows:
frontend-vm backend-vm devclass port_id device action
*`-vm`**:
sys-usb
*
wildcarddevclass:
*
, usb
, block
, pci
, etc.port_id:
sd*
or 3-[0-1]
device (self_identity):
<vendor_id>:<product_id>:<serial if any>:<interface1interface2...>
*
*
*
. Empty string means the device does not have a serial number, and we do not accept any.x******
, where x corresponds to devclass (u
, p
, b
, t
, ?
, *
,
), followed by 6 hex characters. Each character can be hex or wildcard. Multiple interfaces can be used one after another, without spaces. The order is significant but should it be (?). Alternatively, * can be used to accept all interfaces in any number.required
: The device is required to start the frontend-vm
and will be automatically attached. Requires specifying frontend-vm
and backend-vm
by explicit VM name.auto
: The device will be automatically attached if available at the start of frontend-vm
or if plugged in later. It can be detached and reattached at any time. Requires specifying frontend-vm
and backend-vm
by explicit VM name.allow
(default): The device can be manually attached to the frontend-vm
at any time when it is running.deny
: The device cannot be attached to this frontend-vm
.Possible attack: A device can pose as a trusted device and be set to auto-attach to a sensitive VM.
ident = *
is not used for sensitive VMs.Additionally, device interfaces are checked, so auto-attach limited to mass data could be even safer than manual configuration. When a user plugs in a pendrive with a keyboard interface, it will not be automatically attached, whereas an unaware user might miss this and manually attach the device.
This proposal still introduces some risk, but less than using a USB keyboard.
my-vm sys-usb usb 1-1.1 *:*:*:u08* auto
my-vm * * * *:*:*:u03* deny
This policy automatically attaches only devices declaring mass data type to my-vm
, but not multimedia devices. It will not allow attaching input devices like keyboards/mice. In the current model, if the user mixes up ports, which is likely, auto-attach has no more safeguards.
Using automation is practically safer than relying on manual configuration. For applications based on QubesOS, this will allow easy system configuration, increasing user resilience.
I think I like it, although I'm already thinking about how non-simple it's going to be to expose simple GUI config options :D But that's my problem - this looks like good backend.
Proposed solution:
A set of rules as follows:
I think I'm missing some higher level context. Currently device assignments are stored in DeviceAssignment
objects, attached to appropriate QubesVm objects (related to the frontent qube). How this proposal maps to the current API? Or does it abandon the objects idea completely? If so, how various actions like attach/detach/assign etc are going to work? By editing the file (with some API?) and then backed doing diff and figure out what should change? Or maybe existing methods would edit rules you propose here (how does it map then?). For example, if you add an "auto" rule for device that is already present in the system (and frontend qube is running), should it automatically get attached immediately?
The proposed idea for rules (especially actions and identity properties) as a concept looks fine, but there should be some connection to the current model. And here, I'm not sure if the idea of a single flat table as the user-facing (or even an API) approach is a good one. We'll run into various issues regarding presenting this to users, rules ordering (especially when changed both by the users manually, and using some CLI/GUI tools) etc. Maybe a better approach would be to still consider this in terms of objects attached to appropriate qube (either frontend or backend, may depend on the rule?) possibly at the cost of some flexibility. But then, we may have cases where not having explicit rules order will be a problem, especially in case of conflicting rules ("allow only specific device and deny all others" or "deny specific class of devices but allow others"). This could be solved with rules (objects attached to VMs) having a priority (a number). That said, it feels like re-introducing weaker version of rules order, compared to the flat table giving full ordering...
To put this all in other words: I think the flat table you propose gives too much flexibility, that will make supposedly simple cases (like "click here to always attach this device to qube X") significantly harder due to potential interaction with other rules. We had this problem before already, twice (firewall, and qrexec policy). And in both cases it ended up with GUI tools more or less giving up if user added any non-default rule manually (in case of firewall, it literally tells you to go to CLI and disables any kind of editing; in case of qrexec, it complains about the custom rules, and depending where you put them, warns you they will get removed if you continue). I'd like to avoid this situation with this API...
Maybe we should start with collecting use cases we want to support? Do you have a list like this already somewhere? If not, here are some ideas (partially based on earlier comments here):
I'm probably missing many cases...
Ok, I see the problem. I agree that reducing the flexibility of the solution would be the best approach.
Currently, when a device is connected, we rely on information from the qubes.xml
file. If we assign the same port to two different VMs and both are running, the device connected to that port will be attached to the VM that appears earlier in app.domains
. This is unintuitive. The problem here is clarity and lack of control. We want the user to easily check in one place what is connected to what, and which VMs cannot have anything attached. Such a table can be generated as read-only for quick viewing, and can also be stored in a file for better performance, which, in principle, should not be manually edited like qubes.xml
. Changes should still be made via qvm-device
.
Let's introduce the following restrictions:
frontend-vm
must be explicitly specified. This will maintain the concept that assignment connects a device/port with a qube. The key difference underneath is the separation of port and device.
The deny
list can be moved to a separate table. This is additional functionality. It will significantly reduce the complexity of the rules. Deny will "override" auto-attach and will not allow the device to be connected if any rule matches. Deny can be bypassed with the --force
flag during attach. For simplicity, we can limit this to interface dependencies (which include devclass
), meaning we may want to prevent devices of a certain type from being connected, e.g., no:
(a) devices at all (this does not apply to PCI devices since they cannot be connected manually anyway),
(b) USB devices,
(c) mass data/HID type devices,
(d) USB mass data type devices,
(e) etc.
I do not see a use case for not connecting specific devices. We can also have interface + port.
allow
can be removed, as it is just the default behavior.
auto-attach
and required
assignments should assign a port or specific device. Auto-attaching based on a device interface or a class need to be discussed.
So, the mechanism would work as follows: After connecting a device, the rule table would be checked line by line, and the first match would be considered. If the VM from this match cannot accept the device because it is turned off or the device is excluded by the denied list, the next rows are checked.
This method might not be super intuitive, but it meets the basic needs:
We need add command to change the rule no.
How this proposal maps to the current API?
Assignment is still tied to the frontend. It should be considered of as a request from frontend-vm to attach a device with specific parameters.
By editing the file (with some API?) and then backed doing diff and figuring out what should change?
We can introduce a new file or keep all this information in qubes.xml
as it is now. Now we have such table hidden in qubes.xml
. The file would be updated by qvm-device
and loaded only during VM startup/device connection.
For example, if you add an "auto" rule for a device that is already present in the system (and frontend qube is running), should it automatically get attached immediately?
No, we will maintain the current behavior.
Modified tables can looks as follows (it is hidden in qubes.xml
for now):
Auto-attach rules:
rule no. frontend-vm backend-vm:port_id device action options
rule no.:
frontend-vm:
backend-vm:port_id:
backend-vm:port_id
or any
.device:
any
action:
auto-attach
or required
.options:
frontend-vm interfaces
frontend-vm:
interfaces:
Interfaces can be easily translated to user-language like: mouse, keyboard, mass-data. From the point of GUI it can be represented as tree, where the user can choose which nodes/branches are allowed or not:
frontend-vm-1
all
|-pci
| |- ...
|-usb
| |-HID
| | |-mouse
| | |-keyborad
| | |-other```
| |- ...
|- ...
frontend-vm-2
all
|- ...
Introduction
Currently, we can make one assignment of a selected port to a single VM in two ways:
identity='any'
, which means that anything plugged into the designated port will be automatically attached to the VM, as it has been functioning thus far.Proposed enhancement
More:
https://github.com/QubesOS/qubes-core-admin/pull/579#pullrequestreview-1936874263
The value to a user, and who that user might be
Increased security (less error-prone manual clicking/typing) and user convenience.
TODO