Red Hat 7.4: How to inject kickstart file into USB media for UEFI-only system?

The decisive advantage of original over genisoimage remake is probably in the line

-boot_image isolinux partition_entry=gpt_basdat

genisoimage cannot make partition tables for EFI. (Inspect both ISOs by "/sbin/fdisk -l" to see the difference.) You actually need the MBR partition of type 0xef. But tradition is to also add an invalid and thus useless GPT.

One way to get the partition tables would be to run after genisoimage

isohybrid --uefi $THISDIR/$VOLLABEL.iso

The program stems from SYSLINUX source. One should use the version from the same source release or git clone from which "isolinux.bin" stems.

Other distros use xorriso's mkisofs emulation with the boot options which this xorriso command tells:

xorriso -indev rhel-server-7.4-x86_64-dvd.iso -report_el_torito as_mkisofs

There will be a lengthy addess to option -isohybrid-mbr:

--interval:imported_iso:0s-15s:zero_mbrpt,zero_gpt:'rhel-server-7.4-x86_64-boot.iso

It tells xorriso to use the first 32 KiB of the original ISO as MBR template and further System Area. Normally it is SYSLINUX file "isohdpfx.bin", which has only 432 bytes. You may replace it by "mbr.bin" after copying the first 432 bytes yourself:

dd if=rhel-server-7.4-x86_64-dvd.iso bs=432 count=1 of=mbr.bin

I now have completed the process, thanks to @Thomas Schmitt's invaluable tip.

Here is the complete process.

Overview

You must modify three files on the original Red Hat DVD:

  • isolinux/isolinux.cfg
  • EFI/BOOT/grub.cfg
  • images/efiboot.img

Decide on a volume label that you want to use. The volume label should be shorter than 14 characters.

Then use genisoimage to build the new ISO with that volume label, use isohybrid --uefi to make it UEFI compatible, and use implantisomd5 to update the correct checksum

Step-by-Step

I have scripted this, but the script is very specific to our situation, so no point in posting it.

  • Mount the original Red Hat DVD using fuseiso.
  • Set an environment variable VOLUMELABEL to your chosen volume label.
  • Copy the three files you need to modify to another location.

Edit the isolinux.cfg file. This file will be used for BIOS booting.

  • Edit the isolinux.cfg file, adding the argument to the first two lines that start with append: ks=hd:LABEL=$VOLUMELABEL:/ks.cfg.
  • Update the inst.stage2 argument everywhere you find it (probably four places) to read: inst.stage2=hd:LABEL=$VOLUMELABEL.
  • You may also want to remove the menu default entry so your image defaults to Install instead of Test & Install.

Edit the grub.cfg file. This file will be used for EFI booting. Note that this file (and everything in EFI/BOOT) will exist twice: once on the regular DVD image, and also inside the efiboot.img file.

  • Add the same argument to the first two lines that start with linuxefi.
  • Update the inst.stage2 argument everywhere you find it (probably four places) to read: inst.stage2=hd:LABEL=$VOLUMELABEL.
  • IMPORTANT AND EASY TO OVERLOOK: Also edit the line that starts with search in grub.cfg.

Edit the efiboot.img file. This will actually be booted by EFI.

  • Mount your copy of the efiboot.img file. Unfortunately, I have not found a way to do this with fusermount, so you have to be root to do this.
  • Copy the modified grub.cfg into the efiboot.img file under EFI/BOOT
  • Umount your copy of the efiboot.img file.

Now you can generate the ISO image.

The arguments to genisoimage are position-sensitive. Key points: I base the image on the original mounted ISO file (mounted as $TMPDIR), then use -m to exclude the three modified files, and use graft points to insert the modifications, as well as the kickstart file, into the image. In my image, I also removed the directory addons.

genisoimage \
  -untranslated-filenames \
  -graft-points \
  -rational-rock \
  -v \
  -translation-table \
  -input-charset "default" \
  -J \
  -joliet-long \
  -V $VOLLABEL -A $VOLLABEL -volset $VOLLABEL \
  -b isolinux/isolinux.bin \
  -c isolinux/boot.cat \
  -no-emul-boot \
  -boot-load-size 4 \
  -boot-info-table \
  -eltorito-alt-boot \
  -efi-boot images/efiboot.img \
  -no-emul-boot \
  -quiet \
  -o $THISDIR/$VOLLABEL.iso \
  -m $TMPDIR/EFI/BOOT/grub.cfg \
  -m $TMPDIR/isolinux/isolinux.cfg \
  -m $TMPDIR/images/efiboot.img \
  -m addons \
  $TMPDIR \
  EFI/BOOT/grub.cfg=$TMPGRAFT/grub.cfg \
  isolinux/isolinux.cfg=$TMPGRAFT/isolinux.cfg \
  images/efiboot.img=$TMPGRAFT/efiboot.img \
  $KICKSTARTFILE=$KICKSTARTDIR/$KICKSTARTFILE

Then use isohybrid --uefi and implantisomd5:

isohybrid --uefi $THISDIR/$VOLUMELABEL.iso
implantisomd5 $THISDIR/$VOLUMELABEL.iso

Based on answers from Thomas and Kevin I managed to get a working procedure based on xorriso, which is available from EPEL. My goals were similar to Kevin's:

  • Use a stock RHEL 7 installation media
  • Implant a kickstart and configuration scripts into the resulting image
  • Select the kickstart automatically for installation
  • Produce an image compatible with UEFI boot
  • Make the procedure scripted

Tested successfully with RHEL 7.5 and 7.6.

Step by step procedure

  1. Have xorriso installed from EPEL.
  2. Have RHEL 7 installation DVD downloaded in $ISO_SRC.
  3. Have your kickstart (ks.cfg) and scripts ready in $SCRIPTS.
  4. Extract files, which need to be customized, from ISO into $FILES:

    • isolinux/isolinux.cfg
    • EFI/BOOT/grub.cfg
    • images/efiboot.img
  5. Modify the *.cfg boot configuration files in $FILES:

    Note: I do not modify the disc label (or volume ID) intentionally. It is space sensitive (potentially any special characters) and I found it easier to reuse it instead of replacing.

    • add kickstart parameter to boot command

      '(.*)(hd:LABEL=\S+)(.*)''\1\2 inst.ks=\2:/$SCRIPTS/ks.cfg\3'

    • set default boot menu for legacy boot in isolinux.cfg

      '^\s*menu default\s*\n'''

    • set default boot menu for UEFI boot in grub.cfg

      'set default=.*''set default="0"'

  6. Modify the UEFI boot image images/efiboot.img in $FILES:

    • mount it in a temporary directory
    • copy the UEFI boot menu $FILES/EFI/BOOT/grub.cfg into it
    • unmount
  7. Build your customized image:

    xorriso \
      -indev "$ISO_SRC" \
      -map "$SCRIPTS" /"$SCRIPTS" \
      -map "$FILES" / \
      -boot_image any replay \
      -outdev "$ISO_DST"
    

    The command copies contents of the source ISO image $ISO_SRC, merges local directories' contents and replays all boot settings from the source into the destination image $ISO_DST. No further steps are required.

Note on geniso method

I had hard time with following the geniso method as described by Kevin. I managed to produce the output ISO image, however, geniso was complaining about not being able to modify the isolinux.bin image, hence I had to extract it too. This is not mentioned in the procedure.

I faced the wall on enabling the UEFI boot with isohybrid. It returned an error I did not find a workaround for:

$ isohybrid --uefi rhel-custom.iso
isohybrid: rhel-custom.iso: boot loader does not have an isolinux.bin hybrid 
signature. Note that isolinux-debug .bin does not support hybrid booting

Ansible role

I did create an Ansible role, which performs the task: build_boot_iso.