all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Maximiliano Sandoval <m.sandoval@proxmox.com>
To: Thomas Lamprecht <t.lamprecht@proxmox.com>
Cc: Wolfgang Bumiller <w.bumiller@proxmox.com>,
	Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Subject: Re: [pve-devel] [PATCH qemu-server] config: add system and service credentials support
Date: Thu, 03 Apr 2025 10:34:47 +0200	[thread overview]
Message-ID: <s8oa58x3a2r.fsf@proxmox.com> (raw)
In-Reply-To: <09b570e5-fce3-4ecc-a898-9400f21d778a@proxmox.com>


Thomas Lamprecht <t.lamprecht@proxmox.com> writes:

> Am 02.04.25 um 16:36 schrieb Maximiliano Sandoval:
>> 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.
>
> Would this also make sense for Containers?

Absolutely, this would be a followup. The mechanism to pass them down to
containers is not via smbios 11 though.

> If it's something we can expose for all guests, we could also (later) look
> into implementing some simple registry (like a mappings type) fulfilling
> what the snippets approach would  provide one while having it nicely
> integrated into our access control system.

As per systemd-exec's man page, in total one can pass up to 1MB in
system credentials. A VM config file is certainly not the vehicle for
such an amount of data and I am also not fully comfortable with putting
potentially sensitive data as plain-text inside config files or the
cluster filesystem. I am not fully sure how to approach this long term.


There is also the more-secure possibility to pass down system
credentials from the host to the guest (e.g. ImportCredential= or
LoadCredential=) but that would have the drawback that there is no
mechanism to sync them acros a cluster.

>> 
>> A VM configuration file which, for example, contains:
>> 
>>     systemd-cred0: name=foo,value=bar
>>     systemd-cred1: name=encoded-foo,value=YmFy,base64=1
>
> Tangentially related: Moving the VM config fully over to section config
> parsing would get us list/array support for free – albeit the move might be
> rather costly..
>
>
>> 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.'
>
> But it isn't if the base64 flag is not set?

I discussed this off-list a bit with Wolfgang and Fabian and I decided
for the time being to only accept the character set of base64, if a user
needs more control, they can encode the string as base64. But yes, the
description should be improved.

An alternative would be to require the string to be either a base64
string or match a hand-picked format, e.g. [a-zA-Z0-9\-\.], but that
would require studying what are the possible use-cases.

>> +	    .' If the value should contain arbitrary binary data,'
>
> nit: why the early line break in the mid of the same sentence?
>
>> +	    .' then the value can be a Base64 encoded string and the base64=1 flag should be set.',
>
> different casing: Base64 here and base64 below for the property
> with the same name.
>
>> +	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\-\._]+',
>
> The dot does not need to be escaped in a [characterset] pattern, and new use-sites
> might prefer using a quoted regex via qr/REGEX/ [perlop-regex-quote], which we
> already use on a few sites and has some small performance benefit and also
> features like having some regex-modifier flags enforced just for that sub
> pattern. I.e. the following should be the same: qr/[a-z0-9_.-]/i – not that
> you need to use exactly that here, just fyi in case you did not know about qr//.
>
> [perlop-regex-quote]: https://perldoc.perl.org/perlop#Regexp-Quote-Like-Operators
>
>> +	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 = {
>
>> @@ -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 $@;
> Should we really just ignore the error and warn here in a generic parser?
> Isn't that the callers job to decide?

Here I just copied what the other parsers in this file do. I am not sure
how to implement something else.

>
>> +    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}$/;
>
> A base64 regex might be good to have in a module wide variable, that then can
> also be used in the JSONSchema format above. Can be here in this module for now.
>
>> +	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}
>
> might be tiny bit nicer if written as:
>
> my $cred = $conf->{"systemd-cred$i"} or next;
>
> ;
>> +
>> +	my $opts = parse_systemd_credential($conf->{$cred_name});
>> +	my $name = $opts->{'name'};
>> +	my $is_encoded = $opts->{'base64'};
>> +	my $value = $opts->{'value'};
>
> I'd avoid those useless intermediate variables, this part is not big enough
> to justify them IMO, just go for direct shell access.
>
>> +
>> +	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}); 



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

  parent reply	other threads:[~2025-04-03  8:48 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-02 14:36 Maximiliano Sandoval
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 [this message]
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=s8oa58x3a2r.fsf@proxmox.com \
    --to=m.sandoval@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    --cc=t.lamprecht@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