all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Maximiliano Sandoval <m.sandoval@proxmox.com>
To: pve-devel@lists.proxmox.com
Cc: Wolfgang Bumiller <w.bumiller@proxmox.com>
Subject: [pve-devel] [PATCH qemu-server] config: add system and service credentials support
Date: Wed,  2 Apr 2025 16:36:20 +0200	[thread overview]
Message-ID: <20250402143620.440158-1-m.sandoval@proxmox.com> (raw)

Allows to pass system and service credentials to a VM. See [1] for a
description of credentials. This can be potentially used to provision a
VM as per [2]. Values can be passed either as plain text or as a base64
encoded string when the base64 flag is set.

A VM configuration file which, for example, contains:

    systemd-cred0: name=foo,value=bar
    systemd-cred1: name=encoded-foo,value=YmFy,base64=1

will have the following arguments added to its kvm command:

    -smbios 'type=11,value=io.systemd.credential:foo=bar' \
    -smbios 'type=11,value=io.systemd.credential.binary:encoded-foo=YmFy'

On the guest these credentials can be read via:

    dmidecode -t 11

via:

    systemd-creds --system list
    systemd-creds --system cat $CREDENTIAL_NAME

or at:

   /run/credentials/@system/$CREDENTIAL_NAME

[1] https://systemd.io/CREDENTIALS/
[2] https://www.freedesktop.org/software/systemd/man/latest/systemd.system-credentials.html

Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
---

Differences from RFC:
 - Fixed multiple points from the feedback in the RFC
 - Uses "systemd-cred$i" instead of "systemd-credential.$name" as a name
 - The snipped support was dropped
 - More strict format required on credential names and values


 PVE/QemuServer.pm | 72 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index ffd5d56b..1f902b8b 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -150,6 +150,31 @@ my $watchdog_fmt = {
 };
 PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
 
+my $MAX_CREDENTIALS = 16;
+
+my $systemd_cred_fmt = {
+    value => {
+	description => 'The credential\'s value. This is a Base64 encoded string.'
+	    .' If the value should contain arbitrary binary data,'
+	    .' then the value can be a Base64 encoded string and the base64=1 flag should be set.',
+	type => 'string',
+	pattern => '[A-Za-z0-9+\/]+={0,2}',
+	typetext => '<string>',
+    },
+    name => {
+	description => 'The credential\'s name. This is a short ASCII string suitable as filename in the filesystem',
+	type => 'string',
+	pattern => '[A-Za-z0-9\-\._]+',
+	typetext => '<string>',
+    },
+    base64 => {
+	description => 'Whether the credential\'s value is base64 encoded.',
+	type => 'boolean',
+	optional => 1,
+	default => 0,
+    },
+};
+
 my $agent_fmt = {
     enabled => {
 	description => "Enable/disable communication with a QEMU Guest Agent (QGA) running in the VM.",
@@ -946,6 +971,13 @@ my $netdesc = {
     description => "Specify network devices.",
 };
 
+my $systemd_cred_desc = {
+    optional => 1,
+    type => 'string',
+    format => $systemd_cred_fmt,
+    description => 'Specify system and service credentials.',
+};
+
 PVE::JSONSchema::register_standard_option("pve-qm-net", $netdesc);
 
 my $ipconfig_fmt = {
@@ -1007,6 +1039,10 @@ for (my $i = 0; $i < $MAX_NETS; $i++)  {
     $confdesc_cloudinit->{"ipconfig$i"} = $ipconfigdesc;
 }
 
+for (my $i = 0; $i < $MAX_CREDENTIALS; $i++)  {
+    $confdesc->{"systemd-cred$i"} = $systemd_cred_desc;
+}
+
 foreach my $key (keys %$confdesc_cloudinit) {
     $confdesc->{$key} = $confdesc_cloudinit->{$key};
 }
@@ -1955,6 +1991,16 @@ sub parse_guest_agent {
     return $res;
 }
 
+sub parse_systemd_credential {
+    my ($value) = @_;
+
+    return {} if !$value;
+
+    my $res = eval { parse_property_string($systemd_cred_fmt, $value) };
+    warn $@ if $@;
+    return $res;
+}
+
 sub get_qga_key {
     my ($conf, $key) = @_;
     return undef if !defined($conf->{agent});
@@ -3383,6 +3429,17 @@ my sub get_vga_properties {
     return ($vga, $qxlnum);
 }
 
+sub smbios_11_cred_arg {
+    my ($name, $value, $is_encoded) = @_;
+
+    if ($is_encoded) {
+	die "value $value is not base64 encoded\n" if $value !~ /^[A-Za-z0-9+\/]+={0,2}$/;
+	return ('-smbios', "type=11,value=io.systemd.credential.binary:$name=$value");
+    } else {
+	return ('-smbios', "type=11,value=io.systemd.credential:$name=$value");
+    }
+}
+
 sub config_to_command {
     my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
         $live_restore_backing) = @_;
@@ -4015,6 +4072,21 @@ sub config_to_command {
 	push @$cmd, '-snapshot';
     }
 
+    for (my $i = 0; $i < $MAX_CREDENTIALS; $i++)  {
+	my $cred_name = "systemd-cred$i";
+
+	next if !$conf->{$cred_name};
+
+	my $opts = parse_systemd_credential($conf->{$cred_name});
+	my $name = $opts->{'name'};
+	my $is_encoded = $opts->{'base64'};
+	my $value = $opts->{'value'};
+
+	if ($value && $name) {
+	    push @$cmd, smbios_11_cred_arg($name, $value, $is_encoded);
+	}
+    }
+
     # add custom args
     if ($conf->{args}) {
 	my $aa = PVE::Tools::split_args($conf->{args});
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


             reply	other threads:[~2025-04-02 14:36 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-02 14:36 Maximiliano Sandoval [this message]
2025-04-03  7:49 ` Thomas Lamprecht
2025-04-03  7:56   ` Thomas Lamprecht
2025-04-03  8:48     ` Maximiliano Sandoval
2025-04-03  8:34   ` Maximiliano Sandoval
2025-04-03  9:04     ` Thomas Lamprecht
2025-04-03  9:42       ` Lukas Wagner

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=20250402143620.440158-1-m.sandoval@proxmox.com \
    --to=m.sandoval@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    --cc=w.bumiller@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal