* [pve-devel] [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config
@ 2024-07-30 15:15 Mira Limbeck
2024-07-30 15:15 ` [pve-devel] [PATCH v3 docs] cloudinit: add Windows cloudbase-init section Mira Limbeck
2024-07-30 19:14 ` [pve-devel] applied: [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Thomas Lamprecht
0 siblings, 2 replies; 5+ messages in thread
From: Mira Limbeck @ 2024-07-30 15:15 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.
DNS search domains were removed because Cloudbase-Init's ENI parser
doesn't handle it at all.
The password set via `cipassword` is used for the Admin user configured
in the cloudbase-init.conf in the guest while the `ciuser` parameter is
ignored. The Admin user has to be set in the cloudbase-init.conf file
instead.
Specifying a different user does not work.
For the password to work the `ostype` needs to be any Windows variant
before `cipassword` is set. Otherwise the password will be encrypted and
the encrypted password used as plaintext password in the guest.
The `citype` needs to be `configdrive2`, which is the default for
Windows guests, for the generated configs to be compatible with
Cloudbase-Init.
[0] https://cloudbase-init.readthedocs.io/en/latest/index.html
Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
---
v3:
- removed `use URI` since we already `use URI::Escape`
- sent a separate patch adding `liburi-perl` dependency in d/control
v2:
- unchanged
PVE/API2/Qemu.pm | 13 ++---
PVE/QemuServer/Cloudinit.pm | 99 +++++++++++++++++++++++++++++++++++--
2 files changed, 101 insertions(+), 11 deletions(-)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index a3313f3..d25a79f 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -1674,12 +1674,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};
@@ -2029,6 +2023,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..4efbdf5 100644
--- a/PVE/QemuServer/Cloudinit.pm
+++ b/PVE/QemuServer/Cloudinit.pm
@@ -8,6 +8,7 @@ use Digest::SHA;
use URI::Escape;
use MIME::Base64 qw(encode_base64);
use Storable qw(dclone);
+use JSON;
use PVE::Tools qw(run_command file_set_contents);
use PVE::Storage;
@@ -232,12 +233,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 +266,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
* [pve-devel] [PATCH v3 docs] cloudinit: add Windows cloudbase-init section
2024-07-30 15:15 [pve-devel] [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Mira Limbeck
@ 2024-07-30 15:15 ` Mira Limbeck
2024-07-30 19:14 ` [pve-devel] applied: " Thomas Lamprecht
2024-07-30 19:14 ` [pve-devel] applied: [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Thomas Lamprecht
1 sibling, 1 reply; 5+ messages in thread
From: Mira Limbeck @ 2024-07-30 15:15 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
---
v3:
- fixed list continuity/indentation
v2:
- added metadata_services config option
- added Sysprep section
- fixed typos and clarified some parts
qm-cloud-init.adoc | 154 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 154 insertions(+)
diff --git a/qm-cloud-init.adoc b/qm-cloud-init.adoc
index 8686ed7..4e44fc0 100644
--- a/qm-cloud-init.adoc
+++ b/qm-cloud-init.adoc
@@ -169,6 +169,160 @@ qm cloudinit dump 9000 user
The same command exists for `network` and `meta`.
+Cloud-Init on Windows
+~~~~~~~~~~~~~~~~~~~~~
+
+There is a reimplementation of Cloud-Init available for Windows called
+https://cloudbase.it/[cloudbase-init]. Not every feature of Cloud-Init is
+available with Cloudbase-Init, and some features differ compared to Cloud-Init.
+
+Cloudbase-Init requires both `ostype` set to any Windows version and the
+`citype` set to `configdrive2`, which is the default with any Windows
+`ostype`.
+
+There are no ready-made cloud images for Windows available for free. Using
+Cloudbase-Init requires manually installing and configuring a Windows guest.
+
+
+Preparing Cloudbase-Init Templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first step is to install Windows in a VM. Download and install
+Cloudbase-Init in the guest. It may be necessary to install the Beta version.
+Don't run Sysprep at the end of the installation. Instead configure
+Cloudbase-Init first.
+
+A few common options to set would be:
+
+* 'username': This sets the username of the administrator
+
+* 'groups': This allows one to add the user to the `Administrators` group
+
+* 'inject_user_password': Set this to `true` to allow setting the password
+in the VM config
+
+* 'first_logon_behaviour': Set this to `no` to not require a new password on
+login
+
+* 'rename_admin_user': Set this to `true` to allow renaming the default
+`Administrator` user to the username specified with `username`
+
+* 'metadata_services': Set this to
+`cloudbaseinit.metadata.services.configdrive.ConfigDriveService` for
+Cloudbase-Init to first check this serivce. Otherwise it may take a few minutes
+for Cloudbase-Init to configure the system after boot.
+
+Some plugins, for example the SetHostnamePlugin, require reboots and will do
+so automatically. To disable automatic reboots by Cloudbase-Init, you can set
+`allow_reboot` to `false`.
+
+A full set of configuration options can be found in the
+https://cloudbase-init.readthedocs.io/en/latest/config.html[official
+cloudbase-init documentation].
+
+It can make sense to make a snapshot after configuring in case some parts of
+the config still need adjustments.
+After configuring Cloudbase-Init you can start creating the template. Shutdown
+the Windows guest, add a Cloud-Init disk and make it into a template.
+
+----
+qm set 9000 --ide2 local-lvm:cloudinit
+qm template 9000
+----
+
+Clone the template into a new VM:
+
+----
+qm clone 9000 123 --name windows123
+----
+
+Then set the password, network config and SSH key:
+
+----
+qm set 123 --cipassword <password>
+qm set 123 --ipconfig0 ip=10.0.10.123/24,gw=10.0.10.1
+qm set 123 --sshkey ~/.ssh/id_rsa.pub
+----
+
+Make sure that the `ostype` is set to any Windows version before setting the
+password. Otherwise the password will be encrypted and Cloudbase-Init will use
+the encrypted password as plaintext password.
+
+When everything is set, start the cloned guest. On the first boot the login
+won't work and it will reboot automatically for the changed hostname.
+After the reboot the new password should be set and login should work.
+
+
+Cloudbase-Init and Sysprep
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sysprep is a feature to reset the configuration of Windows and provide a `new`
+system. This can be used in conjunction with Cloudbase-Init to create a clean
+template.
+
+
+When using Sysprep there are 2 configuration files that need to be adapted.
+The first one is the normal configuration file, the second one is the one
+ending in `-unattend.conf`.
+
+Cloudbase-Init runs in 2 steps, first the Sysprep step using the
+`-unattend.conf` and then the regular step using the primary config file.
+
+For `Windows Server` running Sysprep with the provided `Unattend.xml` file
+should work out of the box. Normal Windows versions however require additional
+steps:
+
+. Open a PowerShell instance
+
+. Enable the Administrator user:
++
+----
+net user Administrator /active:yes`
+----
+
+. Install Cloudbase-Init using the Administrator user
+
+. Modify `Unattend.xml` to include the command to enable the Administrator user
+on the first boot after sysprepping:
++
+----
+<RunSynchronousCommand wcm:action="add">
+ <Path>net user administrator /active:yes</Path>
+ <Order>1</Order>
+ <Description>Enable Administrator User</Description>
+</RunSynchronousCommand>
+----
++
+Make sure the `<Order>` does not conflict with other synchronous commands.
+Modify `<Order>` of the Cloudbase-Init command to run after this one by
+increasing the number to a higher value: `<Order>2</Order>`
+
+. (Windows 11 only) Remove the conflicting Microsoft.OneDriveSync package:
++
+----
+Get-AppxPackage -AllUsers Microsoft.OneDriveSync | Remove-AppxPackage -AllUsers
+----
+
+. `cd` into the Cloudbase-Init config directory:
++
+----
+cd 'C:\Program Files\Cloudbase Solutions\Cloudbase-Init\conf'
+----
+
+. (optional) Create a snapshot of the VM before Sysprep in case of a
+misconfiguration
+
+. Run Sysprep:
++
+----
+C:\Windows\System32\Sysprep\sysprep.exe /generalize /oobe /unattend:Unattend.xml
+----
+
+After following the above steps the VM should be in shut down state due to
+the Sysprep. Now you can make it into a template, clone it and configure
+it as needed.
+
+
Cloud-Init specific Options
~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
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
* [pve-devel] applied: [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config
2024-07-30 15:15 [pve-devel] [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Mira Limbeck
2024-07-30 15:15 ` [pve-devel] [PATCH v3 docs] cloudinit: add Windows cloudbase-init section Mira Limbeck
@ 2024-07-30 19:14 ` Thomas Lamprecht
2024-07-31 8:02 ` Mira Limbeck
1 sibling, 1 reply; 5+ messages in thread
From: Thomas Lamprecht @ 2024-07-30 19:14 UTC (permalink / raw)
To: Proxmox VE development discussion, Mira Limbeck
Am 30/07/2024 um 17:15 schrieb Mira Limbeck:
> 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.
>
> DNS search domains were removed because Cloudbase-Init's ENI parser
> doesn't handle it at all.
> The password set via `cipassword` is used for the Admin user configured
> in the cloudbase-init.conf in the guest while the `ciuser` parameter is
> ignored. The Admin user has to be set in the cloudbase-init.conf file
> instead.
> Specifying a different user does not work.
>
> For the password to work the `ostype` needs to be any Windows variant
> before `cipassword` is set. Otherwise the password will be encrypted and
> the encrypted password used as plaintext password in the guest.
>
> The `citype` needs to be `configdrive2`, which is the default for
> Windows guests, for the generated configs to be compatible with
> Cloudbase-Init.
>
> [0] https://cloudbase-init.readthedocs.io/en/latest/index.html
>
> Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
> ---
> v3:
> - removed `use URI` since we already `use URI::Escape`
> - sent a separate patch adding `liburi-perl` dependency in d/control
> v2:
> - unchanged
>
> PVE/API2/Qemu.pm | 13 ++---
> PVE/QemuServer/Cloudinit.pm | 99 +++++++++++++++++++++++++++++++++++--
> 2 files changed, 101 insertions(+), 11 deletions(-)
>
>
applied series, thanks!
Some tests would be nice for this CI stuff in general though, e.g. taking
in CI properties and mocking the write/apply parts to test if the resulting
output matches our expectation could already be a simple regression test
providing some basic safety net.
_______________________________________________
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
* Re: [pve-devel] applied: [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config
2024-07-30 19:14 ` [pve-devel] applied: [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Thomas Lamprecht
@ 2024-07-31 8:02 ` Mira Limbeck
0 siblings, 0 replies; 5+ messages in thread
From: Mira Limbeck @ 2024-07-31 8:02 UTC (permalink / raw)
To: Thomas Lamprecht, Proxmox VE development discussion
On 7/30/24 21:14, Thomas Lamprecht wrote:
> Am 30/07/2024 um 17:15 schrieb Mira Limbeck:
>> 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.
>>
>> DNS search domains were removed because Cloudbase-Init's ENI parser
>> doesn't handle it at all.
>> The password set via `cipassword` is used for the Admin user configured
>> in the cloudbase-init.conf in the guest while the `ciuser` parameter is
>> ignored. The Admin user has to be set in the cloudbase-init.conf file
>> instead.
>> Specifying a different user does not work.
>>
>> For the password to work the `ostype` needs to be any Windows variant
>> before `cipassword` is set. Otherwise the password will be encrypted and
>> the encrypted password used as plaintext password in the guest.
>>
>> The `citype` needs to be `configdrive2`, which is the default for
>> Windows guests, for the generated configs to be compatible with
>> Cloudbase-Init.
>>
>> [0] https://cloudbase-init.readthedocs.io/en/latest/index.html
>>
>> Signed-off-by: Mira Limbeck <m.limbeck@proxmox.com>
>> ---
>> v3:
>> - removed `use URI` since we already `use URI::Escape`
>> - sent a separate patch adding `liburi-perl` dependency in d/control
>> v2:
>> - unchanged
>>
>> PVE/API2/Qemu.pm | 13 ++---
>> PVE/QemuServer/Cloudinit.pm | 99 +++++++++++++++++++++++++++++++++++--
>> 2 files changed, 101 insertions(+), 11 deletions(-)
>>
>>
>
> applied series, thanks!
>
> Some tests would be nice for this CI stuff in general though, e.g. taking
> in CI properties and mocking the write/apply parts to test if the resulting
> output matches our expectation could already be a simple regression test
> providing some basic safety net.
Will look into it
_______________________________________________
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-31 8:02 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-30 15:15 [pve-devel] [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Mira Limbeck
2024-07-30 15:15 ` [pve-devel] [PATCH v3 docs] cloudinit: add Windows cloudbase-init section Mira Limbeck
2024-07-30 19:14 ` [pve-devel] applied: " Thomas Lamprecht
2024-07-30 19:14 ` [pve-devel] applied: [PATCH v3 qemu-server] fix 4493: cloud-init: fix generated Windows config Thomas Lamprecht
2024-07-31 8:02 ` Mira Limbeck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox