From 52e2b3719c1526c3980f72a7ec53bc8863eb3b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20L=C3=B6ser?= Date: Tue, 28 Feb 2023 13:16:40 +0100 Subject: [PATCH] New PXE loader "Grub2 UEFI SecureBoot (target OS)" This feature consists of two patches, one for foreman and one for smart-proxy. This patch introduces a new loader of kind `:PXEGrub2TargetOS` which allows to provide host-specific Network Bootstrap Programs (NPB) in order to enable network based installations for SecureBoot-enabled hosts. SecureBoot expects to follow a chain of trust from the start of the host to the loading of Linux kernel modules. The very first shim that is loaded basically determines which distribution is allowed to be booted or kexec'ed until next reboot. The existing "Grub2 UEFI SecureBoot" is not sufficiant as it limits the possible installations to the vendor of the Foreman (Smart Proxy) host system. Providing a shim and GRUB2 by the vendor of the to-be-installed operating systems allows Foreman to install any operating system on SecureBoot-enabled hosts over network. To achieve this, the host's DHCP filename option is set to a shim path in a directory that is host-specific (contains MAC address). Corresponding shim and GRUB2 bianries are copied into that directory along with the generated GRUB2 confiugration files as we know from "Grub2 UEFI". Required binaries must be provided once in `/usr/local/share/bootloader-universe//`. These binaries can be manually retrieved from the installation media and is not part of this patchset. Full example: ------------- [root@vm ~]# hammer host info --id 241 | grep -E "(MAC address|Operating System)" MAC address: 00:50:56:b4:75:5e Operating System: Ubuntu 22.04 LTS [root@vm ~]# tree /usr/local/share/bootloader-universe/ /usr/local/share/bootloader-universe/ |-- centos | |-- grubx64.efi | `-- shimx64.efi `-- ubuntu |-- grubx64.efi `-- shimx64.efi [root@vm ~]# hammer host update --id 241 --build true [root@vm ~]# tree /var/lib/tftpboot/grub2/00-50-56-b4-75-5e/ /var/lib/tftpboot/grub2/00-50-56-b4-75-5e/ |-- grub.cfg |-- grub.cfg-00:50:56:b4:75:5e |-- grub.cfg-01-00-50-56-b4-75-5e |-- grubx64.efi |-- shimx64.efi `-- targetos [root@vm ~]# grep -B2 00-50-56-b4-75-5e /var/lib/dhcpd/dhcpd.leases hardware ethernet 00:50:56:b4:75:5e; fixed-address 192.168.145.84; supersede server.filename = "grub2/00-50-56-b4-75-5e/shimx64.efi"; [root@vm ~]# pesign -S -i /var/lib/tftpboot/grub2/00-50-56-b4-75-5e/grubx64.efi | grep Canonical The signer's common name is Canonical Ltd. Secure Boot Signing (2021 v1) --- modules/tftp/server.rb | 21 +++++++++++++++++++++ modules/tftp/tftp_api.rb | 14 ++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/modules/tftp/server.rb b/modules/tftp/server.rb index 98218d8bd..365d8ec90 100644 --- a/modules/tftp/server.rb +++ b/modules/tftp/server.rb @@ -108,6 +108,27 @@ def pxeconfig_file(mac) end end + class Pxegrub2targetos < Server + def setup_bootloader(mac, os) + FileUtils.mkdir_p(pxeconfig_dir(mac)) + FileUtils.cp "/usr/local/share/bootloader-universe/#{os}/shimx64.efi", pxeconfig_dir(mac) + "shimx64.efi" + FileUtils.cp "/usr/local/share/bootloader-universe/#{os}/grubx64.efi", pxeconfig_dir(mac) + "grubx64.efi" + File.open(pxeconfig_dir(mac) + "/targetos", 'w') { |f| f.write(os) } + end + + def pxeconfig_dir(mac) + "#{path}/grub2/" + mac.tr(':', '-').downcase + "/" + end + + def pxe_default(mac) + ["#{pxeconfig_dir}/grub.cfg", "#{pxeconfig_dir(mac)}/grub.cfg"] + end + + def pxeconfig_file(mac) + ["#{pxeconfig_dir(mac)}/grub.cfg", "#{pxeconfig_dir(mac)}/grub.cfg-01-" + mac.tr(':', '-').downcase, "#{pxeconfig_dir(mac)}/grub.cfg-#{mac.downcase}"] + end + end + class Ztp < Server def pxeconfig_dir "#{path}/ztp.cfg" diff --git a/modules/tftp/tftp_api.rb b/modules/tftp/tftp_api.rb index 1bc6276c7..fadb76209 100644 --- a/modules/tftp/tftp_api.rb +++ b/modules/tftp/tftp_api.rb @@ -8,7 +8,7 @@ class Api < ::Sinatra::Base helpers ::Proxy::Helpers authorize_with_trusted_hosts authorize_with_ssl_client - VARIANTS = ["Syslinux", "Pxelinux", "Pxegrub", "Pxegrub2", "Ztp", "Poap", "Ipxe"].freeze + VARIANTS = ["Syslinux", "Pxelinux", "Pxegrub", "Pxegrub2", "Pxegrub2targetos", "Ztp", "Poap", "Ipxe"].freeze helpers do def instantiate(variant, mac = nil) @@ -23,6 +23,12 @@ def create(variant, mac) log_halt(400, "TFTP: Failed to create pxe config file: ") { tftp.set(mac, (params[:pxeconfig] || params[:syslinux_config])) } end + def create_targetos(variant, mac, os) + tftp = instantiate variant, mac + log_halt(400, "TFTP: Failed to setup target OS bootloader directory: ") { tftp.setup_bootloader(mac, os)} + log_halt(400, "TFTP: Failed to create pxe config file: ") { tftp.set(mac, (params[:pxeconfig] || params[:syslinux_config])) } + end + def delete(variant, mac) tftp = instantiate variant, mac log_halt(400, "TFTP: Failed to delete pxe config file: ") { tftp.del(mac) } @@ -48,7 +54,11 @@ def create_default(variant) end post "/:variant/:mac" do |variant, mac| - create variant, mac + unless params[:targetos].nil? + create_targetos variant, mac, params[:targetos] + else + create variant, mac + end end delete "/:variant/:mac" do |variant, mac|