Skip to content

Commit

Permalink
feat: uki iso
Browse files Browse the repository at this point in the history
Switch to using sd-boot for newer iso.
New profile `legacy-iso` added to support booting with grub.

Part of: #9633

Signed-off-by: Noel Georgi <git@frezbo.dev>
  • Loading branch information
frezbo committed Jan 9, 2025
1 parent cc84caf commit 6313ec5
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 110 deletions.
10 changes: 7 additions & 3 deletions internal/pkg/secureboot/uki/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,14 @@ func (builder *Builder) generatePCRPublicKey() error {
}

func (builder *Builder) generateKernel() error {
path := filepath.Join(builder.scratchDir, "kernel")
path := builder.KernelPath

if err := builder.peSigner.Sign(builder.KernelPath, path); err != nil {
return err
if builder.peSigner != nil {
path = filepath.Join(builder.scratchDir, "kernel")

if err := builder.peSigner.Sign(builder.KernelPath, path); err != nil {
return err
}
}

builder.sections = append(builder.sections,
Expand Down
63 changes: 60 additions & 3 deletions internal/pkg/secureboot/uki/uki.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"log"
"os"

"github.com/siderolabs/go-copy/copy"

"github.com/siderolabs/talos/internal/pkg/secureboot"
"github.com/siderolabs/talos/internal/pkg/secureboot/measure"
"github.com/siderolabs/talos/internal/pkg/secureboot/pesign"
Expand Down Expand Up @@ -67,14 +69,14 @@ type Builder struct {
unsignedUKIPath string
}

// Build the UKI file.
// BuildSignedUKI builds the signed UKI file.
//
// Build process is as follows:
// BuildSignedUKI process is as follows:
// - sign the sd-boot EFI binary, and write it to the OutSdBootPath
// - build ephemeral sections (uname, os-release), and other proposed sections
// - measure sections, generate signature, and append to the list of sections
// - assemble the final UKI file starting from sd-stub and appending generated section.
func (builder *Builder) Build(printf func(string, ...any)) error {
func (builder *Builder) BuildSignedUKI(printf func(string, ...any)) error {
var err error

builder.scratchDir, err = os.MkdirTemp("", "talos-uki")
Expand Down Expand Up @@ -133,3 +135,58 @@ func (builder *Builder) Build(printf func(string, ...any)) error {
// sign the UKI file
return builder.peSigner.Sign(builder.unsignedUKIPath, builder.OutUKIPath)
}

// Build the unsigned UKI file.
//
// Build process is as follows:
// - build ephemeral sections (uname, os-release), and other proposed sections
// - assemble the final UKI file starting from sd-stub and appending generated section.
func (builder *Builder) Build(printf func(string, ...any)) error {
var err error

builder.scratchDir, err = os.MkdirTemp("", "talos-uki")
if err != nil {
return err
}

defer func() {
if err = os.RemoveAll(builder.scratchDir); err != nil {
log.Printf("failed to remove scratch dir: %v", err)
}
}()

if err := copy.File(builder.SdBootPath, builder.OutSdBootPath); err != nil {
return err
}

printf("generating UKI sections")

// generate and build list of all sections
for _, generateSection := range []func() error{
builder.generateOSRel,
builder.generateCmdline,
builder.generateInitrd,
builder.generateSplash,
builder.generateUname,
builder.generateSBAT,
// append kernel last to account for decompression
builder.generateKernel,
} {
if err = generateSection(); err != nil {
return fmt.Errorf("error generating sections: %w", err)
}
}

printf("assembling UKI")

// assemble the final UKI file
if err = builder.assemble(); err != nil {
return fmt.Errorf("error assembling UKI: %w", err)
}

if err := copy.File(builder.unsignedUKIPath, builder.OutUKIPath); err != nil {
return err
}

return nil
}
57 changes: 36 additions & 21 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,26 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte
return "", err
}

report.Report(reporter.Update{
Message: fmt.Sprintf("kernel command line: %s", i.cmdline),
Status: reporter.StatusSucceeded,
})

// 4. Build UKI if Secure Boot is enabled.
if i.prof.SecureBootEnabled() {
// 4. Build UKI unless the output is a kernel or cmdline.
if i.prof.Output.Kind != profile.OutKindKernel && i.prof.Output.Kind != profile.OutKindCmdline {
if err = i.buildUKI(ctx, report); err != nil {
return "", err
}
}

report.Report(reporter.Update{
Message: fmt.Sprintf("kernel command line: %s", i.cmdline),
Status: reporter.StatusSucceeded,
})

// 5. Build the output.
outputAssetPath = filepath.Join(outputPath, i.prof.OutputPath())

switch i.prof.Output.Kind {
case profile.OutKindISO:
err = i.outISO(ctx, outputAssetPath, report)
case profile.OutKindISOUKI:
err = i.outISOUKI(ctx, outputAssetPath, report)
case profile.OutKindKernel:
err = i.outKernel(outputAssetPath, report)
case profile.OutKindUKI:
Expand Down Expand Up @@ -406,17 +408,12 @@ func (i *Imager) buildCmdline() error {
func (i *Imager) buildUKI(ctx context.Context, report *reporter.Reporter) error {
printf := progressPrintf(report, reporter.Update{Message: "building UKI...", Status: reporter.StatusRunning})

i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi.signed")
i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi.signed")
i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi")
i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi")

pcrSigner, err := i.prof.Input.SecureBoot.PCRSigner.GetSigner(ctx)
if err != nil {
return fmt.Errorf("failed to get PCR signer: %w", err)
}

securebootSigner, err := i.prof.Input.SecureBoot.SecureBootSigner.GetSigner(ctx)
if err != nil {
return fmt.Errorf("failed to get SecureBoot signer: %w", err)
if i.prof.SecureBootEnabled() {
i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi.signed")
i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi.signed")
}

builder := uki.Builder{
Expand All @@ -428,14 +425,32 @@ func (i *Imager) buildUKI(ctx context.Context, report *reporter.Reporter) error
InitrdPath: i.initramfsPath,
Cmdline: i.cmdline,

SecureBootSigner: securebootSigner,
PCRSigner: pcrSigner,

OutSdBootPath: i.sdBootPath,
OutUKIPath: i.ukiPath,
}

if err := builder.Build(printf); err != nil {
if i.prof.SecureBootEnabled() {
pcrSigner, err := i.prof.Input.SecureBoot.PCRSigner.GetSigner(ctx)
if err != nil {
return fmt.Errorf("failed to get PCR signer: %w", err)
}

securebootSigner, err := i.prof.Input.SecureBoot.SecureBootSigner.GetSigner(ctx)
if err != nil {
return fmt.Errorf("failed to get SecureBoot signer: %w", err)
}

builder.PCRSigner = pcrSigner
builder.SecureBootSigner = securebootSigner
}

buildFunc := builder.Build

if i.prof.SecureBootEnabled() {
buildFunc = builder.BuildSignedUKI
}

if err := buildFunc(printf); err != nil {
return err
}

Expand Down
83 changes: 50 additions & 33 deletions pkg/imager/iso/uefi.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/siderolabs/go-cmd/pkg/cmd"
"github.com/siderolabs/go-copy/copy"

"github.com/siderolabs/talos/pkg/imager/profile"
"github.com/siderolabs/talos/pkg/imager/utils"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/imager/quirks"
Expand Down Expand Up @@ -42,6 +43,9 @@ type UEFIOptions struct {

ScratchDir string
OutPath string

// Secureboot is a flag to enable secureboot.
Secureboot bool
}

const (
Expand Down Expand Up @@ -85,18 +89,6 @@ func CreateUEFI(printf func(string, ...any), options UEFIOptions) error {
return err
}

printf("preparing loader.conf")

var loaderConfigOut bytes.Buffer

if err := template.Must(template.New("loader.conf").Parse(loaderConfigTemplate)).Execute(&loaderConfigOut, struct {
SecureBootEnroll string
}{
SecureBootEnroll: options.SDBootSecureBootEnrollKeys,
}); err != nil {
return fmt.Errorf("error rendering loader.conf: %w", err)
}

printf("creating vFAT EFI image")

fopts := []makefs.Option{
Expand All @@ -116,11 +108,7 @@ func CreateUEFI(printf func(string, ...any), options UEFIOptions) error {
return err
}

if err := os.MkdirAll(filepath.Join(options.ScratchDir, "EFI/keys"), 0o755); err != nil {
return err
}

if err := os.MkdirAll(filepath.Join(options.ScratchDir, "loader/keys/auto"), 0o755); err != nil {
if err := os.MkdirAll(filepath.Join(options.ScratchDir, "loader"), 0o755); err != nil {
return err
}

Expand All @@ -138,30 +126,59 @@ func CreateUEFI(printf func(string, ...any), options UEFIOptions) error {
return err
}

if err := os.WriteFile(filepath.Join(options.ScratchDir, "loader/loader.conf"), loaderConfigOut.Bytes(), 0o644); err != nil {
return err
}
// if creating a non-secureboot image, we don;t want to try to enroll keys
sdBootEnrollOption := profile.SDBootEnrollKeysOff.String()

if err := copy.File(options.UKISigningCertDerPath, filepath.Join(options.ScratchDir, "EFI/keys/uki-signing-cert.der")); err != nil {
return err
}
if options.Secureboot {
sdBootEnrollOption = options.SDBootSecureBootEnrollKeys

if options.PlatformKeyPath != "" {
if err := copy.File(options.PlatformKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.PlatformKeyAsset)); err != nil {
if err := os.MkdirAll(filepath.Join(options.ScratchDir, "loader/keys/auto"), 0o755); err != nil {
return err
}
}

if options.KeyExchangeKeyPath != "" {
if err := copy.File(options.KeyExchangeKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.KeyExchangeKeyAsset)); err != nil {
return err
if options.UKISigningCertDerPath != "" {
if err := os.MkdirAll(filepath.Join(options.ScratchDir, "EFI/keys"), 0o755); err != nil {
return err
}

if err := copy.File(options.UKISigningCertDerPath, filepath.Join(options.ScratchDir, "EFI/keys/uki-signing-cert.der")); err != nil {
return err
}
}
}

if options.SignatureKeyPath != "" {
if err := copy.File(options.SignatureKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.SignatureKeyAsset)); err != nil {
return err
if options.PlatformKeyPath != "" {
if err := copy.File(options.PlatformKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.PlatformKeyAsset)); err != nil {
return err
}
}

if options.KeyExchangeKeyPath != "" {
if err := copy.File(options.KeyExchangeKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.KeyExchangeKeyAsset)); err != nil {
return err
}
}

if options.SignatureKeyPath != "" {
if err := copy.File(options.SignatureKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.SignatureKeyAsset)); err != nil {
return err
}
}
}

printf("preparing loader.conf")

var loaderConfigOut bytes.Buffer

if err := template.Must(template.New("loader.conf").Parse(loaderConfigTemplate)).Execute(&loaderConfigOut, struct {
SecureBootEnroll string
}{
SecureBootEnroll: sdBootEnrollOption,
}); err != nil {
return fmt.Errorf("error rendering loader.conf: %w", err)
}

if err := os.WriteFile(filepath.Join(options.ScratchDir, "loader/loader.conf"), loaderConfigOut.Bytes(), 0o644); err != nil {
return err
}

if _, err := cmd.Run(
Expand Down
Loading

0 comments on commit 6313ec5

Please sign in to comment.