File grub-use-disk-password.patch of Package python-kiwi

This patch adds a new attribute to the <bootloader> element, called
use_disk_password (true/false). If set to true, the cryptomount command
in the EFI grub.cfg file will explicitly specify the LUKS password.

This is useful when shipping images with a LUKS root partition, allowing
them to come up on firstboot without the user having to enter a well-known
"secret".

Index: kiwi-9.24.47/kiwi/bootloader/config/grub2.py
===================================================================
--- kiwi-9.24.47.orig/kiwi/bootloader/config/grub2.py
+++ kiwi-9.24.47/kiwi/bootloader/config/grub2.py
@@ -120,6 +120,7 @@ class BootLoaderConfigGrub2(BootLoaderCo
             Defaults.get_volume_id()
         self.install_volid = self.xml_state.build_type.get_volid() or \
             Defaults.get_install_volume_id()
+        self.use_disk_password = self.xml_state.get_build_type_bootloader_use_disk_password()
 
         self.live_boot_options = [
             'root=live:CDLABEL={0}'.format(self.volume_id),
Index: kiwi-9.24.47/kiwi/bootloader/install/grub2.py
===================================================================
--- kiwi-9.24.47.orig/kiwi/bootloader/install/grub2.py
+++ kiwi-9.24.47/kiwi/bootloader/install/grub2.py
@@ -272,6 +272,31 @@ class BootLoaderInstallGrub2(BootLoaderI
                 # restore the grub installer noop
                 self._enable_grub2_install(self.root_mount.mountpoint)
 
+    def set_disk_password(self, password):
+        log.debug(f'set_disk_password({password})')
+        if password is None:
+            return
+
+        config_file = f'{self.root_mount.mountpoint}/boot/efi/EFI/BOOT/grub.cfg'
+        if not os.path.exists(config_file):
+            log.debug(f'{config_file} does not exist')
+            return
+
+        with open(config_file) as config:
+            import re
+
+            grub_config = config.read()
+            grub_config = re.sub(
+                    r'cryptomount',
+                    f'cryptomount -p "{password}"',
+                    grub_config
+                )
+
+        with open(config_file, 'w') as grub_config_file:
+            grub_config_file.write(grub_config)
+
+        log.debug(f'<<< {grub_config} >>>')
+
     def _mount_device_and_volumes(self):
         if self.root_mount is None:
             self.root_mount = MountManager(
Index: kiwi-9.24.47/kiwi/builder/disk.py
===================================================================
--- kiwi-9.24.47.orig/kiwi/builder/disk.py
+++ kiwi-9.24.47/kiwi/builder/disk.py
@@ -1497,6 +1497,11 @@ class DiskBuilder:
 
         if self.bootloader != 'custom':
             # create bootloader config prior bootloader installation
+            disk_password = None
+            if self.bootloader_config.use_disk_password and self.luks:
+                disk_password = self.luks
+            log.debug(f'Passing disk encryption password "{disk_password}" to boot loader')
+
             try:
                 self.bootloader_config.setup_disk_image_config(
                     boot_options=custom_install_arguments
@@ -1519,6 +1524,9 @@ class DiskBuilder:
                 bootloader.install()
             bootloader.secure_boot_install()
 
+            if disk_password:
+                bootloader.set_disk_password(disk_password)
+
         self.system_setup.call_edit_boot_install_script(
             self.diskname, boot_device.get_device()
         )
Index: kiwi-9.24.47/kiwi/schema/kiwi.rnc
===================================================================
--- kiwi-9.24.47.orig/kiwi/schema/kiwi.rnc
+++ kiwi-9.24.47/kiwi/schema/kiwi.rnc
@@ -2673,6 +2673,11 @@ div {
             sch:param [ name = "attr" value = "targettype" ]
             sch:param [ name = "types" value = "grub2_s390x_emu" ]
         ]
+    k.bootloader.use_disk_password.attribute =
+	## When /boot is encrypted, make the boot loader store the
+	## password in its configuration file (in cleartext). This
+	## is useful for full disk encryption images
+	attribute use_disk_password { xsd:boolean }
     k.bootloader.attlist =
         k.bootloader.name.attribute &
         k.bootloader.console.attribute? &
@@ -2680,6 +2685,7 @@ div {
         k.bootloader.timeout.attribute? &
         k.bootloader.timeout_style.attribute? &
         k.bootloader.targettype.attribute? &
+        k.bootloader.use_disk_password.attribute? &
         k.bootloader.grub_template.attribute?
 
     k.bootloader =
Index: kiwi-9.24.47/kiwi/schema/kiwi.rng
===================================================================
--- kiwi-9.24.47.orig/kiwi/schema/kiwi.rng
+++ kiwi-9.24.47/kiwi/schema/kiwi.rng
@@ -3996,6 +3996,14 @@ for 4k DASD devices use CDL</a:documenta
         <sch:param name="types" value="grub2_s390x_emu"/>
       </sch:pattern>
     </define>
+    <define name="k.bootloader.use_disk_password.attribute">
+      <attribute name="use_disk_password">
+        <a:documentation>When /boot is encrypted, make the boot loader store the
+password in its configuration file (in cleartext). This
+is useful for full disk encryption images</a:documentation>
+        <data type="boolean"/>
+      </attribute>
+    </define>
     <define name="k.bootloader.attlist">
       <interleave>
         <ref name="k.bootloader.name.attribute"/>
@@ -4015,6 +4023,9 @@ for 4k DASD devices use CDL</a:documenta
           <ref name="k.bootloader.targettype.attribute"/>
         </optional>
         <optional>
+          <ref name="k.bootloader.use_disk_password.attribute"/>
+        </optional>
+        <optional>
           <ref name="k.bootloader.grub_template.attribute"/>
         </optional>
       </interleave>
Index: kiwi-9.24.47/kiwi/xml_parse.py
===================================================================
--- kiwi-9.24.47.orig/kiwi/xml_parse.py
+++ kiwi-9.24.47/kiwi/xml_parse.py
@@ -16,10 +16,10 @@
 #   kiwi/schema/kiwi_for_generateDS.xsd
 #
 # Command line:
-#   /home/ms/Project/kiwi/.tox/3.6/bin/generateDS.py -f --external-encoding="utf-8" --no-dates --no-warnings -o "kiwi/xml_parse.py" kiwi/schema/kiwi_for_generateDS.xsd
+#   /home/okir/.local/bin/generateDS.py -f --external-encoding="utf-8" --no-dates --no-warnings -o "kiwi/xml_parse.py" kiwi/schema/kiwi_for_generateDS.xsd
 #
 # Current working directory (os.getcwd()):
-#   kiwi
+#   kiwi-9.24.41
 #
 
 import sys
@@ -5214,7 +5214,7 @@ class bootloader(GeneratedsSuper):
     provide configuration parameters for it"""
     subclass = None
     superclass = None
-    def __init__(self, name=None, console=None, serial_line=None, timeout=None, timeout_style=None, targettype=None, grub_template=None):
+    def __init__(self, name=None, console=None, serial_line=None, timeout=None, timeout_style=None, targettype=None, use_disk_password=None, grub_template=None):
         self.original_tagname_ = None
         self.name = _cast(None, name)
         self.console = _cast(None, console)
@@ -5222,6 +5222,7 @@ class bootloader(GeneratedsSuper):
         self.timeout = _cast(int, timeout)
         self.timeout_style = _cast(None, timeout_style)
         self.targettype = _cast(None, targettype)
+        self.use_disk_password = _cast(bool, use_disk_password)
         self.grub_template = _cast(None, grub_template)
     def factory(*args_, **kwargs_):
         if CurrentSubclassModule_ is not None:
@@ -5246,6 +5247,8 @@ class bootloader(GeneratedsSuper):
     def set_timeout_style(self, timeout_style): self.timeout_style = timeout_style
     def get_targettype(self): return self.targettype
     def set_targettype(self, targettype): self.targettype = targettype
+    def get_use_disk_password(self): return self.use_disk_password
+    def set_use_disk_password(self, use_disk_password): self.use_disk_password = use_disk_password
     def get_grub_template(self): return self.grub_template
     def set_grub_template(self, grub_template): self.grub_template = grub_template
     def validate_grub_console(self, value):
@@ -5301,6 +5304,9 @@ class bootloader(GeneratedsSuper):
         if self.targettype is not None and 'targettype' not in already_processed:
             already_processed.add('targettype')
             outfile.write(' targettype=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.targettype), input_name='targettype')), ))
+        if self.use_disk_password is not None and 'use_disk_password' not in already_processed:
+            already_processed.add('use_disk_password')
+            outfile.write(' use_disk_password="%s"' % self.gds_format_boolean(self.use_disk_password, input_name='use_disk_password'))
         if self.grub_template is not None and 'grub_template' not in already_processed:
             already_processed.add('grub_template')
             outfile.write(' grub_template=%s' % (self.gds_encode(self.gds_format_string(quote_attrib(self.grub_template), input_name='grub_template')), ))
@@ -5348,6 +5354,15 @@ class bootloader(GeneratedsSuper):
             already_processed.add('targettype')
             self.targettype = value
             self.targettype = ' '.join(self.targettype.split())
+        value = find_attr_value_('use_disk_password', node)
+        if value is not None and 'use_disk_password' not in already_processed:
+            already_processed.add('use_disk_password')
+            if value in ('true', '1'):
+                self.use_disk_password = True
+            elif value in ('false', '0'):
+                self.use_disk_password = False
+            else:
+                raise_parse_error(node, 'Bad boolean attribute')
         value = find_attr_value_('grub_template', node)
         if value is not None and 'grub_template' not in already_processed:
             already_processed.add('grub_template')
Index: kiwi-9.24.47/kiwi/xml_state.py
===================================================================
--- kiwi-9.24.47.orig/kiwi/xml_state.py
+++ kiwi-9.24.47/kiwi/xml_state.py
@@ -1026,6 +1026,20 @@ class XMLState:
             return bootloader.get_targettype()
         return None
 
+    def get_build_type_bootloader_use_disk_password(self) -> bool:
+        """
+        Indicate whether the bootloader configuration should use the
+	password protecting the encrypted root volume.
+
+        :return: True|False
+
+        :rtype: bool
+        """
+        bootloader = self.get_build_type_bootloader_section()
+        if bootloader:
+            return bootloader.get_use_disk_password()
+        return False
+
     def get_build_type_oemconfig_section(self) -> Any:
         """
         First oemconfig section from the build type section
openSUSE Build Service is sponsored by