QubesOS / qubes-issues

The Qubes OS Project issue tracker
https://www.qubes-os.org/doc/issue-tracking/
534 stars 46 forks source link

Device "self-identity oriented" assignment #9325

Open piotrbartman opened 3 months ago

piotrbartman commented 3 months ago

Introduction

Currently, we can make one assignment of a selected port to a single VM in two ways:

  1. Using 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.
  2. Using the identity of the currently plugged device, in which case automatic attachment will only occur if the device presents itself in the same way.

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

rocodes commented 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!

marmarta commented 3 months ago

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.

piotrbartman commented 3 months ago

Proposal: Device Assignment Policy

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

Allowed Values::

*`-vm`**:

devclass:

port_id:

device (self_identity):

action:

Pros:

Security Level Comparison to the Current Level:

Possible attack: A device can pose as a trusted device and be set to auto-attach to a sensitive VM.

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.

More granural Security Control:

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.

marmarta commented 3 months ago

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.

marmarek commented 3 months ago

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):

  1. Deny attaching any device to particular qube
  2. Automatically attach a specific device plugged into a specific port to a specific qube
  3. Same as 2, but refuse to start a qube if device is not available in the system
  4. Allow attaching only device of a specific class to a qube, deny others
  5. Allow attaching only specific device to a qube, but don't do that automatically (might be useful if you have 2+ qubes with a rule for such device).
  6. Allow attaching a device to a specific qube only if it's plugged into a specific port
  7. Same as 6 but with auto-attach (not sure about this one...)

I'm probably missing many cases...

piotrbartman commented 2 months ago

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:

  1. 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.

  2. 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.

  3. allow can be removed, as it is just the default behavior.

  4. 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.

Algorithm

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.

Tables

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:

device:

action:

options:

Device attachemnt restriction:

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
    |- ...