From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 065A51FF161 for ; Tue, 24 Sep 2024 16:35:26 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id C2968F94B; Tue, 24 Sep 2024 16:35:36 +0200 (CEST) From: Maximiliano Sandoval To: pve-devel@lists.proxmox.com Date: Tue, 24 Sep 2024 16:35:02 +0200 Message-Id: <20240924143502.361901-2-m.sandoval@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20240924143502.361901-1-m.sandoval@proxmox.com> References: <20240924143502.361901-1-m.sandoval@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.101 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 Subject: [pve-devel] [RFC PATCH 2/2] config: add systemd credentials support 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 Cc: Wolfgang Bumiller Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" 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 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 Signed-off-by: Maximiliano Sandoval --- 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', + optional => 1, + }, + snippet => { + type => 'string', + description => "Specify a snippet containing the credential's value", + format => 'pve-volume-id', + 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 $@; + return $res; +} + sub get_qga_key { my ($conf, $key) = @_; return undef if !defined($conf->{agent}); @@ -2390,6 +2420,12 @@ sub parse_vm_config { $conf->{$key} = $value; next; } + if ($key =~ /^systemd\.([a-z][a-z_\.-]*)$/) { + # ignore validation of systemd credentials + $conf->{'systemd-credentials'}->{$1} = $value; + next; + } + eval { $value = check_type($key, $value); }; 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); +} + 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); + 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