From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 7EE9D92289 for ; Mon, 3 Oct 2022 15:23:12 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 6024931E37 for ; Mon, 3 Oct 2022 15:22:42 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [94.136.29.106]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Mon, 3 Oct 2022 15:22:38 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B2FF144713 for ; Mon, 3 Oct 2022 15:22:37 +0200 (CEST) Date: Mon, 03 Oct 2022 15:22:29 +0200 From: Fabian =?iso-8859-1?q?Gr=FCnbichler?= To: Proxmox VE development discussion References: <20220928125059.1139296-1-f.gruenbichler@proxmox.com> <20220928125059.1139296-9-f.gruenbichler@proxmox.com> In-Reply-To: <20220928125059.1139296-9-f.gruenbichler@proxmox.com> MIME-Version: 1.0 User-Agent: astroid/0.16.0 (https://github.com/astroidmail/astroid) Message-Id: <1664802982.cf18svi746.astroid@nora.none> Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-SPAM-LEVEL: Spam detection results: 0 AWL -0.007 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: Re: [pve-devel] [PATCH FOLLOW-UP v6 qemu-server 2/6] mtunnel: add API endpoints X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 03 Oct 2022 13:23:12 -0000 as reported by Stefan Hantreich, the following follow-up should be=20 squashed into this patch if applied: ----8<---- diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 57083601..4da37678 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -5581,7 +5581,7 @@ __PACKAGE__->register_method({ print "received command '$cmd'\n"; eval { if ($cmd_desc->{$cmd}) { - PVE::JSONSchema::validate($cmd_desc->{$cmd}, $parsed); + PVE::JSONSchema::validate($parsed, $cmd_desc->{$cmd}); } else { $parsed =3D {}; } ---->8---- On September 28, 2022 2:50 pm, Fabian Gr=C3=BCnbichler wrote: > the following two endpoints are used for migration on the remote side >=20 > POST /nodes/NODE/qemu/VMID/mtunnel >=20 > which creates and locks an empty VM config, and spawns the main qmtunnel > worker which binds to a VM-specific UNIX socket. >=20 > this worker handles JSON-encoded migration commands coming in via this > UNIX socket: > - config (set target VM config) > -- checks permissions for updating config > -- strips pending changes and snapshots > -- sets (optional) firewall config > - disk (allocate disk for NBD migration) > -- checks permission for target storage > -- returns drive string for allocated volume > - disk-import, query-disk-import, bwlimit > -- handled by PVE::StorageTunnel > - start (returning migration info) > - fstrim (via agent) > - ticket (creates a ticket for a WS connection to a specific socket) > - resume > - stop > - nbdstop > - unlock > - quit (+ cleanup) >=20 > this worker serves as a replacement for both 'qm mtunnel' and various > manual calls via SSH. the API call will return a ticket valid for > connecting to the worker's UNIX socket via a websocket connection. >=20 > GET+WebSocket upgrade /nodes/NODE/qemu/VMID/mtunnelwebsocket >=20 > gets called for connecting to a UNIX socket via websocket forwarding, > i.e. once for the main command mtunnel, and once each for the memory > migration and each NBD drive-mirror/storage migration. >=20 > access is guarded by a short-lived ticket binding the authenticated user > to the socket path. such tickets can be requested over the main mtunnel, > which keeps track of socket paths currently used by that > mtunnel/migration instance. >=20 > each command handler should check privileges for the requested action if > necessary. >=20 > both mtunnel and mtunnelwebsocket endpoints are not proxied, the > client/caller is responsible for ensuring the passed 'node' parameter > and the endpoint handling the call are matching. >=20 > Signed-off-by: Fabian Gr=C3=BCnbichler > --- >=20 > Notes: > v6: > - check for Sys.Incoming in mtunnel > - add definedness checks in 'config' command > - switch to vm_running_locally in 'resume' command > - moved $socket_addr closer to usage > v5: > - us vm_running_locally > - move '$socket_addr' declaration closer to usage > v4: > - add timeout to accept() > - move 'bwlimit' to PVE::StorageTunnel and extend it > - mark mtunnel(websocket) as non-proxied, and check $node accordingly > v3: > - handle meta and vmgenid better > - handle failure of 'config' updating > - move 'disk-import' and 'query-disk-import' handlers to pve-guest-co= mmon > - improve tunnel exit by letting client close the connection > - use strict VM config parser > v2: incorporated Fabian Ebner's feedback, mainly: > - use modified nbd alloc helper instead of duplicating > - fix disk cleanup, also cleanup imported disks > - fix firewall-conf vs firewall-config mismatch > =20 > requires > - pve-access-control with tunnel ticket support (already marked in d/= control) > - pve-access-control with Sys.Incoming privilege (not yet applied/bum= ped!) > - pve-http-server with websocket fixes (could be done via breaks? or = bumped in > pve-manager..) >=20 > PVE/API2/Qemu.pm | 527 ++++++++++++++++++++++++++++++++++++++++++++++- > debian/control | 2 +- > 2 files changed, 527 insertions(+), 2 deletions(-) >=20 > diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm > index 3ec31c26..9270ca74 100644 > --- a/PVE/API2/Qemu.pm > +++ b/PVE/API2/Qemu.pm > @@ -4,10 +4,13 @@ use strict; > use warnings; > use Cwd 'abs_path'; > use Net::SSLeay; > -use POSIX; > use IO::Socket::IP; > +use IO::Socket::UNIX; > +use IPC::Open3; > +use JSON; > use URI::Escape; > use Crypt::OpenSSL::Random; > +use Socket qw(SOCK_STREAM); > =20 > use PVE::Cluster qw (cfs_read_file cfs_write_file);; > use PVE::RRD; > @@ -38,6 +41,7 @@ use PVE::VZDump::Plugin; > use PVE::DataCenterConfig; > use PVE::SSHInfo; > use PVE::Replication; > +use PVE::StorageTunnel; > =20 > BEGIN { > if (!$ENV{PVE_GENERATING_DOCS}) { > @@ -1087,6 +1091,7 @@ __PACKAGE__->register_method({ > { subdir =3D> 'spiceproxy' }, > { subdir =3D> 'sendkey' }, > { subdir =3D> 'firewall' }, > + { subdir =3D> 'mtunnel' }, > ]; > =20 > return $res; > @@ -4965,4 +4970,524 @@ __PACKAGE__->register_method({ > return PVE::QemuServer::Cloudinit::dump_cloudinit_config($conf, $param-= >{vmid}, $param->{type}); > }}); > =20 > +__PACKAGE__->register_method({ > + name =3D> 'mtunnel', > + path =3D> '{vmid}/mtunnel', > + method =3D> 'POST', > + protected =3D> 1, > + description =3D> 'Migration tunnel endpoint - only for internal use = by VM migration.', > + permissions =3D> { > + check =3D> > + [ 'and', > + ['perm', '/vms/{vmid}', [ 'VM.Allocate' ]], > + ['perm', '/', [ 'Sys.Incoming' ]], > + ], > + description =3D> "You need 'VM.Allocate' permissions on '/vms/{vmid}' a= nd Sys.Incoming" . > + " on '/'. Further permission checks happen during the ac= tual migration.", > + }, > + parameters =3D> { > + additionalProperties =3D> 0, > + properties =3D> { > + node =3D> get_standard_option('pve-node'), > + vmid =3D> get_standard_option('pve-vmid'), > + storages =3D> { > + type =3D> 'string', > + format =3D> 'pve-storage-id-list', > + optional =3D> 1, > + description =3D> 'List of storages to check permission and availabilit= y. Will be checked again for all actually used storages during migration.', > + }, > + }, > + }, > + returns =3D> { > + additionalProperties =3D> 0, > + properties =3D> { > + upid =3D> { type =3D> 'string' }, > + ticket =3D> { type =3D> 'string' }, > + socket =3D> { type =3D> 'string' }, > + }, > + }, > + code =3D> sub { > + my ($param) =3D @_; > + > + my $rpcenv =3D PVE::RPCEnvironment::get(); > + my $authuser =3D $rpcenv->get_user(); > + > + my $node =3D extract_param($param, 'node'); > + my $vmid =3D extract_param($param, 'vmid'); > + > + my $storages =3D extract_param($param, 'storages'); > + > + my $nodename =3D PVE::INotify::nodename(); > + > + raise_param_exc({ node =3D> "node needs to be 'localhost' or local host= name '$nodename'" }) > + if $node ne 'localhost' && $node ne $nodename; > + > + $node =3D $nodename; > + > + my $storecfg =3D PVE::Storage::config(); > + foreach my $storeid (PVE::Tools::split_list($storages)) { > + $check_storage_access_migrate->($rpcenv, $authuser, $storecfg, $sto= reid, $node); > + } > + > + PVE::Cluster::check_cfs_quorum(); > + > + my $lock =3D 'create'; > + eval { PVE::QemuConfig->create_and_lock_config($vmid, 0, $lock); }; > + > + raise_param_exc({ vmid =3D> "unable to create empty VM config - $@"}) > + if $@; > + > + my $realcmd =3D sub { > + my $state =3D { > + storecfg =3D> PVE::Storage::config(), > + lock =3D> $lock, > + vmid =3D> $vmid, > + }; > + > + my $run_locked =3D sub { > + my ($code, $params) =3D @_; > + return PVE::QemuConfig->lock_config($state->{vmid}, sub { > + my $conf =3D PVE::QemuConfig->load_config($state->{vmid}); > + > + $state->{conf} =3D $conf; > + > + die "Encountered wrong lock - aborting mtunnel command handling.\n= " > + if $state->{lock} && !PVE::QemuConfig->has_lock($conf, $state->{lock}= ); > + > + return $code->($params); > + }); > + }; > + > + my $cmd_desc =3D { > + config =3D> { > + conf =3D> { > + type =3D> 'string', > + description =3D> 'Full VM config, adapted for target cluster/node', > + }, > + 'firewall-config' =3D> { > + type =3D> 'string', > + description =3D> 'VM firewall config', > + optional =3D> 1, > + }, > + }, > + disk =3D> { > + format =3D> PVE::JSONSchema::get_standard_option('pve-qm-image-for= mat'), > + storage =3D> { > + type =3D> 'string', > + format =3D> 'pve-storage-id', > + }, > + drive =3D> { > + type =3D> 'object', > + description =3D> 'parsed drive information without volid and format', > + }, > + }, > + start =3D> { > + start_params =3D> { > + type =3D> 'object', > + description =3D> 'params passed to vm_start_nolock', > + }, > + migrate_opts =3D> { > + type =3D> 'object', > + description =3D> 'migrate_opts passed to vm_start_nolock', > + }, > + }, > + ticket =3D> { > + path =3D> { > + type =3D> 'string', > + description =3D> 'socket path for which the ticket should be valid. m= ust be known to current mtunnel instance.', > + }, > + }, > + quit =3D> { > + cleanup =3D> { > + type =3D> 'boolean', > + description =3D> 'remove VM config and disks, aborting migration', > + default =3D> 0, > + }, > + }, > + 'disk-import' =3D> $PVE::StorageTunnel::cmd_schema->{'disk-import'}, > + 'query-disk-import' =3D> $PVE::StorageTunnel::cmd_schema->{'query-disk= -import'}, > + bwlimit =3D> $PVE::StorageTunnel::cmd_schema->{bwlimit}, > + }; > + > + my $cmd_handlers =3D { > + 'version' =3D> sub { > + # compared against other end's version > + # bump/reset for breaking changes > + # bump/bump for opt-in changes > + return { > + api =3D> 2, > + age =3D> 0, > + }; > + }, > + 'config' =3D> sub { > + my ($params) =3D @_; > + > + # parse and write out VM FW config if given > + if (my $fw_conf =3D $params->{'firewall-config'}) { > + my ($path, $fh) =3D PVE::Tools::tempfile_contents($fw_conf, 700); > + > + my $empty_conf =3D { > + rules =3D> [], > + options =3D> {}, > + aliases =3D> {}, > + ipset =3D> {} , > + ipset_comments =3D> {}, > + }; > + my $cluster_fw_conf =3D PVE::Firewall::load_clusterfw_conf(); > + > + # TODO: add flag for strict parsing? > + # TODO: add import sub that does all this given raw content? > + my $vmfw_conf =3D PVE::Firewall::generic_fw_config_parser($path, $clu= ster_fw_conf, $empty_conf, 'vm'); > + $vmfw_conf->{vmid} =3D $state->{vmid}; > + PVE::Firewall::save_vmfw_conf($state->{vmid}, $vmfw_conf); > + > + $state->{cleanup}->{fw} =3D 1; > + } > + > + my $conf_fn =3D "incoming/qemu-server/$state->{vmid}.conf"; > + my $new_conf =3D PVE::QemuServer::parse_vm_config($conf_fn, $param= s->{conf}, 1); > + delete $new_conf->{lock}; > + delete $new_conf->{digest}; > + > + # TODO handle properly? > + delete $new_conf->{snapshots}; > + delete $new_conf->{parent}; > + delete $new_conf->{pending}; > + > + # not handled by update_vm_api > + my $vmgenid =3D delete $new_conf->{vmgenid}; > + my $meta =3D delete $new_conf->{meta}; > + > + $new_conf->{vmid} =3D $state->{vmid}; > + $new_conf->{node} =3D $node; > + > + PVE::QemuConfig->remove_lock($state->{vmid}, 'create'); > + > + eval { > + $update_vm_api->($new_conf, 1); > + }; > + if (my $err =3D $@) { > + # revert to locked previous config > + my $conf =3D PVE::QemuConfig->load_config($state->{vmid}); > + $conf->{lock} =3D 'create'; > + PVE::QemuConfig->write_config($state->{vmid}, $conf); > + > + die $err; > + } > + > + my $conf =3D PVE::QemuConfig->load_config($state->{vmid}); > + $conf->{lock} =3D 'migrate'; > + $conf->{vmgenid} =3D $vmgenid if defined($vmgenid); > + $conf->{meta} =3D $meta if defined($meta); > + PVE::QemuConfig->write_config($state->{vmid}, $conf); > + > + $state->{lock} =3D 'migrate'; > + > + return; > + }, > + 'bwlimit' =3D> sub { > + my ($params) =3D @_; > + return PVE::StorageTunnel::handle_bwlimit($params); > + }, > + 'disk' =3D> sub { > + my ($params) =3D @_; > + > + my $format =3D $params->{format}; > + my $storeid =3D $params->{storage}; > + my $drive =3D $params->{drive}; > + > + $check_storage_access_migrate->($rpcenv, $authuser, $state->{store= cfg}, $storeid, $node); > + > + my $storagemap =3D { > + default =3D> $storeid, > + }; > + > + my $source_volumes =3D { > + 'disk' =3D> [ > + undef, > + $storeid, > + undef, > + $drive, > + 0, > + $format, > + ], > + }; > + > + my $res =3D PVE::QemuServer::vm_migrate_alloc_nbd_disks($state->{s= torecfg}, $state->{vmid}, $source_volumes, $storagemap); > + if (defined($res->{disk})) { > + $state->{cleanup}->{volumes}->{$res->{disk}->{volid}} =3D 1; > + return $res->{disk}; > + } else { > + die "failed to allocate NBD disk..\n"; > + } > + }, > + 'disk-import' =3D> sub { > + my ($params) =3D @_; > + > + $check_storage_access_migrate->( > + $rpcenv, > + $authuser, > + $state->{storecfg}, > + $params->{storage}, > + $node > + ); > + > + $params->{unix} =3D "/run/qemu-server/$state->{vmid}.storage"; > + > + return PVE::StorageTunnel::handle_disk_import($state, $params); > + }, > + 'query-disk-import' =3D> sub { > + my ($params) =3D @_; > + > + return PVE::StorageTunnel::handle_query_disk_import($state, $param= s); > + }, > + 'start' =3D> sub { > + my ($params) =3D @_; > + > + my $info =3D PVE::QemuServer::vm_start_nolock( > + $state->{storecfg}, > + $state->{vmid}, > + $state->{conf}, > + $params->{start_params}, > + $params->{migrate_opts}, > + ); > + > + > + if ($info->{migrate}->{proto} ne 'unix') { > + PVE::QemuServer::vm_stop(undef, $state->{vmid}, 1, 1); > + die "migration over non-UNIX sockets not possible\n"; > + } > + > + my $socket =3D $info->{migrate}->{addr}; > + chown $state->{socket_uid}, -1, $socket; > + $state->{sockets}->{$socket} =3D 1; > + > + my $unix_sockets =3D $info->{migrate}->{unix_sockets}; > + foreach my $socket (@$unix_sockets) { > + chown $state->{socket_uid}, -1, $socket; > + $state->{sockets}->{$socket} =3D 1; > + } > + return $info; > + }, > + 'fstrim' =3D> sub { > + if (PVE::QemuServer::qga_check_running($state->{vmid})) { > + eval { mon_cmd($state->{vmid}, "guest-fstrim") }; > + warn "fstrim failed: $@\n" if $@; > + } > + return; > + }, > + 'stop' =3D> sub { > + PVE::QemuServer::vm_stop(undef, $state->{vmid}, 1, 1); > + return; > + }, > + 'nbdstop' =3D> sub { > + PVE::QemuServer::nbd_stop($state->{vmid}); > + return; > + }, > + 'resume' =3D> sub { > + if (PVE::QemuServer::Helpers::vm_running_locally($state->{vmid})) = { > + PVE::QemuServer::vm_resume($state->{vmid}, 1, 1); > + } else { > + die "VM $state->{vmid} not running\n"; > + } > + return; > + }, > + 'unlock' =3D> sub { > + PVE::QemuConfig->remove_lock($state->{vmid}, $state->{lock}); > + delete $state->{lock}; > + return; > + }, > + 'ticket' =3D> sub { > + my ($params) =3D @_; > + > + my $path =3D $params->{path}; > + > + die "Not allowed to generate ticket for unknown socket '$path'\n" > + if !defined($state->{sockets}->{$path}); > + > + return { ticket =3D> PVE::AccessControl::assemble_tunnel_ticket($a= uthuser, "/socket/$path") }; > + }, > + 'quit' =3D> sub { > + my ($params) =3D @_; > + > + if ($params->{cleanup}) { > + if ($state->{cleanup}->{fw}) { > + PVE::Firewall::remove_vmfw_conf($state->{vmid}); > + } > + > + for my $volid (keys $state->{cleanup}->{volumes}->%*) { > + print "freeing volume '$volid' as part of cleanup\n"; > + eval { PVE::Storage::vdisk_free($state->{storecfg}, $volid) }; > + warn $@ if $@; > + } > + > + PVE::QemuServer::destroy_vm($state->{storecfg}, $state->{vmid}, 1); > + } > + > + print "switching to exit-mode, waiting for client to disconnect\n"= ; > + $state->{exit} =3D 1; > + return; > + }, > + }; > + > + $run_locked->(sub { > + my $socket_addr =3D "/run/qemu-server/$state->{vmid}.mtunnel"; > + unlink $socket_addr; > + > + $state->{socket} =3D IO::Socket::UNIX->new( > + Type =3D> SOCK_STREAM(), > + Local =3D> $socket_addr, > + Listen =3D> 1, > + ); > + > + $state->{socket_uid} =3D getpwnam('www-data') > + or die "Failed to resolve user 'www-data' to numeric UID\n"; > + chown $state->{socket_uid}, -1, $socket_addr; > + }); > + > + print "mtunnel started\n"; > + > + my $conn =3D eval { PVE::Tools::run_with_timeout(300, sub { $state-= >{socket}->accept() }) }; > + if ($@) { > + warn "Failed to accept tunnel connection - $@\n"; > + > + warn "Removing tunnel socket..\n"; > + unlink $state->{socket}; > + > + warn "Removing temporary VM config..\n"; > + $run_locked->(sub { > + PVE::QemuServer::destroy_vm($state->{storecfg}, $state->{vmid}, 1)= ; > + }); > + > + die "Exiting mtunnel\n"; > + } > + > + $state->{conn} =3D $conn; > + > + my $reply_err =3D sub { > + my ($msg) =3D @_; > + > + my $reply =3D JSON::encode_json({ > + success =3D> JSON::false, > + msg =3D> $msg, > + }); > + $conn->print("$reply\n"); > + $conn->flush(); > + }; > + > + my $reply_ok =3D sub { > + my ($res) =3D @_; > + > + $res->{success} =3D JSON::true; > + my $reply =3D JSON::encode_json($res); > + $conn->print("$reply\n"); > + $conn->flush(); > + }; > + > + while (my $line =3D <$conn>) { > + chomp $line; > + > + # untaint, we validate below if needed > + ($line) =3D $line =3D~ /^(.*)$/; > + my $parsed =3D eval { JSON::decode_json($line) }; > + if ($@) { > + $reply_err->("failed to parse command - $@"); > + next; > + } > + > + my $cmd =3D delete $parsed->{cmd}; > + if (!defined($cmd)) { > + $reply_err->("'cmd' missing"); > + } elsif ($state->{exit}) { > + $reply_err->("tunnel is in exit-mode, processing '$cmd' cmd not po= ssible"); > + next; > + } elsif (my $handler =3D $cmd_handlers->{$cmd}) { > + print "received command '$cmd'\n"; > + eval { > + if ($cmd_desc->{$cmd}) { > + PVE::JSONSchema::validate($cmd_desc->{$cmd}, $parsed); > + } else { > + $parsed =3D {}; > + } > + my $res =3D $run_locked->($handler, $parsed); > + $reply_ok->($res); > + }; > + $reply_err->("failed to handle '$cmd' command - $@") > + if $@; > + } else { > + $reply_err->("unknown command '$cmd' given"); > + } > + } > + > + if ($state->{exit}) { > + print "mtunnel exited\n"; > + } else { > + die "mtunnel exited unexpectedly\n"; > + } > + }; > + > + my $socket_addr =3D "/run/qemu-server/$vmid.mtunnel"; > + my $ticket =3D PVE::AccessControl::assemble_tunnel_ticket($authuser, "/= socket/$socket_addr"); > + my $upid =3D $rpcenv->fork_worker('qmtunnel', $vmid, $authuser, $realcm= d); > + > + return { > + ticket =3D> $ticket, > + upid =3D> $upid, > + socket =3D> $socket_addr, > + }; > + }}); > + > +__PACKAGE__->register_method({ > + name =3D> 'mtunnelwebsocket', > + path =3D> '{vmid}/mtunnelwebsocket', > + method =3D> 'GET', > + permissions =3D> { > + description =3D> "You need to pass a ticket valid for the selected sock= et. Tickets can be created via the mtunnel API call, which will check permi= ssions accordingly.", > + user =3D> 'all', # check inside > + }, > + description =3D> 'Migration tunnel endpoint for websocket upgrade - = only for internal use by VM migration.', > + parameters =3D> { > + additionalProperties =3D> 0, > + properties =3D> { > + node =3D> get_standard_option('pve-node'), > + vmid =3D> get_standard_option('pve-vmid'), > + socket =3D> { > + type =3D> "string", > + description =3D> "unix socket to forward to", > + }, > + ticket =3D> { > + type =3D> "string", > + description =3D> "ticket return by initial 'mtunnel' API call, or retr= ieved via 'ticket' tunnel command", > + }, > + }, > + }, > + returns =3D> { > + type =3D> "object", > + properties =3D> { > + port =3D> { type =3D> 'string', optional =3D> 1 }, > + socket =3D> { type =3D> 'string', optional =3D> 1 }, > + }, > + }, > + code =3D> sub { > + my ($param) =3D @_; > + > + my $rpcenv =3D PVE::RPCEnvironment::get(); > + my $authuser =3D $rpcenv->get_user(); > + > + my $nodename =3D PVE::INotify::nodename(); > + my $node =3D extract_param($param, 'node'); > + > + raise_param_exc({ node =3D> "node needs to be 'localhost' or local host= name '$nodename'" }) > + if $node ne 'localhost' && $node ne $nodename; > + > + my $vmid =3D $param->{vmid}; > + # check VM exists > + PVE::QemuConfig->load_config($vmid); > + > + my $socket =3D $param->{socket}; > + PVE::AccessControl::verify_tunnel_ticket($param->{ticket}, $authuser, "= /socket/$socket"); > + > + return { socket =3D> $socket }; > + }}); > + > 1; > diff --git a/debian/control b/debian/control > index a90ecd6f..ce469cbd 100644 > --- a/debian/control > +++ b/debian/control > @@ -33,7 +33,7 @@ Depends: dbus, > libjson-perl, > libjson-xs-perl, > libnet-ssleay-perl, > - libpve-access-control (>=3D 5.0-7), > + libpve-access-control (>=3D 7.0-7), > libpve-cluster-perl, > libpve-common-perl (>=3D 7.1-4), > libpve-guest-common-perl (>=3D 4.1-1), > --=20 > 2.30.2 >=20 >=20 >=20 > _______________________________________________ > pve-devel mailing list > pve-devel@lists.proxmox.com > https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel >=20