From 606f72f4f98eb7904c1905a67532211cad49a0c2 Mon Sep 17 00:00:00 2001 From: budimanjojo Date: Mon, 22 Jan 2024 13:45:46 +0700 Subject: [PATCH] feat(config): support secureboot installer and ISO url This allows generating per node secureboot installer and ISO url. You can have per node `machineSpec` like so: ```yaml --- nodes: - machineSpec: secureboot: true ``` And the generated installer url will be something like: `factory.talos.dev/installer-secureboot/:v1.6.0` --- cmd/genurl.go | 2 ++ cmd/genurl_installer.go | 11 ++++++--- cmd/genurl_iso.go | 8 +++++-- docs/docs/reference/configuration.md | 34 ++++++++++++++++++++++++---- example/talconfig.yaml | 5 ++++ pkg/config/config.go | 10 ++++---- pkg/config/defaults.go | 6 +++-- pkg/generate/command.go | 4 ++-- pkg/talos/nodeconfig.go | 4 ++-- pkg/talos/schematic.go | 8 ++++++- pkg/talos/schematic_test.go | 11 +++++++-- 11 files changed, 80 insertions(+), 23 deletions(-) diff --git a/cmd/genurl.go b/cmd/genurl.go index 351ac2a7..280a3aa4 100644 --- a/cmd/genurl.go +++ b/cmd/genurl.go @@ -14,6 +14,7 @@ var ( genurlExtensions []string genurlKernelArgs []string genurlOfflineMode bool + genurlSecureboot bool ) var genurlCmd = &cobra.Command{ @@ -32,4 +33,5 @@ func init() { genurlCmd.PersistentFlags().StringSliceVarP(&genurlExtensions, "extension", "e", []string{}, "Official extension image to be included in the image (ignored when talconfig.yaml is found)") genurlCmd.PersistentFlags().StringSliceVarP(&genurlKernelArgs, "kernel-arg", "k", []string{}, "Kernel arguments to be passed to the image kernel (ignored when talconfig.yaml is found)") genurlCmd.PersistentFlags().BoolVar(&genurlOfflineMode, "offline-mode", false, "Generate schematic ID without doing POST request to image-factory") + genurlCmd.PersistentFlags().BoolVar(&genurlSecureboot, "secure-boot", false, "Whether to generate Secure Boot enabled URL") } diff --git a/cmd/genurl_installer.go b/cmd/genurl_installer.go index 10ddca8e..5b15e83d 100644 --- a/cmd/genurl_installer.go +++ b/cmd/genurl_installer.go @@ -35,7 +35,7 @@ var genurlInstallerCmd = &cobra.Command{ } if node.IPAddress == genurlNode || node.Hostname == genurlNode { - url, err := talos.GetInstallerURL(schema, cfg.GetImageFactory(), cfg.GetTalosVersion(), genurlOfflineMode) + url, err := talos.GetInstallerURL(schema, cfg.GetImageFactory(), node.GetMachineSpec(), cfg.GetTalosVersion(), genurlOfflineMode) if err != nil { log.Fatalf("Failed to generate installer url for %s, %v", node.Hostname, err) } @@ -43,7 +43,7 @@ var genurlInstallerCmd = &cobra.Command{ break } - url, err := talos.GetInstallerURL(schema, cfg.GetImageFactory(), cfg.GetTalosVersion(), genurlOfflineMode) + url, err := talos.GetInstallerURL(schema, cfg.GetImageFactory(), node.GetMachineSpec(), cfg.GetTalosVersion(), genurlOfflineMode) if err != nil { log.Fatalf("Failed to generate installer url for %s, %v", node.Hostname, err) } @@ -70,10 +70,15 @@ var genurlInstallerCmd = &cobra.Command{ }, }, } + tconfig := &config.TalhelperConfig{} tconfig.ImageFactory.RegistryURL = genurlRegistryURL - url, err := talos.GetInstallerURL(cfg, tconfig.GetImageFactory(), genurlVersion, genurlOfflineMode) + spec := &config.MachineSpec{ + Secureboot: genurlSecureboot, + } + + url, err := talos.GetInstallerURL(cfg, tconfig.GetImageFactory(), spec, genurlVersion, genurlOfflineMode) if err != nil { log.Fatalf("Failed to generate installer url, %v", err) } diff --git a/cmd/genurl_iso.go b/cmd/genurl_iso.go index ecd097e7..6687ae78 100644 --- a/cmd/genurl_iso.go +++ b/cmd/genurl_iso.go @@ -16,6 +16,7 @@ import ( var ( genurlISOTalosMode string genurlISOArch string + genurlISOUseUKI bool ) var genurlISOCmd = &cobra.Command{ @@ -77,8 +78,10 @@ var genurlISOCmd = &cobra.Command{ } tcfg := &config.TalhelperConfig{} spec := &config.MachineSpec{ - Mode: genurlISOTalosMode, - Arch: genurlISOArch, + Mode: genurlISOTalosMode, + Arch: genurlISOArch, + Secureboot: genurlSecureboot, + UseUKI: genurlISOUseUKI, } url, err := talos.GetISOURL(cfg, tcfg.GetImageFactory(), spec, genurlVersion, genurlOfflineMode) if err != nil { @@ -97,4 +100,5 @@ func init() { genurlISOCmd.Flags().StringVarP(&genurlISOTalosMode, "talos-mode", "m", "metal", "Talos runtime mode to generate URL") genurlISOCmd.Flags().StringVarP(&genurlISOArch, "arch", "a", "amd64", "CPU architecture support of the image") + genurlISOCmd.Flags().BoolVar(&genurlISOUseUKI, "use-uki", false, "Whether to generate UKI image url if Secure Boot is enabled") } diff --git a/docs/docs/reference/configuration.md b/docs/docs/reference/configuration.md index 04fe9878..9faf5d95 100644 --- a/docs/docs/reference/configuration.md +++ b/docs/docs/reference/configuration.md @@ -651,24 +651,24 @@ schematicEndpoint: /schematics `installerURLTmpl` string -
Go template to parse the full installer URL.Available placeholders: `RegistryURL`,`ID`,`Version`
*Show example* +
Go template to parse the full installer URL.Available placeholders: `RegistryURL`,`ID`,`Version`, `Secureboot`
*Show example* ```yaml installerURLTmpl: "{{.RegistryURL}}/installer/{{.ID}}:{{.Version}}" ``` -`{{.RegistryURL}}/installer/{{.ID}}:{{.Version}}` +`{{.RegistryURL}}/installer{{if .Secureboot}}-secureboot{{end}}/{{.ID}}:{{.Version}}` :negative_squared_cross_mark: `ISOURLTmpl` string -
Go template to parse the full ISO image URL.Available placeholders: `Protocol`,`RegistryURL`,`ID`,`Version`,`Mode`,`Arch`
*Show example* +
Go template to parse the full ISO image URL.Available placeholders: `Protocol`,`RegistryURL`,`ID`,`Version`,`Mode`,`Arch`, `Secureboot`, `UseUKI`
*Show example* ```yaml -installerURLTmpl: "{{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}.iso" +ISOURLTmpl: "{{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}.iso" ``` -`{{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}.iso` +`{{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}{{if .Secureboot}}-secureboot{{end}}{{if and .Secureboot .UseUKI}}-uki.efi{{else}}.iso{{end}}` :negative_squared_cross_mark: @@ -707,6 +707,30 @@ arch: arm64 :negative_squared_cross_mark: + +`secureboot` +bool +Whether to enable Secure Boot.
*Show example* +```yaml +secureboot: true +``` + +`false` +:negative_squared_cross_mark: + + + +`useUKI` +bool +Whether to use UKI if Secure Boot is enabled.
*Show example* +```yaml +useUKI: true +``` + +`false` +:negative_squared_cross_mark: + + ## IngressFirewall diff --git a/example/talconfig.yaml b/example/talconfig.yaml index f1ccbfb8..e9757b70 100644 --- a/example/talconfig.yaml +++ b/example/talconfig.yaml @@ -38,6 +38,11 @@ nodes: except: 172.20.0.1/32 ipAddress: 192.168.200.11 controlPlane: true + machineSpec: + mode: metal + arch: amd64 + useUKI: true + secureboot: true schematic: customization: extraKernelArgs: diff --git a/pkg/config/config.go b/pkg/config/config.go index 2056a4e9..d6f201f4 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -59,13 +59,15 @@ type ImageFactory struct { RegistryURL string `yaml:"registryURL,omitempty" jsonschema:"default=factory.talos.dev,description=Registry url or the image"` SchematicEndpoint string `yaml:"schematicEndpoint,omitempty" jsonschema:"default=/schematics,description:Endpoint to get schematic ID from the registry"` Protocol string `yaml:"protocol,omitempty" jsonschema:"default=https,description=Protocol of the registry(https or http)"` - InstallerURLTmpl string `yaml:"installerURLTmpl,omitempty" jsonschema:"default={{.RegistryURL}}/installer/{{.ID}}:{{.Version}},description=Template for installer image URL"` - ISOURLTmpl string `yaml:"ISOURLTmpl,omitempty" jsonschema:"default={{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}.iso,description=Template for ISO image URL"` + InstallerURLTmpl string `yaml:"installerURLTmpl,omitempty" jsonschema:"default={{.RegistryURL}}/installer{{if .Secureboot}}-secureboot{{end}}/{{.ID}}:{{.Version}},description=Template for installer image URL"` + ISOURLTmpl string `yaml:"ISOURLTmpl,omitempty" jsonschema:"default={{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}{{if .Secureboot}}-secureboot{{end}}{{if and .Secureboot .UseUKI}}-uki.efi{{else}}.iso{{end}},description=Template for ISO image URL"` } type MachineSpec struct { - Mode string `yaml:"mode,omitempty" jsonschema:"default=metal,description=Machine mode (e.g: metal)"` - Arch string `yaml:"arch,omitempty" jsonschema:"default=amd64,description=Machine architecture (e.g: amd64, arm64)"` + Mode string `yaml:"mode,omitempty" jsonschema:"default=metal,description=Machine mode (e.g: metal)"` + Arch string `yaml:"arch,omitempty" jsonschema:"default=amd64,description=Machine architecture (e.g: amd64, arm64)"` + Secureboot bool `yaml:"secureboot,omitempty" jsonschema:"default=false,description=Whether to enable Secure Boot"` + UseUKI bool `yaml:"useUKI,omitempty" jsonschema:"default=false,description=Whether to use UKI if Secure Boot is enabled"` } type IngressFirewall struct { diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index aa9972e2..286c225a 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -77,8 +77,8 @@ func (c *TalhelperConfig) GetImageFactory() *ImageFactory { RegistryURL: "factory.talos.dev", SchematicEndpoint: "/schematics", Protocol: "https", - InstallerURLTmpl: "{{.RegistryURL}}/installer/{{.ID}}:{{.Version}}", - ISOURLTmpl: "{{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}.iso", + InstallerURLTmpl: "{{.RegistryURL}}/installer{{if .Secureboot}}-secureboot{{end}}/{{.ID}}:{{.Version}}", + ISOURLTmpl: "{{.Protocol}}://{{.RegistryURL}}/image/{{.ID}}/{{.Version}}/{{.Mode}}-{{.Arch}}{{if .Secureboot}}-secureboot{{end}}{{if and .Secureboot .UseUKI}}-uki.efi{{else}}.iso{{end}}", } if c.ImageFactory.RegistryURL != "" { result.RegistryURL = c.ImageFactory.RegistryURL @@ -107,6 +107,8 @@ func (n *Node) GetMachineSpec() *MachineSpec { if n.MachineSpec.Arch != "" { result.Arch = n.MachineSpec.Arch } + result.Secureboot = n.MachineSpec.Secureboot + result.UseUKI = n.MachineSpec.UseUKI return result } diff --git a/pkg/generate/command.go b/pkg/generate/command.go index 74213ae8..7fc4ea3a 100644 --- a/pkg/generate/command.go +++ b/pkg/generate/command.go @@ -56,12 +56,12 @@ func GenerateUpgradeCommand(cfg *config.TalhelperConfig, outDir string, node str url = n.TalosImageURL + ":" + cfg.GetTalosVersion() } else if n.Schematic != nil { var err error - url, err = talos.GetInstallerURL(n.Schematic, cfg.GetImageFactory(), cfg.GetTalosVersion(), true) + url, err = talos.GetInstallerURL(n.Schematic, cfg.GetImageFactory(), n.GetMachineSpec(), cfg.GetTalosVersion(), true) if err != nil { return fmt.Errorf("Failed to generate installer url for %s, %v", n.Hostname, err) } } else { - url, _ = talos.GetInstallerURL(&schematic.Schematic{}, cfg.GetImageFactory(), cfg.GetTalosVersion(), true) + url, _ = talos.GetInstallerURL(&schematic.Schematic{}, cfg.GetImageFactory(), n.GetMachineSpec(), cfg.GetTalosVersion(), true) } upgradeFlags := []string{ diff --git a/pkg/talos/nodeconfig.go b/pkg/talos/nodeconfig.go index f179b80a..de3cbc7e 100644 --- a/pkg/talos/nodeconfig.go +++ b/pkg/talos/nodeconfig.go @@ -107,7 +107,7 @@ func installerURL(node *config.Node, cfg taloscfg.Provider, iFactory *config.Ima version := strings.Split(cfg.Machine().Install().Image(), ":") if node.Schematic != nil && node.TalosImageURL == "" { - url, err := GetInstallerURL(node.Schematic, iFactory, version[1], offlineMode) + url, err := GetInstallerURL(node.Schematic, iFactory, node.GetMachineSpec(), version[1], offlineMode) if err != nil { return "", err } @@ -118,5 +118,5 @@ func installerURL(node *config.Node, cfg taloscfg.Provider, iFactory *config.Ima return node.TalosImageURL + ":" + version[1], nil } - return GetInstallerURL(&schematic.Schematic{}, iFactory, version[1], offlineMode) + return GetInstallerURL(&schematic.Schematic{}, iFactory, node.GetMachineSpec(), version[1], offlineMode) } diff --git a/pkg/talos/schematic.go b/pkg/talos/schematic.go index 93d3a8da..59762c01 100644 --- a/pkg/talos/schematic.go +++ b/pkg/talos/schematic.go @@ -22,6 +22,7 @@ type installerTmpl struct { RegistryURL string ID string Version string + Secureboot bool } type isoTmpl struct { @@ -31,12 +32,15 @@ type isoTmpl struct { Version string Mode string Arch string + Secureboot bool + UseUKI bool } -func GetInstallerURL(cfg *schematic.Schematic, factory *config.ImageFactory, version string, offlineMode bool) (string, error) { +func GetInstallerURL(cfg *schematic.Schematic, factory *config.ImageFactory, spec *config.MachineSpec, version string, offlineMode bool) (string, error) { tmplData := installerTmpl{ RegistryURL: factory.RegistryURL, Version: version, + Secureboot: spec.Secureboot, } id, err := getSchematicID(cfg, factory, offlineMode) @@ -65,6 +69,8 @@ func GetISOURL(cfg *schematic.Schematic, factory *config.ImageFactory, spec *con Version: version, Mode: spec.Mode, Arch: spec.Arch, + Secureboot: spec.Secureboot, + UseUKI: spec.UseUKI, } id, err := getSchematicID(cfg, factory, offlineMode) diff --git a/pkg/talos/schematic_test.go b/pkg/talos/schematic_test.go index 9c2ff811..93fd3974 100644 --- a/pkg/talos/schematic_test.go +++ b/pkg/talos/schematic_test.go @@ -43,6 +43,7 @@ func TestGetInstallerURL(t *testing.T) { name string cfg *schematic.Schematic iFactory *config.ImageFactory + machineSpec *config.MachineSpec version string expectedURL string } @@ -54,6 +55,7 @@ func TestGetInstallerURL(t *testing.T) { iFactory: &config.ImageFactory{ RegistryURL: "factory.talos.dev", }, + machineSpec: &config.MachineSpec{}, version: "v1.5.4", expectedURL: "factory.talos.dev/installer/376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba:v1.5.4", }, @@ -70,6 +72,7 @@ func TestGetInstallerURL(t *testing.T) { iFactory: &config.ImageFactory{ RegistryURL: "", }, + machineSpec: &config.MachineSpec{}, version: "v1.5.4", expectedURL: "factory.talos.dev/installer/98442b5bb4e8d050f30978ce3e6ec22e7bf534d57cafcd51313235128057e612:v1.5.4", }, @@ -82,6 +85,7 @@ func TestGetInstallerURL(t *testing.T) { }, }, iFactory: &config.ImageFactory{}, + machineSpec: &config.MachineSpec{}, expectedURL: "factory.talos.dev/installer/ff5083b14ccb03821ea738d712ac08a82b44d2693013622059edaae286665239:", }, @@ -98,14 +102,17 @@ func TestGetInstallerURL(t *testing.T) { iFactory: &config.ImageFactory{ RegistryURL: "test.registry/", }, + machineSpec: &config.MachineSpec{ + Secureboot: true, + }, version: "1.5.4", - expectedURL: "test.registry//installer/104c23dfe7c5bfeff6a4cc7e166d8b3bba0f371760592c7677c90c822bb1d109:1.5.4", + expectedURL: "test.registry//installer-secureboot/104c23dfe7c5bfeff6a4cc7e166d8b3bba0f371760592c7677c90c822bb1d109:1.5.4", }, } { t.Run(test.name, func(t *testing.T) { cfg := &config.TalhelperConfig{} cfg.ImageFactory = *test.iFactory - url, err := GetInstallerURL(test.cfg, cfg.GetImageFactory(), test.version, true) + url, err := GetInstallerURL(test.cfg, cfg.GetImageFactory(), test.machineSpec, test.version, true) if err != nil { t.Fatal(err) }