Lukas' Notes

PXE booting my custom Alma Linux ISO

· Lukas Hartl

Introduction

In my last blog post, we have already built an ISO image containing the anaconda installer using livemedia-creator.

Of course, it is far too annoying if you have to mount the ISO again after every build, etc.

That is why we now want to automatically start an installation via PXE after every new build.

The idea of this article is to show which services are necessary for this and how to provide them under AlmaLinux 9.4.

Important: Layer 2

Before you start setting up a DHCP server, you should be aware that there may be problems if a standard DHCP server is already running in your home network. My recommendation is to operate this build- and test infrastructure in a separate Layer 2. I will use a separte VLAN for this.

Building for PXE booting

livemedia-creator --make-pxe-live \
  --iso AlmaLinux-9.4-x86_64-boot.iso \
  --ks lukas.ks \
  --nomacboot \
  --resultdir /root/isobuild/result_pxe \
  --project LukasOS \
  --releasever 2024.1

This will result in the result_pxe directory containing the following files:

[root@localhost isobuild]# ls -1 --size -h result_pxe/
total 4.8G
 4.0K PXE_CONFIG
  58M initramfs-5.14.0-427.37.1.el9_4.x86_64.img
1011M live-rootfs.squashfs.img
 3.8G lmc-disk-yicryf70.img
  13M vmlinuz-5.14.0-427.37.1.el9_4.x86_64

Netbooting under Linux

This is the sequence for booting a Linux using the network (simplified):

  1. DHCP:
    • The UEFI initializes the network stack and requests a DHCP Lease.
    • The DHCP server offers a DHCP lease and sets the following DHCP options 1:
      • 66: tftp-server-name: name or IP address of the TFTP Server
      • 67: boot-file-name: filename to load from the TFTP Server
  2. TFTP Boot:
    • The PXEClient will connect to the tftp server (DHCP Option 66) and request the filename given in option 67.
  3. Boot Procedure:
    • The PXEClient will hand over to shimx64.efi.
    • shimx64.efi is required for Secure Boot to work. Shim is signed by the “Microsoft 3rd Party UEFI CA”.
    • shimx64.efi will then load grubx64.efi.
    • grubx64.efi will load grub.cfg via TFTP.
    • grubx64.efi will hand over to vmlinuz and initramfs.
  4. initramfs (dracut)
    • it will evalute the kernel command line argument (root)
    • the initramfs will mount the soon-to-be root under /sysroot
    • and then it will switch-root to /sysroot

Setting up DHCP

To run network based installations we need a DHCP Server that tells the clients which file to boot and a TFTP Server which offers the files to boot.

Under CentOS/Alma/Rocky Linux 9 the default is still the isc-dhcp-server. But the ISC has already moved their development to the new kea server. 2

Fedora 39 and 40 already provide kea in the default package repositories 3, hence I will focus on kea.

To install kea we need to enable EPEL first:

dnf install epel-release
crb enable
dnf list kea*

The most important configuration is the following section inside /etc/kea/kea-dhcp4.conf:

{
  // ...
    "client-classes": [
      {
        "name": "pxe_uefi_x64",
        "test": "substring(option[60].hex,0,9) == 'PXEClient' and option[93].hex == 0x0007",
        "option-data": [
          {
            "name": "tftp-server-name",
            "data": "192.168.99.3"
          },
          {
            "name": "boot-file-name",
            "data": "shimx64.efi"
          }
        ]
      }
    ]
  // ...
}

Setting up TFTP

yum install tftp-server
firewall-cmd --add-service=tftp
systemctl enable tftp.socket

After the installation anda activation, in.tftpd will serve the directory /var/lib/tftpboot.

Then we can copy the files to the directory:

cp /boot/efi/EFI/almalinux/shimx64.efi /var/lib/tftpboot/
cp /boot/efi/EFI/almalinux/grubx64.efi /var/lib/tftpboot/
cp /root/isobuild/result_pxe/initramfs-*.x86_64.img /var/lib/tftpboot/
cp /root/isobuild/result_pxe/initramfs-*.x86_64.img /var/lib/tftpboot/
cp /root/isobuild/result_pxe/vmlinuz-*.x86_64  /var/lib/tftpboot/

We also have to set the correct permissions:

chmod -R 0755 /var/lib/tftpboot/

Now we are only missing the configuration for grub:

[root@localhost ~]# cat /var/lib/tftpboot/grub.cfg 

function load_video {
  insmod efi_gop
  insmod efi_uga
  insmod video_bochs
  insmod video_cirrus
  insmod all_video
}

load_video
set gfxpayload=keep
insmod gzio

menuentry 'Netboot my custom AlmaLinux' --class gnu-linux --class gnu --class os {
  linux $fw_path/vmlinuz ip=dhcp root=live:http://192.168.99.3/live.img
  initrd $fw_path/initramfs.img
}

menuentry 'Leave GRUB' {
  exit
}

Setting up HTTP

I will be using caddy as a small HTTP server.

[root@localhost ~]# yum install caddy

[root@localhost ~]# cat /etc/caddy/Caddyfile
http:// {
  root * /var/www

  file_server
}

And we have to copy the built image:

cp /root/isobuild/result_pxe/live-rootfs.squashfs.img /var/www/live.img 

Booting via PXE

That’s it, now we are ready to boot via PXE.

Debugging DHCP Leases

[root@localhost kea]# tcpdump -i ens18 -n port 67 and port 68
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens18, link-type EN10MB (Ethernet), snapshot length 262144 bytes
12:41:48.010159 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from bc:24:11:5a:68:a7, length 347
12:41:51.969200 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from bc:24:11:5a:68:a7, length 347
12:41:59.969826 IP 0.0.0.0.bootpc > 255.255.255.255.bootps: BOOTP/DHCP, Request from bc:24:11:5a:68:a7, length 347