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

feat: uki iso #10095

Closed
wants to merge 1 commit into from
Closed
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
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
Loading