Skip to content

Commit

Permalink
Fixes #36834 - New PXE loader "Grub2 UEFI SecureBoot (target OS)"
Browse files Browse the repository at this point in the history
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 sufficient as it limits the
possible installations to the vendor of the Foreman (Smart Proxy) host
system.

Providing shim and GRUB2 by the vendor of the to-be-installed
operating system 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 binaries are copied into that directory
along with the generated GRUB2 configuration files as we know from
"Grub2 UEFI".

The required binaries must be provided once in the so called "bootloader
universe". This directory can be configured via the settings file
`/etc/foreman-proxy/settings.d/tftp.yml` and defaults to
`/usr/local/share/bootloader-universe/<os>/`. These binaries can be
manually retrieved from the installation media and is not part of this
patch set.

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)
  • Loading branch information
Jan Löser authored and goarsna committed Oct 16, 2023
1 parent d329c93 commit e003197
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 2 deletions.
4 changes: 4 additions & 0 deletions app/models/concerns/orchestration/dhcp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ def dhcp_attrs(record_mac)
if provision?
dhcp_attr[:nextServer] = boot_server unless host.pxe_loader == 'None'
filename = operatingsystem.boot_filename(host)
if filename.include? "@@subdir@@"
mac = host.mac.downcase
filename = filename.gsub("@@subdir@@", mac.tr(':', '-').downcase)
end
dhcp_attr[:filename] = filename if filename.present?
if jumpstart?
jumpstart_arguments = os.jumpstart_params host, model.vendor_class
Expand Down
19 changes: 17 additions & 2 deletions app/models/concerns/orchestration/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,20 @@ def default_pxe_render(kind)
# Adds the host to the forward and reverse TFTP zones
# +returns+ : Boolean true on success
def setTFTP(kind)
content = generate_pxe_template(kind)
if kind.to_s == "PXEGrub2TargetOS"
content = generate_pxe_template("PXEGrub2".to_sym)
else
content = generate_pxe_template(kind)
end
if content
logger.info "Deploying TFTP #{kind} configuration for #{host.name}"
each_unique_feasible_tftp_proxy do |proxy|
mac_addresses_for_provisioning.each do |mac_addr|
proxy.set(kind, mac_addr, :pxeconfig => content)
targetos = nil
if kind.to_s == "PXEGrub2TargetOS"
targetos = host.operatingsystem.name.downcase
end
proxy.set(kind, mac_addr, {:pxeconfig => content, :targetos => targetos})
end
end
else
Expand Down Expand Up @@ -132,6 +140,9 @@ def validate_tftp
return unless tftp? || tftp6?
return unless host.operatingsystem
pxe_kind = host.operatingsystem.pxe_loader_kind(host)
if pxe_kind.to_s == "PXEGrub2TargetOS"
pxe_kind = "PXEGrub2".to_sym
end
if pxe_kind && host.provisioning_template({:kind => pxe_kind}).nil?
failure _("No %{template_kind} templates were found for this host, make sure you define at least one in your %{os} settings or change PXE loader") %
{ :template_kind => pxe_kind, :os => host.operatingsystem }
Expand All @@ -146,7 +157,11 @@ def queue_tftp
end

def queue_tftp_create
pxe_kind = host.operatingsystem.pxe_loader_kind(host)
host.operatingsystem.template_kinds.each do |kind|
if kind.to_s == "PXEGrub2" && pxe_kind.to_s == "PXEGrub2TargetOS"
queue.create(:name => _("Deploy TFTP SecureBoot %{kind} config for %{host}") % {:kind => kind, :host => self}, :priority => 20, :action => [self, :setTFTP, pxe_kind])
end
queue.create(:name => _("Deploy TFTP %{kind} config for %{host}") % {:kind => kind, :host => self}, :priority => 20, :action => [self, :setTFTP, kind])
end
return unless build
Expand Down
2 changes: 2 additions & 0 deletions app/models/concerns/pxe_loader_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module PxeLoaderSupport
PXE_KINDS = {
:PXELinux => /^(pxelinux.*|PXELinux (BIOS|UEFI))$/,
:PXEGrub => /^(grub\/|Grub UEFI).*/,
:PXEGrub2TargetOS => /^(Grub2 UEFI SecureBoot \(target OS\))$/,
:PXEGrub2 => /^(grub2\/|Grub2 (BIOS|UEFI|ELF)|http.*grub2\/).*/,
:iPXE => /^((iPXE|http.*\/ipxe-).*|ipxe\.efi|undionly\.kpxe)$/,
}.with_indifferent_access.freeze
Expand All @@ -28,6 +29,7 @@ def all_loaders_map(precision = 'x64', httpboot_host = "httpboot_host")
"Grub2 ELF" => "grub2/grub#{precision}.elf",
"Grub2 UEFI" => "grub2/grub#{precision}.efi",
"Grub2 UEFI SecureBoot" => "grub2/shim#{precision}.efi",
"Grub2 UEFI SecureBoot (target OS)" => "grub2/@@subdir@@/shim#{precision}.efi",
"Grub2 UEFI HTTP" => "http://#{httpboot_host}/httpboot/grub2/grub#{precision}.efi",
"Grub2 UEFI HTTPS" => "https://#{httpboot_host}/httpboot/grub2/grub#{precision}.efi",
"Grub2 UEFI HTTPS SecureBoot" => "https://#{httpboot_host}/httpboot/grub2/shim#{precision}.efi",
Expand Down
1 change: 1 addition & 0 deletions app/services/proxy_api/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ def initialize(args)
# [+mac+] : MAC address
# [+args+] : Hash containing
# :pxeconfig => String containing the configuration
# :targetos => String containing the lowercase target operating system name or nil
# Returns : Boolean status
def set(kind, mac, args)
parse(post(args, "#{kind}/#{mac}"))
Expand Down

0 comments on commit e003197

Please sign in to comment.