From: Maximiliano Sandoval <m.sandoval@proxmox.com>
To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Cc: Wolfgang Bumiller <w.bumiller@proxmox.com>
Subject: Re: [pve-devel] [RFC PATCH 2/2] config: add systemd credentials support
Date: Wed, 02 Apr 2025 16:36:43 +0200 [thread overview]
Message-ID: <s8o8qoid3yi.fsf@proxmox.com> (raw)
In-Reply-To: <1729584916.zq75nvzkam.astroid@yuna.none>
Fabian Grünbichler <f.gruenbichler@proxmox.com> writes:
> On September 24, 2024 4:35 pm, Maximiliano Sandoval wrote:
>> Allows to pass systemd credentials to a VM. See [1] for a description of
>> systemd credentials. This can be potentially used to provision a VM as
>> per [2]. Values can be passed either as plain text (which might be
>> base64 encrypted) or by reading the contents of a snippet.
>>
>> A VM configuration file which, for example, contains:
>>
>> systemd.foo: value=bar
>> systemd.ssh.authorized_keys.root: snippet=local:snippets/id_ed25519.pub
>> systemd.encoded-foo: value=YmFya=,base64=1
>
> why not our usual scheme?
>
> systemd0: key=foo,value=bar
> systemd1: key=ssh.authorized_keys.root,snippet=local:snippets/id_ed25519.pub
> systemd2: key=encoded-foo,value=YmFya=,base64=1
>
> if need be, the key could be persisted base64-encoded (e.g., if it can
> contain characters used as delimiters or otherwise special)
>
>> will have the following arguments added to its kvm command:
>>
>> -smbios 'type=11,value=io.systemd.credential.binary:ssh.authorized_keys.root=c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUZWZkFTYnVHdGdoWXBQQTBUS0w4N3I2dWRYNm5CbEM2L2hLWVZaTTdENzYgZm9vQGJhcgo=' \
>> -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
>>
>> In the example above, the SSH key will be added to
>> /root/.ssh/authorized_keys provided the file did not exist, see [3].
>>
>> [1] https://systemd.io/CREDENTIALS/
>> [2] https://www.freedesktop.org/software/systemd/man/latest/systemd.system-credentials.html
>> [3] https://www.freedesktop.org/software/systemd/man/latest/systemd.system-credentials.html#ssh.authorized_keys.root
>>
>> Suggested-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
>> Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
>> ---
>> PVE/QemuServer.pm | 77 +++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 77 insertions(+)
>>
>> diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
>> index 1566cf91..3ec21064 100644
>> --- a/PVE/QemuServer.pm
>> +++ b/PVE/QemuServer.pm
>> @@ -149,6 +149,26 @@ my $watchdog_fmt = {
>> };
>> PVE::JSONSchema::register_format('pve-qm-watchdog', $watchdog_fmt);
>>
>> +our $systemd_value_fmt = {
>> + value => {
>> + description => 'The credential value. base64=1 should be specified if the value is base64 encoded.',
>> + type => 'string',
>
> nack - this needs some sort of restriction (e.g. a sane character set
> for non-base64 encoded values, or base64)
>
>> + optional => 1,
>> + },
>> + snippet => {
>> + type => 'string',
>> + description => "Specify a snippet containing the credential's value",
>> + format => 'pve-volume-id',
>
> not too sure about adding yet more usage to the already overloaded and
> problematic snippet feature that we want to get rid of/overhaul..
>
>> + optional => 1,
>> + },
>> + base64 => {
>> + description => 'Whether the 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.",
>> @@ -2039,6 +2059,16 @@ sub parse_guest_agent {
>> return $res;
>> }
>>
>> +sub parse_systemd_credential {
>> + my ($value) = @_;
>> +
>> + return {} if !$value;
>> +
>> + my $res = eval { parse_property_string($systemd_value_fmt, $value) };
>> + warn $@ if $@;
>
> why? if you do this, it needs to be optin via a $noerr parameter..
>
>> + return $res;
>> +}
>> +
>> sub get_qga_key {
>> my ($conf, $key) = @_;
>> return undef if !defined($conf->{agent});
>> @@ -2390,6 +2420,12 @@ sub parse_vm_config {
>
> you only touch parse_vm_config.. how does this work on read-modify-write
> cycles??
>
What do you mean here?
>
>> $conf->{$key} = $value;
>> next;
>> }
>> + if ($key =~ /^systemd\.([a-z][a-z_\.-]*)$/) {
>> + # ignore validation of systemd credentials
>> + $conf->{'systemd-credentials'}->{$1} = $value;
>> + next;
>> + }
>> +
>
> this part here would not be needed if it were a regular config option
> instead of getting special treatment..
>
>> eval { $value = check_type($key, $value); };
>
> but this part here would still trigger schema validation then, which
> would be a plus!
>
>> if ($@) {
>> $handle_error->("vm $vmid - unable to parse value of '$key' - $@");
>> @@ -3514,6 +3550,27 @@ my sub get_vga_properties {
>> return ($vga, $qxlnum);
>> }
>>
>> +sub smbios_11_cred_arg {
>> + my ($key, $value, $is_encoded) = @_;
>> +
>> + if ($is_encoded) {
>> + return ('-smbios', "type=11,value=io.systemd.credential.binary:$key=$value");
>> + } else {
>> + return ('-smbios', "type=11,value=io.systemd.credential:$key=$value");
>> + }
>> +}
>> +
>> +sub read_systemd_custom_file {
>> + my ($storage_conf, $path) = @_;
>> +
>> + my ($vtype, undef) = PVE::Storage::parse_volname($storage_conf, $path);
>> +
>> + die "$path is not in the snippets directory\n" if $vtype ne 'snippets';
>> +
>> + my $full_path = PVE::Storage::abs_filesystem_path($storage_conf, $path, 1);
>> + return PVE::Tools::file_get_contents($full_path, 1 * 1024 * 1024);
>> +}
>
> this is a 1:1 copy of
> PVE::QemuServer::Cloudinit::read_cloudinit_snippets_file ..
>
>> +
>> sub config_to_command {
>> my ($storecfg, $vmid, $conf, $defaults, $forcemachine, $forcecpu,
>> $live_restore_backing) = @_;
>> @@ -4142,6 +4199,26 @@ sub config_to_command {
>> push @$cmd, '-snapshot';
>> }
>>
>> + # set systemd-credentials
>> + my $storage_conf;
>> + my $systemd_credentials = $conf->{'systemd-credentials'} || {};
>> + foreach my $key (keys %$systemd_credentials) {
>> + my $opts = parse_systemd_credential($systemd_credentials->{$key});
>> + my $is_encoded = $opts->{'base64'} ? 1 : 0;
>> + my $value;
>> +
>> + if (my $v = $opts->{'value'}) {
>> + $value = $v;
>> + } elsif (my $snippet = $opts->{'snippet'}) {
>> + $storage_conf = PVE::Storage::config() if !defined($storage_conf);
>
> why? config_to_command already has a copy of the storage config..
>
>> + my $contents = read_systemd_custom_file($storage_conf, $snippet);
>> + $value = encode_base64($contents, '');
>> + $is_encoded = 1;
>> + }
>> +
>> + push @$cmd, smbios_11_cred_arg($key, $value, $is_encoded) if $value;
>> + }
>> +
>> # 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
>>
>>
>>
>
>
> _______________________________________________
> pve-devel mailing list
> pve-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
v1 sent at https://lore.proxmox.com/pve-devel/20250402143620.440158-1-m.sandoval@proxmox.com/T/#u.
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next prev parent reply other threads:[~2025-04-02 14:38 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-24 14:35 [pve-devel] [RFC PATCH 1/2] config: allow hypens and dots in config keys Maximiliano Sandoval
2024-09-24 14:35 ` [pve-devel] [RFC PATCH 2/2] config: add systemd credentials support Maximiliano Sandoval
2024-10-22 8:29 ` Fabian Grünbichler
2025-04-02 14:36 ` Maximiliano Sandoval [this message]
2024-10-21 12:19 ` [pve-devel] [RFC PATCH 1/2] config: allow hypens and dots in config keys Fiona Ebner
2024-10-22 6:45 ` 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=s8o8qoid3yi.fsf@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 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