Skip to content

Commit

Permalink
feat: generate iso's with both UKI and grub
Browse files Browse the repository at this point in the history
Starting with Talos 1.10, the default generated ISO's will use GRUB for
BIOS boot and sd-boot for EFI boot.

Fixes: #10192

Signed-off-by: Noel Georgi <[email protected]>
  • Loading branch information
frezbo committed Jan 24, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 75673b6 commit cd5e549
Showing 15 changed files with 284 additions and 134 deletions.
12 changes: 11 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2025-01-22T17:37:55Z by kres 3075de9.
# Generated on 2025-01-24T14:30:35Z by kres 3075de9.

name: default
concurrency:
@@ -2199,6 +2199,16 @@ jobs:
WITH_UEFI: "false"
run: |
sudo -E make e2e-qemu
- name: e2e-bios-iso
env:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios-iso
IMAGE_REGISTRY: registry.dev.siderolabs.io
SHORT_INTEGRATION_TEST: "yes"
VIA_MAINTENANCE_MODE: "true"
WITH_ISO: "true"
WITH_UEFI: "false"
run: |
sudo -E make e2e-qemu
- name: e2e-disk-image
env:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image
12 changes: 11 additions & 1 deletion .github/workflows/integration-misc-2-cron.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
#
# Generated on 2024-12-18T13:55:17Z by kres b9507d6.
# Generated on 2025-01-24T14:30:35Z by kres 3075de9.

name: integration-misc-2-cron
concurrency:
@@ -99,6 +99,16 @@ jobs:
WITH_UEFI: "false"
run: |
sudo -E make e2e-qemu
- name: e2e-bios-iso
env:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios-iso
IMAGE_REGISTRY: registry.dev.siderolabs.io
SHORT_INTEGRATION_TEST: "yes"
VIA_MAINTENANCE_MODE: "true"
WITH_ISO: "true"
WITH_UEFI: "false"
run: |
sudo -E make e2e-qemu
- name: e2e-disk-image
env:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image
10 changes: 10 additions & 0 deletions .kres.yaml
Original file line number Diff line number Diff line change
@@ -838,6 +838,16 @@ spec:
SHORT_INTEGRATION_TEST: yes
WITH_UEFI: false
IMAGE_REGISTRY: registry.dev.siderolabs.io
- name: e2e-bios-iso
command: e2e-qemu
withSudo: true
environment:
GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios-iso
SHORT_INTEGRATION_TEST: yes
WITH_UEFI: false
VIA_MAINTENANCE_MODE: true
WITH_ISO: true
IMAGE_REGISTRY: registry.dev.siderolabs.io
- name: e2e-disk-image
command: e2e-qemu
withSudo: true
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -456,7 +456,7 @@ secureboot-iso: image-secureboot-iso ## Builds UEFI only ISO which uses UKI and

.PHONY: secureboot-installer
secureboot-installer: ## Builds UEFI only installer which uses UKI and push it to the registry.
@$(MAKE) image-secureboot-installer IMAGER_ARGS="--base-installer-image $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG) $(IMAGER_ARGS)"
@$(MAKE) image-secureboot-installer IMAGER_ARGS="--base-installer-image $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG) --extra-kernel-arg=console=ttyS0 $(IMAGER_ARGS)"
@for platform in $(subst $(,),$(space),$(PLATFORM)); do \
arch=$$(basename "$${platform}") && \
crane push $(ARTIFACTS)/installer-$${arch}-secureboot.tar $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG)-$${arch}-secureboot ; \
6 changes: 6 additions & 0 deletions hack/release.toml
Original file line number Diff line number Diff line change
@@ -92,6 +92,12 @@ The iqn can be read by `talosctl read /etc/iscsi/initiatorname.iscsi`
Talos now generates `/etc/nvme/hostnqn` and `/etc/nvme/hostid` files based on the node identity which is tied to the lifecycle of the node.
The NQN can be read by `talosctl read /etc/nvme/hostnqn`
"""

[notes.iso]
title = "ISO"
description = """\
Talos starting with 1.10 will have ISO's that will use GRUB only for legacy BIOS and systemd-boot for modern UEFI systems.
"""

[make_deps]
4 changes: 3 additions & 1 deletion hack/test/e2e-iso.sh
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@ CLUSTER_NAME=e2e-iso

NODE="172.20.2.2"

INSTALLER_IMAGE=${INSTALLER_IMAGE}-amd64-secureboot # we don't use secureboot part here, but this installer contains UKIs

function create_cluster {
build_registry_mirrors

@@ -24,7 +26,7 @@ function create_cluster {
--cpus=2.0 \
--cidr=172.20.2.0/24 \
--with-apply-config \
--install-image=${REGISTRY:-ghcr.io}/siderolabs/installer:${TAG} \
--install-image="${INSTALLER_IMAGE}" \
--cni-bundle-url=${ARTIFACTS}/talosctl-cni-bundle-'${ARCH}'.tar.gz \
"${REGISTRY_MIRROR_FLAGS[@]}"

1 change: 1 addition & 0 deletions hack/test/e2e-qemu.sh
Original file line number Diff line number Diff line change
@@ -135,6 +135,7 @@ case "${WITH_ISO:-false}" in
false)
;;
*)
INSTALLER_IMAGE=${INSTALLER_IMAGE}-amd64-secureboot # we don't use secureboot part here, but this installer contains UKIs
QEMU_FLAGS+=("--iso-path=${ARTIFACTS}/metal-amd64.iso")
;;
esac
4 changes: 2 additions & 2 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
@@ -111,9 +111,9 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte
if !needBuildUKI {
return "", fmt.Errorf("UKI output is not supported in this Talos version")
}
case profile.OutKindISO, profile.OutKindImage:
case profile.OutKindImage:
needBuildUKI = needBuildUKI && i.prof.SecureBootEnabled()
case profile.OutKindInstaller:
case profile.OutKindISO, profile.OutKindInstaller:
needBuildUKI = needBuildUKI || quirks.New(i.prof.Version).UseSDBootForUEFI()
case profile.OutKindCmdline, profile.OutKindKernel, profile.OutKindInitramfs:
needBuildUKI = false
82 changes: 18 additions & 64 deletions pkg/imager/iso/grub.go
Original file line number Diff line number Diff line change
@@ -7,44 +7,28 @@ package iso
import (
"bytes"
_ "embed"
"fmt"
"os"
"path/filepath"
"text/template"
"time"

"github.com/siderolabs/go-cmd/pkg/cmd"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub"
"github.com/siderolabs/talos/pkg/imager/utils"
"github.com/siderolabs/talos/pkg/machinery/imager/quirks"
)

// GRUBOptions described the input for the CreateGRUB function.
type GRUBOptions struct {
KernelPath string
InitramfsPath string
Cmdline string
Version string

ScratchDir string

OutPath string
}

//go:embed grub.cfg
var grubCfgTemplate string

// CreateGRUB creates a GRUB-based ISO image.
//
// This iso supports both BIOS and UEFI booting.
func CreateGRUB(printf func(string, ...any), options GRUBOptions) error {
func (options Options) CreateGRUB(printf func(string, ...any)) (Generator, error) {
if err := utils.CopyFiles(
printf,
utils.SourceDestination(options.KernelPath, filepath.Join(options.ScratchDir, "boot", "vmlinuz")),
utils.SourceDestination(options.InitramfsPath, filepath.Join(options.ScratchDir, "boot", "initramfs.xz")),
); err != nil {
return err
return nil, err
}

printf("creating grub.cfg")
@@ -57,7 +41,7 @@ func CreateGRUB(printf func(string, ...any), options GRUBOptions) error {
}).
Parse(grubCfgTemplate)
if err != nil {
return err
return nil, err
}

if err = tmpl.Execute(&grubCfg, struct {
@@ -67,64 +51,34 @@ func CreateGRUB(printf func(string, ...any), options GRUBOptions) error {
Cmdline: options.Cmdline,
AddResetOption: quirks.New(options.Version).SupportsResetGRUBOption(),
}); err != nil {
return err
return nil, err
}

cfgPath := filepath.Join(options.ScratchDir, "boot/grub/grub.cfg")

if err = os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil {
return err
return nil, err
}

if err = os.WriteFile(cfgPath, grubCfg.Bytes(), 0o666); err != nil {
return err
return nil, err
}

if err = utils.TouchFiles(printf, options.ScratchDir); err != nil {
return err
return nil, err
}

printf("creating ISO image")

return grubMkrescue(options)
}

func grubMkrescue(options GRUBOptions) error {
args := []string{
"--compress=xz",
"--output=" + options.OutPath,
"--verbose",
options.ScratchDir,
"--",
}

if epoch, ok, err := utils.SourceDateEpoch(); err != nil {
return err
} else if ok {
// set EFI FAT image serial number
if err := os.Setenv("GRUB_FAT_SERIAL_NUMBER", fmt.Sprintf("%x", uint32(epoch))); err != nil {
return err
}

args = append(args,
"-volume_date", "all_file_dates", fmt.Sprintf("=%d", epoch),
"-volume_date", "uuid", time.Unix(epoch, 0).Format("2006010215040500"),
)
}

if quirks.New(options.Version).SupportsISOLabel() {
label := Label(options.Version, false)

args = append(args,
"-volid", VolumeID(label),
"-volset-id", label,
)
}

_, err := cmd.Run("grub-mkrescue", args...)
if err != nil {
return fmt.Errorf("failed to create ISO: %w", err)
}

return nil
return &ExecutorOptions{
Command: "grub-mkrescue",
Version: options.Version,
Arguments: []string{
"--compress=xz",
"--output=" + options.OutPath,
"--verbose",
options.ScratchDir,
"--",
},
}, nil
}
43 changes: 43 additions & 0 deletions pkg/imager/iso/hybrid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package iso

import "path/filepath"

// CreateHybrid creates an ISO image that supports both BIOS and UEFI booting.
func (options Options) CreateHybrid(printf func(string, ...any)) (Generator, error) {
if _, err := options.CreateGRUB(printf); err != nil {
return nil, err
}

if _, err := options.CreateUEFI(printf); err != nil {
return nil, err
}

efiBootImg := filepath.Join(options.ScratchDir, "efiboot.img")

return &ExecutorOptions{
Command: "grub-mkrescue",
Version: options.Version,
Arguments: []string{
"--compress=xz",
"--output=" + options.OutPath,
"--verbose",
"--directory=/usr/lib/grub/i386-pc", // only for BIOS boot
"-m", "efiboot.img", // exclude the EFI boot image from the ISO
options.ScratchDir,
"-eltorito-alt-boot",
"-e", "--interval:appended_partition_2:all::", // use appended partition 2 for EFI
"-append_partition", "2", "0xef", efiBootImg,
"-appended_part_as_gpt",
"-partition_cyl_align", // pad partition to cylinder boundary
"all",
"-partition_offset", "16", // support booting from USB
"-iso_mbr_part_type", "0x83", // just to have more clear info when doing a fdisk -l
"-no-emul-boot",
"--",
},
}, nil
}
Loading

0 comments on commit cd5e549

Please sign in to comment.