Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #36834 - Add SecureBoot support for arbitrary operating systems to "Grub2 UEFI" PXE loaders #9864

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion app/models/concerns/orchestration/dhcp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ def build_dhcp_record(record_mac)
end
end

def dhcp_filename(record_mac)
filename = operatingsystem.boot_filename(host)
if filename.include? "@@subdir@@"
if host.subnet&.tftp&.has_capability?(:TFTP, :bootloader_universe)
filename = filename.gsub("@@subdir@@", "host-config/#{record_mac.tr(':', '-').downcase}")
filename = filename.gsub(/\/grub\w*\.efi$/, "/boot.efi")
return filename.gsub(/\/shim\w*\.efi$/, "/boot-sb.efi")
else
return filename.gsub("@@subdir@@/", "")
end
end
filename
end

# returns a hash of dhcp record settings
def dhcp_attrs(record_mac)
raise ::Foreman::Exception.new(N_("DHCP not supported for this NIC")) unless dhcp?
Expand All @@ -124,7 +138,7 @@ def dhcp_attrs(record_mac)

if provision?
dhcp_attr[:nextServer] = boot_server unless host.pxe_loader == 'None'
filename = operatingsystem.boot_filename(host)
filename = dhcp_filename(record_mac)
dhcp_attr[:filename] = filename if filename.present?
if jumpstart?
jumpstart_arguments = os.jumpstart_params host, model.vendor_class
Expand Down
8 changes: 7 additions & 1 deletion app/models/concerns/orchestration/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,13 @@ def setTFTP(kind)
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)
proxy.set(kind, mac_addr, {
:pxeconfig => content,
:targetos => host.operatingsystem.name.downcase,
:release => host.operatingsystem.release,
:arch => host.arch.name,
:bootfile_suffix => host.arch.bootfilename_efi,
})
end
end
else
Expand Down
12 changes: 6 additions & 6 deletions app/models/concerns/pxe_loader_support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module PxeLoaderSupport
PXE_KINDS = {
:PXELinux => /^(pxelinux.*|PXELinux (BIOS|UEFI))$/,
:PXEGrub => /^(grub\/|Grub UEFI).*/,
:PXEGrub2 => /^(grub2\/|Grub2 (BIOS|UEFI|ELF)|http.*grub2\/).*/,
goarsna marked this conversation as resolved.
Show resolved Hide resolved
:PXEGrub2 => /(^Grub2 (BIOS|UEFI|ELF).*|\/?grub2\/)/,
:iPXE => /^((iPXE|http.*\/ipxe-).*|ipxe\.efi|undionly\.kpxe)$/,
}.with_indifferent_access.freeze

Expand All @@ -26,11 +26,11 @@ def all_loaders_map(precision = 'x64', httpboot_host = "httpboot_host")
"Grub UEFI" => "grub/grub#{precision}.efi",
"Grub2 BIOS" => "grub2/grub#{precision}.0",
"Grub2 ELF" => "grub2/grub#{precision}.elf",
"Grub2 UEFI" => "grub2/grub#{precision}.efi",
"Grub2 UEFI SecureBoot" => "grub2/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",
"Grub2 UEFI" => "@@subdir@@/grub2/grub#{precision}.efi",
"Grub2 UEFI SecureBoot" => "@@subdir@@/grub2/shim#{precision}.efi",
"Grub2 UEFI HTTP" => "http://#{httpboot_host}/httpboot/@@subdir@@/grub2/grub#{precision}.efi",
"Grub2 UEFI HTTPS" => "https://#{httpboot_host}/httpboot/@@subdir@@/grub2/grub#{precision}.efi",
"Grub2 UEFI HTTPS SecureBoot" => "https://#{httpboot_host}/httpboot/@@subdir@@/grub2/shim#{precision}.efi",
"iPXE Embedded" => nil, # renders directly as foreman_url('iPXE')
"iPXE UEFI HTTP" => "http://#{httpboot_host}/httpboot/ipxe-#{precision}.efi",
"iPXE Chain BIOS" => "undionly-ipxe.0",
Expand Down
4 changes: 4 additions & 0 deletions app/services/proxy_api/tftp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ def initialize(args)
# [+mac+] : MAC address
# [+args+] : Hash containing
# :pxeconfig => String containing the configuration
# :targetos => String containing the lowercase operating system name
# :release => String containing the operating system major and minor version
# :arch => String containing the operating system architecture
# :bootfile_suffix => String containing the architecture specific boot filename suffix
# Returns : Boolean status
def set(kind, mac, args)
parse(post(args, "#{kind}/#{mac}"))
Expand Down
4 changes: 4 additions & 0 deletions test/factories/architecture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
trait :for_snapshots_x86_64 do
name { 'x86_64' }
end

trait :x64 do
name { 'x64' }
end
end
end
11 changes: 11 additions & 0 deletions test/factories/operatingsystem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@
title { 'Red Hat Enterprise Linux 7.5' }
end

factory :rhel9, class: Redhat do
name { 'RHEL' }
major { '9' }
minor { '0' }
type { 'Redhat' }
title { 'Red Hat Enterprise Linux 9.0' }
architectures { [FactoryBot.build(:architecture, :x64)] }
media { [FactoryBot.build(:rhel_for_snapshots)] }
ptables { [FactoryBot.build(:ptable, name: 'ptable')] }
end

factory :for_snapshots_centos_7_0, class: Redhat do
name { 'CentOS' }
major { '7' }
Expand Down
29 changes: 17 additions & 12 deletions test/models/concerns/pxe_loader_support_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,28 @@ def setup
assert_equal :PXEGrub, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for given filename" do
@host.pxe_loader = "grub2/grubx64.efi"
test "PXEGrub2 is found for grubx64.elf filename" do
@host.pxe_loader = "grub2/grubx64.elf"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for grubx64.0 filename" do
@host.pxe_loader = "grub2/grubx64.0"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for grubx64.efi filename" do
@host.pxe_loader = "host-config/#{@host.mac.tr(':', '-')}/grub2/grubx64.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for shimx64.efi filename" do
@host.pxe_loader = "grub2/shimx64.efi"
@host.pxe_loader = "host-config/#{@host.mac.tr(':', '-')}/grub2/shimx64.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for shimia32.efi filename" do
@host.pxe_loader = "grub2/shimia32.efi"
@host.pxe_loader = "host-config/#{@host.mac.tr(':', '-')}/grub2/shimia32.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

Expand Down Expand Up @@ -88,22 +98,17 @@ def setup
end

test "PXEGrub2 is found for http://smart_proxy/tftp/grub2/grubx64.efi filename" do
@host.pxe_loader = "http://smart_proxy/tftp/grub2/grubx64.efi"
@host.pxe_loader = "http://smart_proxy/tftp/host-config/#{@host.mac.tr(':', '-')}/grub2/grubx64.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for https://smart_proxy/tftp/grub2/grubx64.efi filename" do
@host.pxe_loader = "https://smart_proxy/tftp/grub2/grubx64.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for https://smart_proxy/tftp/grub2/shimx64.efi filename" do
@host.pxe_loader = "https://smart_proxy/tftp/grub2/shimx64.efi"
@host.pxe_loader = "https://smart_proxy/tftp/host-config/#{@host.mac.tr(':', '-')}/grub2/grubx64.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

test "PXEGrub2 is found for https://smart_proxy/tftp/grub2/shimx64.efi filename" do
@host.pxe_loader = "https://smart_proxy/tftp/grub2/shimx64.efi"
@host.pxe_loader = "https://smart_proxy/tftp/host-config/#{@host.mac.tr(':', '-')}/grub2/shimx64.efi"
assert_equal :PXEGrub2, @subject.pxe_loader_kind(@host)
end

Expand Down
4 changes: 2 additions & 2 deletions test/models/operatingsystem_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -379,13 +379,13 @@ class OperatingsystemTest < ActiveSupport::TestCase
test 'should be the smart proxy and httpboot port for UEFI HTTP' do
SmartProxy.any_instance.expects(:setting).with(:HTTPBoot, 'http_port').returns(1234)
host = FactoryBot.build(:host, :managed, :with_tftp_and_httpboot_subnet, pxe_loader: 'Grub2 UEFI HTTP')
assert_match(%r{http://somewhere.*net:1234/httpboot/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host))
assert_match(%r{http://somewhere.*net:1234/httpboot/@@subdir@@/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host))
end

test 'should be the smart proxy and httpboot port for UEFI HTTPS' do
SmartProxy.any_instance.expects(:setting).with(:HTTPBoot, 'https_port').returns(1235)
host = FactoryBot.build(:host, :managed, :with_tftp_and_httpboot_subnet, pxe_loader: 'Grub2 UEFI HTTPS')
assert_match(%r{https://somewhere.*net:1235/httpboot/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host))
assert_match(%r{https://somewhere.*net:1235/httpboot/@@subdir@@/grub2/grubx64.efi}, host.operatingsystem.boot_filename(host))
end

test 'should not raise an error without httpboot feature for PXE' do
Expand Down
6 changes: 4 additions & 2 deletions test/models/orchestration/dhcp_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,8 @@ def host_with_loader(loader)

h.build = true
assert h.valid?, h.errors.messages.to_s
assert_equal ["dhcp_remove_aa:bb:cc:dd:ee:f1", "dhcp_create_aa:bb:cc:dd:ee:f1"], h.queue.task_ids
assert_includes h.queue.task_ids, "dhcp_remove_aa:bb:cc:dd:ee:f1"
assert_includes h.queue.task_ids, "dhcp_create_aa:bb:cc:dd:ee:f1"
end

test "when an existing host trigger a 'rebuild', its dhcp records should not be updated if valid dhcp records are found" do
Expand All @@ -383,7 +384,8 @@ def host_with_loader(loader)
h.build = true
assert h.valid?
assert_empty h.errors
assert_equal ["dhcp_create_aa:bb:cc:dd:ee:f1"], h.queue.task_ids
assert_includes h.queue.task_ids, "dhcp_create_aa:bb:cc:dd:ee:f1"
assert_not_includes h.queue.task_ids, "dhcp_remove_aa:bb:cc:dd:ee:f1"
end

test "when an existing host change its bmc mac address, its dhcp record should be updated" do
Expand Down
22 changes: 19 additions & 3 deletions test/models/orchestration/tftp_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,26 +145,42 @@ class TFTPOrchestrationTest < ActiveSupport::TestCase
),
]
end
let(:os) do
FactoryBot.create(:rhel9)
end
let(:host) do
FactoryBot.create(:host,
:with_tftp_orchestration,
:subnet => subnet,
:interfaces => interfaces,
:build => true,
:location => subnet.locations.first,
:organization => subnet.organizations.first)
:organization => subnet.organizations.first,
:operatingsystem => os)
end

test '#setTFTP should provision tftp for all bond child macs' do
ProxyAPI::TFTP.any_instance.expects(:set).with(
'PXEGrub2',
'00:53:67:ab:dd:00',
:pxeconfig => 'Template'
{
:pxeconfig => 'Template',
:targetos => os.name.downcase.to_s,
:release => host.operatingsystem.release,
:arch => host.arch.name,
:bootfile_suffix => host.arch.bootfilename_efi,
}
).once
ProxyAPI::TFTP.any_instance.expects(:set).with(
'PXEGrub2',
'00:53:67:ab:dd:01',
:pxeconfig => 'Template'
{
:pxeconfig => 'Template',
:targetos => os.name.downcase.to_s,
:release => host.operatingsystem.release,
:arch => host.arch.name,
:bootfile_suffix => host.arch.bootfilename_efi,
}
).once
host.provision_interface.stubs(:generate_pxe_template).returns('Template')
host.provision_interface.send(:setTFTP, 'PXEGrub2')
Expand Down
Loading