From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 71E221FF165 for ; Thu, 17 Jul 2025 15:37:05 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 72F183B207; Thu, 17 Jul 2025 15:37:49 +0200 (CEST) From: Fiona Ebner To: pve-devel@lists.proxmox.com Date: Thu, 17 Jul 2025 15:36:51 +0200 Message-ID: <20250717133711.84715-4-f.ebner@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250717133711.84715-1-f.ebner@proxmox.com> References: <20250717133711.84715-1-f.ebner@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.028 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [agent.pm] Subject: [pve-devel] [PATCH qemu-server 3/9] api: agent: use more specific guest agent privileges 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: , Reply-To: Proxmox VE development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" Make the 'perms' property in the command schema required. Replace the misleadingly named 'VM.Monitor' privilege with more precise guest-agent-specific privileges and, for power managment, the pre-existing 'VM.PowerMgmt' privilege. There is a basic VM.GuestAgent.Audit privilege for read-only, informational commands. There are dedicated privileges VM.GuestAgent.File{Read,Write} for the file-{read,write} commands. There is a separate VM.GuestAgent.FileSystemMgmt privilege for filesystem freeze, thaw and trim. Querying the filesystem freeze status is also allowed with VM.GuestAgent.Audit. The VM.GuestAgent.Unrestricted privilege is allowed to do all guest agent operations, in particular also execution of arbitrary commands with guest-exec. Querying the result of the guest-exec command via guest-exec-status is read-only, but it only makes sense in combination with guest-exec, so it also requires VM.GuestAgent.Unrestricted. Signed-off-by: Fiona Ebner --- src/PVE/API2/Qemu/Agent.pm | 66 ++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/src/PVE/API2/Qemu/Agent.pm b/src/PVE/API2/Qemu/Agent.pm index 8a9b9264..05ef4f50 100644 --- a/src/PVE/API2/Qemu/Agent.pm +++ b/src/PVE/API2/Qemu/Agent.pm @@ -18,68 +18,99 @@ my $MAX_READ_SIZE = 16 * 1024 * 1024; # 16 MiB # list of commands # will generate one api endpoint per command -# needs a 'method' property and optionally a 'perms' property (default VM.Monitor) +# needs a 'method' property and a 'perms' property my $guest_agent_commands = { 'ping' => { method => 'POST', + perms => 'VM.GuestAgent.Audit', }, 'get-time' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'info' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'fsfreeze-status' => { method => 'POST', + perms => { + check => [ + 'perm', + '/vms/{vmid}', + [ + 'VM.GuestAgent.Audit', + 'VM.GuestAgent.FileSystemMgmt', + 'VM.GuestAgent.Unrestricted', + ], + any => 1, + ], + }, }, 'fsfreeze-freeze' => { method => 'POST', + perms => 'VM.GuestAgent.FileSystemMgmt', }, 'fsfreeze-thaw' => { method => 'POST', + perms => 'VM.GuestAgent.FileSystemMgmt', }, 'fstrim' => { method => 'POST', + perms => 'VM.GuestAgent.FileSystemMgmt', }, 'network-get-interfaces' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-vcpus' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-fsinfo' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-memory-blocks' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-memory-block-info' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'suspend-hybrid' => { method => 'POST', + perms => 'VM.PowerMgmt', }, 'suspend-ram' => { method => 'POST', + perms => 'VM.PowerMgmt', }, 'suspend-disk' => { method => 'POST', + perms => 'VM.PowerMgmt', }, 'shutdown' => { method => 'POST', + perms => 'VM.PowerMgmt', }, # added since qemu 2.9 'get-host-name' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-osinfo' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-users' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, 'get-timezone' => { method => 'GET', + perms => 'VM.GuestAgent.Audit', }, }; @@ -144,8 +175,11 @@ sub register_command { if (ref($perm) eq 'HASH') { $permission = $perm; } else { - $perm //= 'VM.Monitor'; - $permission = { check => ['perm', '/vms/{vmid}', [$perm]] }; + die "internal error: missing permission for $command" if !$perm; + + $permission = { + check => ['perm', '/vms/{vmid}', [$perm, 'VM.GuestAgent.Unrestricted'], any => 1], + }; } my $parameters = { @@ -206,7 +240,7 @@ sub register_command { } # old {vmid}/agent POST endpoint, here for compatibility -__PACKAGE__->register_command('', 'POST'); +__PACKAGE__->register_command('', 'POST', 'VM.GuestAgent.Unrestricted'); for my $cmd (sort keys %$guest_agent_commands) { my $props = $guest_agent_commands->{$cmd}; @@ -221,7 +255,7 @@ __PACKAGE__->register_method({ protected => 1, proxyto => 'node', description => "Sets the password for the given user to the given password", - permissions => { check => ['perm', '/vms/{vmid}', ['VM.Monitor']] }, + permissions => { check => ['perm', '/vms/{vmid}', ['VM.GuestAgent.Unrestricted']] }, parameters => { additionalProperties => 0, properties => { @@ -280,7 +314,7 @@ __PACKAGE__->register_method({ proxyto => 'node', description => "Executes the given command in the vm via the guest-agent and returns an object with the pid.", - permissions => { check => ['perm', '/vms/{vmid}', ['VM.Monitor']] }, + permissions => { check => ['perm', '/vms/{vmid}', ['VM.GuestAgent.Unrestricted']] }, parameters => { additionalProperties => 0, properties => { @@ -335,7 +369,7 @@ __PACKAGE__->register_method({ protected => 1, proxyto => 'node', description => "Gets the status of the given pid started by the guest-agent", - permissions => { check => ['perm', '/vms/{vmid}', ['VM.Monitor']] }, + permissions => { check => ['perm', '/vms/{vmid}', ['VM.GuestAgent.Unrestricted']] }, parameters => { additionalProperties => 0, properties => { @@ -411,7 +445,14 @@ __PACKAGE__->register_method({ protected => 1, proxyto => 'node', description => "Reads the given file via guest agent. Is limited to $MAX_READ_SIZE bytes.", - permissions => { check => ['perm', '/vms/{vmid}', ['VM.Monitor']] }, + permissions => { + check => [ + 'perm', + '/vms/{vmid}', + ['VM.GuestAgent.FileRead', 'VM.GuestAgent.Unrestricted'], + any => 1, + ], + }, parameters => { additionalProperties => 0, properties => { @@ -490,7 +531,14 @@ __PACKAGE__->register_method({ protected => 1, proxyto => 'node', description => "Writes the given file via guest agent.", - permissions => { check => ['perm', '/vms/{vmid}', ['VM.Monitor']] }, + permissions => { + check => [ + 'perm', + '/vms/{vmid}', + ['VM.GuestAgent.FileWrite', 'VM.GuestAgent.Unrestricted'], + any => 1, + ], + }, parameters => { additionalProperties => 0, properties => { -- 2.47.2 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel