From 73e25009e55124f099d3dd24dae3c18e69cbe73c Mon Sep 17 00:00:00 2001 From: csoylu Date: Mon, 10 Jan 2022 17:57:12 +0100 Subject: [PATCH] Manual patching & Readme update. --- MANUALPATCH.md | 214 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 213 +----------------------------------------------- 2 files changed, 217 insertions(+), 210 deletions(-) create mode 100644 MANUALPATCH.md diff --git a/MANUALPATCH.md b/MANUALPATCH.md new file mode 100644 index 0000000..2d5d506 --- /dev/null +++ b/MANUALPATCH.md @@ -0,0 +1,214 @@ +Do these steps to apply patches manually; + +There is two files that we need to modify; + +## Qemu.pm +At : /usr/share/perl5/PVE/API2/Qemu.pm + +We need to change user password to cleartext because it is hashed as default and Cloudbase-Init can not use it as it is. +We can get the os type from the options of the VM so we will use to prevent Proxmox from hashing it if it is a Windows VM. + +The code to edit in my $update_vm_api fonction is belove; + + +``` + if (defined(my $cipassword = $param->{cipassword})) { + # Same logic as in cloud-init (but with the regex fixed...) + if (!(PVE::QemuServer::windows_version($ostype))) { # new if block for support windowsand insert old code inside it + $param->{cipassword} = PVE::Tools::encrypt_pw($cipassword) + if $cipassword !~ /^\$(?:[156]|2[ay])(\$.+){2}/; + } + } +``` + +## Cloudinit.pm +At : /usr/share/perl5/PVE/QemuServer/Cloudinit.pm + +We have a few changes to make to generate a meta_data.json that is compatible with Cloudbase-Init. + +* sub configdrive2_network + +We add a few lines to add DNS config + +``` +sub configdrive2_network { + my ($conf) = @_; + + my $content = "auto lo\n"; + $content .= "iface lo inet loopback\n\n"; + + my ($searchdomains, $nameservers) = get_dns_conf($conf); + + ## support windows + my $ostype = $conf->{"ostype"}; + my $default_dns = ''; + my $default_search = ''; + ## + + if ($nameservers && @$nameservers) { + $nameservers = join(' ', @$nameservers); + $content .= " dns_nameservers $nameservers\n"; + $default_dns = $nameservers; # Support windows + } + if ($searchdomains && @$searchdomains) { + $searchdomains = join(' ', @$searchdomains); + $content .= " dns_search $searchdomains\n"; + $default_search = $searchdomains; # Support windows + } + + 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}; + ## Support Windows + if(PVE::QemuServer::windows_version($ostype) && ($id eq "eth0")) { + $content .= " dns-nameservers $default_dns\n"; + $content .= " dns-search $default_search\n"; + } + ## + } + } + 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}; + } + } + } + + return $content; +} + +``` + +* New fonction before sub configdrive2_gen_metadata + +Cloudbase-Init doesnt turn static network adapters back to DHCP configuration. This fonction will provide us the mac adresses of adapters to turn on dhcp from our config file and we will use it later with our script to enable dhcp on those network adapters. + +``` +sub get_mac_addresses { + my ($conf) = @_; + + my $dhcpstring = undef; + my @dhcpmacs = (); + my @ifaces = grep { /^net(\d+)$/ } keys %$conf; + + foreach my $iface (sort @ifaces) { + (my $id = $iface) =~ s/^net//; + my $net = PVE::QemuServer::parse_net($conf->{$iface}); + next if !$conf->{"ipconfig$id"}; + my $ipconfig = PVE::QemuServer::parse_ipconfig($conf->{"ipconfig$id"}); + + my $mac = lc $net->{macaddr}; + + if (($ipconfig->{ip}) and ($ipconfig->{ip} eq 'dhcp')){ + push @dhcpmacs, $mac; + } + } + + if (@dhcpmacs){ + $dhcpstring .= ",\n \"dhcp\":["; + foreach my $mac (@dhcpmacs){ + if ($mac != @dhcpmacs[-1]){ + $dhcpstring .= "\"$mac\","; + } + else{ + $dhcpstring .= "\"$mac\"]"; + } + } + } + return ($dhcpstring); +} + +``` + +* configdrive2_gen_metadata +We will generate our meta data variables from this fonction and call the give to another fonction which will format them. +We get DHCP macs from our previous fonction, UUID, Hostname, Username, Password and we generate a json list from ssh keys. + +``` +sub configdrive2_gen_metadata { + my ($conf, $vmid, $user, $network) = @_; + + # Get DHCP nics by mac adresses + my $dhcpmacs = undef; + $dhcpmacs = get_mac_addresses($conf); + + # Get UUID + my $uuid_str = Digest::SHA::sha1_hex($user.$network); + # Get hostname + my ($hostname, $fqdn) = get_hostname_fqdn($conf, $vmid); + + # Get username, default to Administrateur if none + my $username = "Administrateur"; + if (defined($conf->{ciuser})){ + $username = $conf->{ciuser}; + } + + # Get user password + my $password = $conf->{cipassword}; + + # Get ssh keys and make a list out of it in json format + my $keystring = undef; + if (defined(my $pubkeys = $conf->{sshkeys})) { + $pubkeys = URI::Escape::uri_unescape($pubkeys); + my @pubkeysarray = split "\n", $pubkeys; + my $arraylength = @pubkeysarray; + my $incrementer = 1; + $keystring =",\n \"public_keys\": {\n"; + for my $key (@pubkeysarray){ + $keystring .= " \"SSH${incrementer}\" : \"${key}\""; + if ($arraylength != $incrementer){ + $keystring .= ",\n"; + }else{ + $keystring .= "\n }"; + } + $incrementer++; + } + } + + return configdrive2_metadata($password, $uuid_str, $hostname, $username, $keystring, $network, $dhcpmacs); +} +``` + +* configdrive2_metadata + +This will format a json file, with our previously generated values, in a very stringy way since this is how proxmox originally generates this data. + +``` +sub configdrive2_metadata { + my ($password, $uuid, $hostname, $username, $pubkeys, $network, $dhcpmacs) = @_; + return <<"EOF"; +{ + "meta":{ + "admin_username": "$username", + "admin_pass": "$password", + "uuid":"$uuid", + "hostname":"$hostname" + }, + "network_config":{"content_path":"/content/0000"}$pubkeys$dhcpmacs +} +EOF +} +``` + + + diff --git a/README.md b/README.md index 499173e..873cf78 100644 --- a/README.md +++ b/README.md @@ -13,215 +13,8 @@ Use Cloudbase-Init with Windows VMs to: You can do all below on system startup with the data provided by the cloud-init section of the proxmox gui. -There is two files that we need to modify; - -## Qemu.pm -At : /usr/share/perl5/PVE/API2/Qemu.pm - -We need to change user password to cleartext because it is hashed as default and Cloudbase-Init can not use it as it is. -We can get the os type from the options of the VM so we will use to prevent Proxmox from hashing it if it is a Windows VM. - -The code to edit in my $update_vm_api fonction is belove; - - -``` - if (defined(my $cipassword = $param->{cipassword})) { - # Same logic as in cloud-init (but with the regex fixed...) - if (!(PVE::QemuServer::windows_version($ostype))) { # new if block for support windowsand insert old code inside it - $param->{cipassword} = PVE::Tools::encrypt_pw($cipassword) - if $cipassword !~ /^\$(?:[156]|2[ay])(\$.+){2}/; - } - } -``` - -## Cloudinit.pm -At : /usr/share/perl5/PVE/QemuServer/Cloudinit.pm - -We have a few changes to make to generate a meta_data.json that is compatible with Cloudbase-Init. - -* sub configdrive2_network - -We add a few lines to add DNS config - -``` -sub configdrive2_network { - my ($conf) = @_; - - my $content = "auto lo\n"; - $content .= "iface lo inet loopback\n\n"; - - my ($searchdomains, $nameservers) = get_dns_conf($conf); - - ## support windows - my $ostype = $conf->{"ostype"}; - my $default_dns = ''; - my $default_search = ''; - ## - - if ($nameservers && @$nameservers) { - $nameservers = join(' ', @$nameservers); - $content .= " dns_nameservers $nameservers\n"; - $default_dns = $nameservers; # Support windows - } - if ($searchdomains && @$searchdomains) { - $searchdomains = join(' ', @$searchdomains); - $content .= " dns_search $searchdomains\n"; - $default_search = $searchdomains; # Support windows - } - - 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}; - ## Support Windows - if(PVE::QemuServer::windows_version($ostype) && ($id eq "eth0")) { - $content .= " dns-nameservers $default_dns\n"; - $content .= " dns-search $default_search\n"; - } - ## - } - } - 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}; - } - } - } - - return $content; -} - -``` - -* New fonction before sub configdrive2_gen_metadata - -Cloudbase-Init doesnt turn static network adapters back to DHCP configuration. This fonction will provide us the mac adresses of adapters to turn on dhcp from our config file and we will use it later with our script to enable dhcp on those network adapters. - -``` -sub get_mac_addresses { - my ($conf) = @_; - - my $dhcpstring = undef; - my @dhcpmacs = (); - my @ifaces = grep { /^net(\d+)$/ } keys %$conf; - - foreach my $iface (sort @ifaces) { - (my $id = $iface) =~ s/^net//; - my $net = PVE::QemuServer::parse_net($conf->{$iface}); - next if !$conf->{"ipconfig$id"}; - my $ipconfig = PVE::QemuServer::parse_ipconfig($conf->{"ipconfig$id"}); - - my $mac = lc $net->{macaddr}; - - if (($ipconfig->{ip}) and ($ipconfig->{ip} eq 'dhcp')){ - push @dhcpmacs, $mac; - } - } - - if (@dhcpmacs){ - $dhcpstring .= ",\n \"dhcp\":["; - foreach my $mac (@dhcpmacs){ - if ($mac != @dhcpmacs[-1]){ - $dhcpstring .= "\"$mac\","; - } - else{ - $dhcpstring .= "\"$mac\"]"; - } - } - } - return ($dhcpstring); -} - -``` - -* configdrive2_gen_metadata -We will generate our meta data variables from this fonction and call the give to another fonction which will format them. -We get DHCP macs from our previous fonction, UUID, Hostname, Username, Password and we generate a json list from ssh keys. - -``` -sub configdrive2_gen_metadata { - my ($conf, $vmid, $user, $network) = @_; - - # Get DHCP nics by mac adresses - my $dhcpmacs = undef; - $dhcpmacs = get_mac_addresses($conf); - - # Get UUID - my $uuid_str = Digest::SHA::sha1_hex($user.$network); - # Get hostname - my ($hostname, $fqdn) = get_hostname_fqdn($conf, $vmid); - - # Get username, default to Administrateur if none - my $username = "Administrateur"; - if (defined($conf->{ciuser})){ - $username = $conf->{ciuser}; - } - - # Get user password - my $password = $conf->{cipassword}; - - # Get ssh keys and make a list out of it in json format - my $keystring = undef; - if (defined(my $pubkeys = $conf->{sshkeys})) { - $pubkeys = URI::Escape::uri_unescape($pubkeys); - my @pubkeysarray = split "\n", $pubkeys; - my $arraylength = @pubkeysarray; - my $incrementer = 1; - $keystring =",\n \"public_keys\": {\n"; - for my $key (@pubkeysarray){ - $keystring .= " \"SSH${incrementer}\" : \"${key}\""; - if ($arraylength != $incrementer){ - $keystring .= ",\n"; - }else{ - $keystring .= "\n }"; - } - $incrementer++; - } - } - - return configdrive2_metadata($password, $uuid_str, $hostname, $username, $keystring, $network, $dhcpmacs); -} -``` - -* configdrive2_metadata - -This will format a json file, with our previously generated values, in a very stringy way since this is how proxmox originally generates this data. - -``` -sub configdrive2_metadata { - my ($password, $uuid, $hostname, $username, $pubkeys, $network, $dhcpmacs) = @_; - return <<"EOF"; -{ - "meta":{ - "admin_username": "$username", - "admin_pass": "$password", - "uuid":"$uuid", - "hostname":"$hostname" - }, - "network_config":{"content_path":"/content/0000"}$pubkeys$dhcpmacs -} -EOF -} -``` - +There is two files that we need to modify Qemu.pm and Cloudinit.pm. +* Qemu.pm to get password as cleartext in meta_data drive when it is a Windows VM. +* Cloudinit.pm to generate a metadata json file with variables that are compatible with Cloudbase-Init.