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
1 parent 75673b6 commit cd5e549
Show file tree
Hide file tree
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:
Expand Down Expand Up @@ -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
Expand Down
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:
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions .kres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 ; \
Expand Down
6 changes: 6 additions & 0 deletions hack/release.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
4 changes: 3 additions & 1 deletion hack/test/e2e-iso.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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[@]}"

Expand Down
1 change: 1 addition & 0 deletions hack/test/e2e-qemu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 18 additions & 64 deletions pkg/imager/iso/grub.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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 {
Expand All @@ -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.