all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH qemu-server] fix 4493: cloud-init: fix generated Windows config
@ 2024-07-09 15:12 Mira Limbeck
  2024-07-09 15:12 ` [pve-devel] [PATCH docs] cloudinit: add Windows cloudbase-init section Mira Limbeck
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Mira Limbeck @ 2024-07-09 15:12 UTC (permalink / raw)
  To: pve-devel

cloudbase-init, a cloud-init reimplementation for Windows, supports only
a subset of the configuration options of cloud-init. Some features
depend on support by the Metadata Service (ConfigDrive2 here) and have
further limitations [0].

To support a basic setup the following changes were made:
 - password is saved as plaintext for any Windows guests (ostype)
 - DNS servers are added to each of the interfaces
 - SSH public keys are passed via metadata

Network and metadata generation for cloudbase-init is separate from the
default ConfigDrive2 one so as to not interfere with any other OSes that
depend on the current ConfigDrive2 implementation.

[0] https://cloudbase-init.readthedocs.io/en/latest/index.html

Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
---
DNS search domains are not handled at all by the cloudbase-init ENI
parser.
The password is used for the Admin user specified in the
cloudbase-init.conf inside the guest. Specifying a different user does
not work. This would require rewriting the userdata handling as
described in #5384 [1] which is a breaking change. Userdata generation
is currently shared between all implementations, but the new one could
be made cloudbase-init only for now.

To know if the password needs to be unencrypted, we have to check the
`ostype`. For this we need access to the config. That's why I moved the
`cipassword` handling inside $updatefn.

When no `citype` is specified, it will default to `configdrive2` on
Windows. The check requires `ostype` to be set correctly.
Any other `citype`s may not work correctly if used for cloudbase-init.

The docs patch adds a section on how to configure a Windows guest for
cloudbase-init.


[1] https://bugzilla.proxmox.com/show_bug.cgi?id=5384

 PVE/API2/Qemu.pm            |  13 ++---
 PVE/QemuServer/Cloudinit.pm | 100 ++++++++++++++++++++++++++++++++++--
 2 files changed, 102 insertions(+), 11 deletions(-)

diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 7414385..b75c695 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -1667,12 +1667,6 @@ my $update_vm_api  = sub {
 
     my $skip_cloud_init = extract_param($param, 'skip_cloud_init');
 
-    if (defined(my $cipassword = $param->{cipassword})) {
-	# Same logic as in cloud-init (but with the regex fixed...)
-	$param->{cipassword} = PVE::Tools::encrypt_pw($cipassword)
-	    if $cipassword !~ /^\$(?:[156]|2[ay])(\$.+){2}/;
-    }
-
     my @paramarr = (); # used for log message
     foreach my $key (sort keys %$param) {
 	my $value = $key eq 'cipassword' ? '<hidden>' : $param->{$key};
@@ -2022,6 +2016,13 @@ my $update_vm_api  = sub {
 		    my $machine_conf = PVE::QemuServer::Machine::parse_machine($param->{$opt});
 		    PVE::QemuServer::Machine::assert_valid_machine_property($conf, $machine_conf);
 		    $conf->{pending}->{$opt} = $param->{$opt};
+		} elsif ($opt eq 'cipassword') {
+		    if (!PVE::QemuServer::Helpers::windows_version($conf->{ostype})) {
+			# Same logic as in cloud-init (but with the regex fixed...)
+			$param->{cipassword} = PVE::Tools::encrypt_pw($param->{cipassword})
+			    if $param->{cipassword} !~ /^\$(?:[156]|2[ay])(\$.+){2}/;
+		    }
+		    $conf->{cipassword} = $param->{cipassword};
 		} else {
 		    $conf->{pending}->{$opt} = $param->{$opt};
 
diff --git a/PVE/QemuServer/Cloudinit.pm b/PVE/QemuServer/Cloudinit.pm
index abc6b14..d4ecfac 100644
--- a/PVE/QemuServer/Cloudinit.pm
+++ b/PVE/QemuServer/Cloudinit.pm
@@ -8,6 +8,8 @@ use Digest::SHA;
 use URI::Escape;
 use MIME::Base64 qw(encode_base64);
 use Storable qw(dclone);
+use JSON;
+use URI;
 
 use PVE::Tools qw(run_command file_set_contents);
 use PVE::Storage;
@@ -232,12 +234,23 @@ sub generate_configdrive2 {
     my ($conf, $vmid, $drive, $volname, $storeid) = @_;
 
     my ($user_data, $network_data, $meta_data, $vendor_data) = get_custom_cloudinit_files($conf);
-    $user_data = cloudinit_userdata($conf, $vmid) if !defined($user_data);
-    $network_data = configdrive2_network($conf) if !defined($network_data);
-    $vendor_data = '' if !defined($vendor_data);
+    if (PVE::QemuServer::Helpers::windows_version($conf->{ostype})) {
+	$user_data = cloudinit_userdata($conf, $vmid) if !defined($user_data);
+	$network_data = cloudbase_network_eni($conf) if !defined($network_data);
+	$vendor_data = '' if !defined($vendor_data);
+
+	if (!defined($meta_data)) {
+	    my $instance_id = cloudbase_gen_instance_id($user_data, $network_data);
+	    $meta_data = cloudbase_configdrive2_metadata($instance_id, $conf);
+	}
+    } else {
+	$user_data = cloudinit_userdata($conf, $vmid) if !defined($user_data);
+	$network_data = configdrive2_network($conf) if !defined($network_data);
+	$vendor_data = '' if !defined($vendor_data);
 
-    if (!defined($meta_data)) {
-	$meta_data = configdrive2_gen_metadata($user_data, $network_data);
+	if (!defined($meta_data)) {
+	    $meta_data = configdrive2_gen_metadata($user_data, $network_data);
+	}
     }
 
     # we always allocate a 4MiB disk for cloudinit and with the overhead of the ISO
@@ -254,6 +267,83 @@ sub generate_configdrive2 {
     commit_cloudinit_disk($conf, $vmid, $drive, $volname, $storeid, $files, 'config-2');
 }
 
+sub cloudbase_network_eni {
+    my ($conf) = @_;
+
+    my $content = "";
+
+    my ($searchdomains, $nameservers) = get_dns_conf($conf);
+    if ($nameservers && @$nameservers) {
+	$nameservers = join(' ', @$nameservers);
+    }
+
+    my @ifaces = grep { /^net(\d+)$/ } keys %$conf;
+    foreach my $iface (sort @ifaces) {
+	(my $id = $iface) =~ s/^net//;
+	next if !$conf->{"ipconfig$id"};
+	my $net = PVE::QemuServer::parse_ipconfig($conf->{"ipconfig$id"});
+	$id = "eth$id";
+
+	$content .="auto $id\n";
+	if ($net->{ip}) {
+	    if ($net->{ip} eq 'dhcp') {
+		$content .= "iface $id inet dhcp\n";
+	    } else {
+		my ($addr, $mask) = split_ip4($net->{ip});
+		$content .= "iface $id inet static\n";
+		$content .= "        address $addr\n";
+		$content .= "        netmask $mask\n";
+		$content .= "        gateway $net->{gw}\n" if $net->{gw};
+		$content .= "        dns-nameservers $nameservers\n" if $nameservers;
+	    }
+	}
+	if ($net->{ip6}) {
+	    if ($net->{ip6} =~ /^(auto|dhcp)$/) {
+		$content .= "iface $id inet6 $1\n";
+	    } else {
+		my ($addr, $mask) = split('/', $net->{ip6});
+		$content .= "iface $id inet6 static\n";
+		$content .= "        address $addr\n";
+		$content .= "        netmask $mask\n";
+		$content .= "        gateway $net->{gw6}\n" if $net->{gw6};
+		$content .= "        dns-nameservers $nameservers\n" if $nameservers;
+	    }
+	}
+    }
+
+    return $content;
+}
+
+sub cloudbase_configdrive2_metadata {
+    my ($uuid, $conf) = @_;
+    my $meta_data = {
+	uuid => $uuid,
+	'network_config' => {
+	    'content_path' => '/content/0000',
+	},
+    };
+    $meta_data->{'admin_pass'} = $conf->{cipassword} if $conf->{cipassword};
+    if (defined(my $keys = $conf->{sshkeys})) {
+	$keys = URI::Escape::uri_unescape($keys);
+	$keys = [map { my $key = $_; chomp $key; $key } split(/\n/, $keys)];
+	$keys = [grep { /\S/ } @$keys];
+	my $i = 0;
+	foreach my $k (@$keys) {
+	    $meta_data->{'public_keys'}->{"key-$i"} = $k;
+	    $i++;
+	}
+    }
+    my $json = encode_json($meta_data);
+    return $json;
+}
+
+sub cloudbase_gen_instance_id {
+    my ($user, $network) = @_;
+
+    my $uuid_str = Digest::SHA::sha1_hex($user.$network);
+    return $uuid_str;
+}
+
 sub generate_opennebula {
     my ($conf, $vmid, $drive, $volname, $storeid) = @_;
 
-- 
2.39.2


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


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2024-07-22 10:01 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-09 15:12 [pve-devel] [PATCH qemu-server] fix 4493: cloud-init: fix generated Windows config Mira Limbeck
2024-07-09 15:12 ` [pve-devel] [PATCH docs] cloudinit: add Windows cloudbase-init section Mira Limbeck
2024-07-10 14:35 ` [pve-devel] [PATCH qemu-server] fix 4493: cloud-init: fix generated Windows config Mira Limbeck
2024-07-18 15:51 ` Friedrich Weber
2024-07-22 10:01   ` Mira Limbeck

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