public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Dominik Csapak <d.csapak@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH qemu-server v7 2/7] enable cluster mapped USB devices for guests
Date: Fri, 16 Jun 2023 15:05:22 +0200	[thread overview]
Message-ID: <20230616130542.199182-3-d.csapak@proxmox.com> (raw)
In-Reply-To: <20230616130542.199182-1-d.csapak@proxmox.com>

this patch allows configuring usb devices that are mapped via
cluster resource mapping when the user has 'Mapping.Use' on the ACL
path '/mapping/usb/{ID}' (in addition to the usual required vm config
privileges)

for now, this is only valid if there is exactly one mapping for the
host, since we don't track passed through usb devices yet

This now also checks permissions on clone/restore, meaning a
'non-mapped' device can only be cloned/restored as root@pam user.
That is a breaking change.

Refactor the checks for restoring into a sub, so we have central place
where we can add such checks

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
changes from v6:
* adapted to refactor
* deduplicated permission checks
* use seperate 'mapping' property for the mapping instead of reusing 'host'
* added a FIXME comment for the restore permission checks

 PVE/API2/Qemu.pm      | 12 +++++++---
 PVE/QemuServer.pm     | 29 +++++++++++++++++++++--
 PVE/QemuServer/USB.pm | 55 +++++++++++++++++++++++++++++++++----------
 3 files changed, 78 insertions(+), 18 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index c6a4cd2a..78c5a7f6 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -32,6 +32,7 @@ use PVE::QemuServer::Drive;
 use PVE::QemuServer::ImportDisk;
 use PVE::QemuServer::Monitor qw(mon_cmd);
 use PVE::QemuServer::Machine;
+use PVE::QemuServer::USB qw(parse_usb_device);
 use PVE::QemuMigrate;
 use PVE::RPCEnvironment;
 use PVE::AccessControl;
@@ -588,11 +589,15 @@ my sub check_usb_perm {
 
     return 1 if $authuser eq 'root@pam';
 
+    $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
+
     my $device = PVE::JSONSchema::parse_property_string('pve-qm-usb', $value);
-    if ($device->{host} =~ m/^spice$/i) {
-	$rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.HWType']);
-    } else {
+    if ($device->{host} && $device->{host} !~ m/^spice$/i) {
 	die "only root can set '$opt' config for real devices\n";
+    } elsif ($device->{mapping}) {
+	$rpcenv->check_full($authuser, "/mapping/usb/$device->{mapping}", ['Mapping.Use']);
+    } else {
+	die "either 'host' or 'mapping' must be set.\n";
     }
 
     return 1;
@@ -3517,6 +3522,7 @@ __PACKAGE__->register_method({
 	    my $oldconf = $snapname ? $conf->{snapshots}->{$snapname} : $conf;
 
 	    my $sharedvm = &$check_storage_access_clone($rpcenv, $authuser, $storecfg, $oldconf, $storage);
+	    PVE::QemuServer::check_mapping_access($rpcenv, $authuser, $oldconf);
 
 	    PVE::QemuServer::check_bridge_access($rpcenv, $authuser, $oldconf);
 
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 38200fea..9341b1ae 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -6473,6 +6473,31 @@ sub check_bridge_access {
     return 1;
 };
 
+sub check_mapping_access {
+    my ($rpcenv, $user, $conf) = @_;
+
+    for my $opt (keys $conf->%*) {
+	if ($opt =~ m/^usb\d+$/) {
+	    my $device = PVE::JSONSchema::parse_property_string('pve-qm-usb', $conf->{$opt});
+	    if (my $host = $device->{host}) {
+		die "only root can set '$opt' config for real devices\n"
+		    if $host !~ m/^spice$/i && $user ne 'root@pam';
+	    } elsif ($device->{mapping}) {
+		$rpcenv->check_full($user, "/mapping/usb/$device->{mapping}", ['Mapping.Use']);
+	    } else {
+		die "either 'host' or 'mapping' must be set.\n";
+	    }
+       }
+   }
+};
+
+# FIXME: improve checks on restore by checking before actually extracing and
+# merging the new config
+sub check_restore_permissions {
+    my ($rpcenv, $user, $conf) = @_;
+    check_bridge_access($rpcenv, $user, $conf);
+    check_mapping_access($rpcenv, $user, $conf);
+}
 # vzdump restore implementaion
 
 sub tar_archive_read_firstfile {
@@ -7117,7 +7142,7 @@ sub restore_proxmox_backup_archive {
     }
 
     my $new_conf = $restore_merge_config->($conffile, $new_conf_raw, $options->{override_conf});
-    check_bridge_access($rpcenv, $user, $new_conf);
+    check_restore_permissions($rpcenv, $user, $new_conf);
     PVE::QemuConfig->write_config($vmid, $new_conf);
 
     eval { rescan($vmid, 1); };
@@ -7431,7 +7456,7 @@ sub restore_vma_archive {
     }
 
     my $new_conf = $restore_merge_config->($conffile, $new_conf_raw, $opts->{override_conf});
-    check_bridge_access($rpcenv, $user, $new_conf);
+    check_restore_permissions($rpcenv, $user, $new_conf);
     PVE::QemuConfig->write_config($vmid, $new_conf);
 
     eval { rescan($vmid, 1); };
diff --git a/PVE/QemuServer/USB.pm b/PVE/QemuServer/USB.pm
index fe541c1f..49957444 100644
--- a/PVE/QemuServer/USB.pm
+++ b/PVE/QemuServer/USB.pm
@@ -6,6 +6,7 @@ use PVE::QemuServer::PCI qw(print_pci_addr);
 use PVE::QemuServer::Machine;
 use PVE::QemuServer::Helpers qw(min_version windows_version);
 use PVE::JSONSchema;
+use PVE::Mapping::USB;
 use base 'Exporter';
 
 our @EXPORT_OK = qw(
@@ -24,6 +25,7 @@ my $USB_PATH_RE = qr/(\d+)\-(\d+(\.\d+)*)/;
 my $usb_fmt = {
     host => {
 	default_key => 1,
+	optional => 1,
 	type => 'string',
 	pattern => qr/(?:(?:$USB_ID_RE)|(?:$USB_PATH_RE)|[Ss][Pp][Ii][Cc][Ee])/,
 	format_description => 'HOSTUSBDEVICE|spice',
@@ -40,8 +42,18 @@ NOTE: This option allows direct access to host hardware. So it is no longer poss
 machines - use with special care.
 
 The value 'spice' can be used to add a usb redirection devices for spice.
+
+Either this or the 'mapping' key must be set.
 EODESCR
     },
+    mapping => {
+	optional => 1,
+	type => 'string',
+	format_description => 'mapping-id',
+	format => 'pve-configid',
+	description => "The ID of a cluster wide mapping. Either this or the default-key 'host'"
+	    ." must be set.",
+    },
     usb3 => {
 	optional => 1,
 	type => 'boolean',
@@ -63,21 +75,38 @@ our $usbdesc = {
 PVE::JSONSchema::register_standard_option("pve-qm-usb", $usbdesc);
 
 sub parse_usb_device {
-    my ($value) = @_;
+    my ($value, $mapping) = @_;
 
-    return if !$value;
+    return if $value && $mapping; # not a valid configuration
 
     my $res = {};
-    if ($value =~ m/^$USB_ID_RE$/) {
-	$res->{vendorid} = $2;
-	$res->{productid} = $4;
-    } elsif ($value =~ m/^$USB_PATH_RE$/) {
-	$res->{hostbus} = $1;
-	$res->{hostport} = $2;
-    } elsif ($value =~ m/^spice$/i) {
-	$res->{spice} = 1;
-    } else {
-	return;
+    if (defined($value)) {
+	if ($value =~ m/^$USB_ID_RE$/) {
+	    $res->{vendorid} = $2;
+	    $res->{productid} = $4;
+	} elsif ($value =~ m/^$USB_PATH_RE$/) {
+	    $res->{hostbus} = $1;
+	    $res->{hostport} = $2;
+	} elsif ($value =~ m/^spice$/i) {
+	    $res->{spice} = 1;
+	}
+    } elsif (defined($mapping)) {
+	my $devices = PVE::Mapping::USB::find_on_current_node($mapping);
+	die "USB device mapping not found for '$mapping'\n" if !$devices || !scalar($devices->@*);
+	die "More than one USB mapping per host not supported\n" if scalar($devices->@*) > 1;
+	eval {
+	    PVE::Mapping::USB::assert_valid($mapping, $devices->[0]);
+	};
+	if (my $err = $@) {
+	    die "USB Mapping invalid (hardware probably changed): $err\n";
+	}
+	my $device = $devices->[0];
+
+	if ($device->{path}) {
+	    $res = parse_usb_device($device->{path});
+	} else {
+	    $res = parse_usb_device($device->{id});
+	}
     }
 
     return $res;
@@ -199,7 +228,7 @@ sub print_usbdevice_full {
 	$usbdevice .= ",port=$port" if defined($port);
     }
 
-    my $parsed = parse_usb_device($device->{host});
+    my $parsed = parse_usb_device($device->{host}, $device->{mapping});
 
     if (defined($parsed->{vendorid}) && defined($parsed->{productid})) {
 	$usbdevice .= ",vendorid=0x$parsed->{vendorid},productid=0x$parsed->{productid}";
-- 
2.30.2





  parent reply	other threads:[~2023-06-16 13:05 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-16 13:05 [pve-devel] [PATCH qemu-server/manager/docs v7] cluster mapping Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH qemu-server v7 1/7] usb: refactor usb code and move some into USB module Dominik Csapak
2023-06-16 13:05 ` Dominik Csapak [this message]
2023-06-16 13:05 ` [pve-devel] [PATCH qemu-server v7 3/7] enable cluster mapped PCI devices for guests Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH qemu-server v7 4/7] check_local_resources: extend for mapped resources Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH qemu-server v7 5/7] api: migrate preconditions: use new check_local_resources info Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH qemu-server v7 6/7] migration: check for mapped resources Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH qemu-server v7 7/7] add test for mapped pci devices Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 01/14] api: add resource map api endpoints for PCI and USB Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 02/14] ui: parser: add helper for lists of property strings Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 03/14] ui: form/USBSelector: make it more flexible with nodename Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 04/14] ui: form: add PCIMapSelector Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 05/14] ui: form: add USBMapSelector Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 06/14] ui: qemu/PCIEdit: rework panel to add a mapped configuration Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 07/14] ui: qemu/USBEdit: add 'mapped' device case Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 08/14] ui: form: add MultiPCISelector Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 09/14] ui: add edit window for pci mappings Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 10/14] ui: add edit window for usb mappings Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 11/14] ui: add ResourceMapTree Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 12/14] ui: allow configuring pci and usb mapping Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 13/14] ui: window/Migrate: allow mapped devices Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH manager v7 14/14] ui: improve permission handling for hardware Dominik Csapak
2023-06-16 13:05 ` [pve-devel] [PATCH docs v7 1/1] qemu: add documentation about cluster device mapping Dominik Csapak
2023-06-19  7:12   ` [pve-devel] applied: " Thomas Lamprecht
2023-06-16 15:00 ` [pve-devel] [PATCH qemu-server/manager/docs v7] cluster mapping Friedrich Weber
2023-06-19  5:29 ` [pve-devel] partially-applied: " Thomas Lamprecht
2023-06-19  7:28 ` [pve-devel] applied: " Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230616130542.199182-3-d.csapak@proxmox.com \
    --to=d.csapak@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal