mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 12:26:21 +02:00
feat: support generating unsigned UKIs
Support generating unsigned UKI's. Also plumb in support to `talosctl cluster create` to boot off UKI's. This doesn't work yet as installer needs more work. Signed-off-by: Noel Georgi <git@frezbo.dev>
This commit is contained in:
parent
bbd6067d42
commit
e6a4583ba8
@ -122,6 +122,7 @@ var (
|
||||
nodeInitramfsPath string
|
||||
nodeISOPath string
|
||||
nodeUSBPath string
|
||||
nodeUKIPath string
|
||||
nodeDiskImagePath string
|
||||
nodeIPXEBootScript string
|
||||
applyConfigEnabled bool
|
||||
@ -231,6 +232,9 @@ func downloadBootAssets(ctx context.Context) error {
|
||||
{
|
||||
path: &nodeUSBPath,
|
||||
},
|
||||
{
|
||||
path: &nodeUKIPath,
|
||||
},
|
||||
{
|
||||
path: &nodeDiskImagePath,
|
||||
},
|
||||
@ -471,6 +475,7 @@ func create(ctx context.Context) error {
|
||||
InitramfsPath: nodeInitramfsPath,
|
||||
ISOPath: nodeISOPath,
|
||||
USBPath: nodeUSBPath,
|
||||
UKIPath: nodeUKIPath,
|
||||
IPXEBootScript: nodeIPXEBootScript,
|
||||
DiskImagePath: nodeDiskImagePath,
|
||||
|
||||
@ -1255,6 +1260,7 @@ func init() {
|
||||
createCmd.Flags().StringVar(&nodeVmlinuzPath, "vmlinuz-path", helpers.ArtifactPath(constants.KernelAssetWithArch), "the compressed kernel image to use")
|
||||
createCmd.Flags().StringVar(&nodeISOPath, "iso-path", "", "the ISO path to use for the initial boot (VM only)")
|
||||
createCmd.Flags().StringVar(&nodeUSBPath, "usb-path", "", "the USB stick image path to use for the initial boot (VM only)")
|
||||
createCmd.Flags().StringVar(&nodeUKIPath, "uki-path", "", "the UKI image path to use for the initial boot (VM only)")
|
||||
createCmd.Flags().StringVar(&nodeInitramfsPath, "initrd-path", helpers.ArtifactPath(constants.InitramfsAssetWithArch), "initramfs image to use")
|
||||
createCmd.Flags().StringVar(&nodeDiskImagePath, "disk-image-path", "", "disk image to use")
|
||||
createCmd.Flags().StringVar(&nodeIPXEBootScript, "ipxe-boot-script", "", "iPXE boot script (URL) to use")
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/siderolabs/talos/internal/pkg/secureboot"
|
||||
"github.com/siderolabs/talos/internal/pkg/secureboot/measure"
|
||||
"github.com/siderolabs/talos/internal/pkg/secureboot/pesign"
|
||||
"github.com/siderolabs/talos/pkg/imager/utils"
|
||||
)
|
||||
|
||||
// section is a UKI file section.
|
||||
@ -67,14 +68,65 @@ type Builder struct {
|
||||
unsignedUKIPath string
|
||||
}
|
||||
|
||||
// Build the UKI file.
|
||||
// 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 := utils.CopyFiles(printf, utils.SourceDestination(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)
|
||||
}
|
||||
|
||||
return utils.CopyFiles(printf, utils.SourceDestination(builder.unsignedUKIPath, builder.OutUKIPath))
|
||||
}
|
||||
|
||||
// BuildSigned the UKI file.
|
||||
//
|
||||
// BuildSigned 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) BuildSigned(printf func(string, ...any)) error {
|
||||
var err error
|
||||
|
||||
builder.scratchDir, err = os.MkdirTemp("", "talos-uki")
|
||||
|
||||
@ -103,8 +103,8 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte
|
||||
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
|
||||
}
|
||||
@ -406,18 +406,8 @@ 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")
|
||||
|
||||
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)
|
||||
}
|
||||
i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi")
|
||||
i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi")
|
||||
|
||||
builder := uki.Builder{
|
||||
Arch: i.prof.Arch,
|
||||
@ -428,14 +418,36 @@ 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 {
|
||||
buildFunc := builder.Build
|
||||
|
||||
if i.prof.SecureBootEnabled() {
|
||||
i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi.signed")
|
||||
i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi.signed")
|
||||
|
||||
builder.OutSdBootPath = i.sdBootPath
|
||||
builder.OutUKIPath = i.ukiPath
|
||||
|
||||
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.SecureBootSigner = securebootSigner
|
||||
builder.PCRSigner = pcrSigner
|
||||
|
||||
buildFunc = builder.BuildSigned
|
||||
}
|
||||
|
||||
if err := buildFunc(printf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,22 @@ var Default = map[string]Profile{
|
||||
},
|
||||
},
|
||||
},
|
||||
"metal-uki": {
|
||||
Platform: constants.PlatformMetal,
|
||||
SecureBoot: pointer.To(false),
|
||||
Output: Output{
|
||||
Kind: OutKindUKI,
|
||||
OutFormat: OutFormatRaw,
|
||||
},
|
||||
},
|
||||
"secureboot-metal-uki": {
|
||||
Platform: constants.PlatformMetal,
|
||||
SecureBoot: pointer.To(true),
|
||||
Output: Output{
|
||||
Kind: OutKindUKI,
|
||||
OutFormat: OutFormatRaw,
|
||||
},
|
||||
},
|
||||
"secureboot-metal": {
|
||||
Platform: constants.PlatformMetal,
|
||||
SecureBoot: pointer.To(true),
|
||||
|
||||
@ -209,15 +209,15 @@ func (i *Input) FillDefaults(arch, version string, secureboot bool) {
|
||||
i.BaseInstaller.ImageRef = fmt.Sprintf("%s:%s", images.DefaultInstallerImageRepository, version)
|
||||
}
|
||||
|
||||
if i.SDStub == zeroFileAsset {
|
||||
i.SDStub.Path = fmt.Sprintf(constants.SDStubAssetPath, arch)
|
||||
}
|
||||
|
||||
if i.SDBoot == zeroFileAsset {
|
||||
i.SDBoot.Path = fmt.Sprintf(constants.SDBootAssetPath, arch)
|
||||
}
|
||||
|
||||
if secureboot {
|
||||
if i.SDStub == zeroFileAsset {
|
||||
i.SDStub.Path = fmt.Sprintf(constants.SDStubAssetPath, arch)
|
||||
}
|
||||
|
||||
if i.SDBoot == zeroFileAsset {
|
||||
i.SDBoot.Path = fmt.Sprintf(constants.SDBootAssetPath, arch)
|
||||
}
|
||||
|
||||
if i.SecureBoot == nil {
|
||||
i.SecureBoot = &SecureBootAssets{}
|
||||
}
|
||||
|
||||
@ -124,9 +124,6 @@ func (p *Profile) Validate() error {
|
||||
return fmt.Errorf("customization of meta partition is not supported for %s output", p.Output.Kind)
|
||||
}
|
||||
case OutKindUKI:
|
||||
if !p.SecureBootEnabled() {
|
||||
return fmt.Errorf("!secureboot is not supported for %s output", p.Output.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -49,6 +49,7 @@ type LaunchConfig struct {
|
||||
InitrdPath string
|
||||
ISOPath string
|
||||
USBPath string
|
||||
UKIPath string
|
||||
ExtraISOPath string
|
||||
PFlashImages []string
|
||||
KernelArgs string
|
||||
@ -468,6 +469,11 @@ func launchVM(config *LaunchConfig) error {
|
||||
"-device", "nec-usb-xhci,id=xhci",
|
||||
"-device", "usb-storage,bus=xhci.0,drive=stick,removable=on",
|
||||
)
|
||||
case config.UKIPath != "":
|
||||
args = append(args,
|
||||
"-kernel", config.UKIPath,
|
||||
"-append", config.KernelArgs,
|
||||
)
|
||||
case config.KernelImagePath != "":
|
||||
args = append(args,
|
||||
"-kernel", config.KernelImagePath,
|
||||
|
||||
@ -230,6 +230,7 @@ func (p *provisioner) createNode(state *vm.State, clusterReq provision.ClusterRe
|
||||
launchConfig.InitrdPath = strings.ReplaceAll(clusterReq.InitramfsPath, constants.ArchVariable, opts.TargetArch)
|
||||
launchConfig.ISOPath = strings.ReplaceAll(clusterReq.ISOPath, constants.ArchVariable, opts.TargetArch)
|
||||
launchConfig.USBPath = strings.ReplaceAll(clusterReq.USBPath, constants.ArchVariable, opts.TargetArch)
|
||||
launchConfig.UKIPath = strings.ReplaceAll(clusterReq.UKIPath, constants.ArchVariable, opts.TargetArch)
|
||||
}
|
||||
|
||||
launchConfig.StatePath, err = state.StatePath()
|
||||
|
||||
@ -35,6 +35,7 @@ type ClusterRequest struct {
|
||||
InitramfsPath string
|
||||
ISOPath string
|
||||
USBPath string
|
||||
UKIPath string
|
||||
DiskImagePath string
|
||||
IPXEBootScript string
|
||||
|
||||
|
||||
@ -199,6 +199,7 @@ talosctl cluster create [flags]
|
||||
--skip-kubeconfig skip merging kubeconfig from the created cluster
|
||||
--talos-version string the desired Talos version to generate config for (if not set, defaults to image version)
|
||||
--talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order.
|
||||
--uki-path string the UKI image path to use for the initial boot (VM only)
|
||||
--usb-path string the USB stick image path to use for the initial boot (VM only)
|
||||
--use-vip use a virtual IP for the controlplane endpoint instead of the loadbalancer
|
||||
--user-disk strings list of disks to create for each VM in format: <mount_point1>:<size1>:<mount_point2>:<size2>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user