diff --git a/hack/release.toml b/hack/release.toml index a7bd283397..e919da02ae 100644 --- a/hack/release.toml +++ b/hack/release.toml @@ -49,6 +49,20 @@ machine: features: localDNS: false ``` +""" + + [notes.secureboot-image] + title = "Secure Boot Image" + description = """\ +Talos Linux now provides a way to configure systemd-boot ISO 'secure-boot-enroll' option while generating a SecureBoot ISO image: + +```yaml +output: + kind: iso + isoOptions: + sdBootEnrollKeys: force # default is still if-safe + outFormat: raw +``` """ [notes.rsa-service-account] diff --git a/pkg/imager/iso/loader.conf b/pkg/imager/iso/loader.conf deleted file mode 100644 index b7d71563e5..0000000000 --- a/pkg/imager/iso/loader.conf +++ /dev/null @@ -1,5 +0,0 @@ -# systemd-boot configuration - -timeout 10 - -secure-boot-enroll if-safe diff --git a/pkg/imager/iso/loader.conf.tmpl b/pkg/imager/iso/loader.conf.tmpl new file mode 100644 index 0000000000..d0e79ce430 --- /dev/null +++ b/pkg/imager/iso/loader.conf.tmpl @@ -0,0 +1,5 @@ +# systemd-boot configuration + +timeout 10 + +secure-boot-enroll {{ .SecureBootEnroll }} diff --git a/pkg/imager/iso/uefi.go b/pkg/imager/iso/uefi.go index 4a2b36f21d..1df37ceab5 100644 --- a/pkg/imager/iso/uefi.go +++ b/pkg/imager/iso/uefi.go @@ -11,6 +11,7 @@ import ( "fmt" "os" "path/filepath" + "text/template" "github.com/siderolabs/go-cmd/pkg/cmd" @@ -24,6 +25,9 @@ type UEFIOptions struct { UKIPath string SDBootPath string + // A value in loader.conf secure-boot-enroll: off, manual, if-safe, force. + SDBootSecureBootEnrollKeys string + // optional, for auto-enrolling secureboot keys PlatformKeyPath string KeyExchangeKeyPath string @@ -41,8 +45,8 @@ const ( mib = 1024 * 1024 ) -//go:embed loader.conf -var loaderConfig []byte +//go:embed loader.conf.tmpl +var loaderConfigTemplate string // CreateUEFI creates an iso using a UKI, systemd-boot. // @@ -54,6 +58,8 @@ func CreateUEFI(printf func(string, ...any), options UEFIOptions) error { return err } + printf("preparing raw image") + efiBootImg := filepath.Join(options.ScratchDir, "efiboot.img") // initial size @@ -75,6 +81,18 @@ 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{ @@ -125,7 +143,7 @@ func CreateUEFI(printf func(string, ...any), options UEFIOptions) error { } if _, err := cmd.RunContext( - cmd.WithStdin(context.Background(), bytes.NewReader(loaderConfig)), + cmd.WithStdin(context.Background(), &loaderConfigOut), "mcopy", "-i", efiBootImg, "-", "::loader/loader.conf", ); err != nil { return err diff --git a/pkg/imager/out.go b/pkg/imager/out.go index 9ac88f321b..5bc81ddb5c 100644 --- a/pkg/imager/out.go +++ b/pkg/imager/out.go @@ -20,6 +20,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" + "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/cmd/installer/pkg/install" @@ -85,10 +86,14 @@ func (i *Imager) outISO(ctx context.Context, path string, report *reporter.Repor var err error if i.prof.SecureBootEnabled() { + isoOptions := pointer.SafeDeref(i.prof.Output.ISOOptions) + options := iso.UEFIOptions{ UKIPath: i.ukiPath, SDBootPath: i.sdBootPath, + SDBootSecureBootEnrollKeys: isoOptions.SDBootEnrollKeys.String(), + PlatformKeyPath: i.prof.Input.SecureBoot.PlatformKeyPath, KeyExchangeKeyPath: i.prof.Input.SecureBoot.KeyExchangeKeyPath, SignatureKeyPath: i.prof.Input.SecureBoot.SignatureKeyPath, diff --git a/pkg/imager/profile/deep_copy.generated.go b/pkg/imager/profile/deep_copy.generated.go index 36c5b78925..1b0665ac36 100644 --- a/pkg/imager/profile/deep_copy.generated.go +++ b/pkg/imager/profile/deep_copy.generated.go @@ -37,5 +37,9 @@ func (o Profile) DeepCopy() Profile { cp.Output.ImageOptions = new(ImageOptions) *cp.Output.ImageOptions = *o.Output.ImageOptions } + if o.Output.ISOOptions != nil { + cp.Output.ISOOptions = new(ISOOptions) + *cp.Output.ISOOptions = *o.Output.ISOOptions + } return cp } diff --git a/pkg/imager/profile/default.go b/pkg/imager/profile/default.go index b5e5ab4344..6833cff2dd 100644 --- a/pkg/imager/profile/default.go +++ b/pkg/imager/profile/default.go @@ -37,6 +37,9 @@ var Default = map[string]Profile{ Output: Output{ Kind: OutKindISO, OutFormat: OutFormatRaw, + ISOOptions: &ISOOptions{ + SDBootEnrollKeys: SDBootEnrollKeysIfSafe, + }, }, }, // Metal images diff --git a/pkg/imager/profile/output.go b/pkg/imager/profile/output.go index 2b1208d2c1..61576775ee 100644 --- a/pkg/imager/profile/output.go +++ b/pkg/imager/profile/output.go @@ -15,6 +15,8 @@ type Output struct { Kind OutputKind `yaml:"kind"` // Options for the 'image' output. ImageOptions *ImageOptions `yaml:"imageOptions,omitempty"` + // Options for the 'iso' output. + ISOOptions *ISOOptions `yaml:"isoOptions,omitempty"` // OutFormat is the format for the output: // * raw - output raw file // * .tar.gz - output tar.gz archive @@ -37,6 +39,14 @@ type ImageOptions struct { DiskFormatOptions string `yaml:"diskFormatOptions,omitempty"` } +// ISOOptions describes options for the 'iso' output. +type ISOOptions struct { + // SDBootEnrollKeys is a value in loader.conf secure-boot-enroll: off, manual, if-safe, force. + // + // If not set, it defaults to if-safe. + SDBootEnrollKeys SDBootEnrollKeys `yaml:"sdBootEnrollKeys"` +} + //go:generate enumer -type=OutputKind -linecomment -text // OutputKind is output specification. @@ -81,3 +91,16 @@ const ( DiskFormatVPC // vhd DiskFormatOVA // ova ) + +//go:generate enumer -type SDBootEnrollKeys -linecomment -text + +// SDBootEnrollKeys is a value in loader.conf secure-boot-enroll: off, manual, if-safe, force. +type SDBootEnrollKeys int + +// SDBootEnrollKeys values. +const ( + SDBootEnrollKeysIfSafe SDBootEnrollKeys = iota // if-safe + SDBootEnrollKeysManual // manual + SDBootEnrollKeysForce // force + SDBootEnrollKeysOff // off +) diff --git a/pkg/imager/profile/sdbootenrollkeys_enumer.go b/pkg/imager/profile/sdbootenrollkeys_enumer.go new file mode 100644 index 0000000000..c120aba902 --- /dev/null +++ b/pkg/imager/profile/sdbootenrollkeys_enumer.go @@ -0,0 +1,63 @@ +// Code generated by "enumer -type SDBootEnrollKeys -linecomment -text"; DO NOT EDIT. + +package profile + +import ( + "fmt" +) + +const _SDBootEnrollKeysName = "if-safemanualforceoff" + +var _SDBootEnrollKeysIndex = [...]uint8{0, 7, 13, 18, 21} + +func (i SDBootEnrollKeys) String() string { + if i < 0 || i >= SDBootEnrollKeys(len(_SDBootEnrollKeysIndex)-1) { + return fmt.Sprintf("SDBootEnrollKeys(%d)", i) + } + return _SDBootEnrollKeysName[_SDBootEnrollKeysIndex[i]:_SDBootEnrollKeysIndex[i+1]] +} + +var _SDBootEnrollKeysValues = []SDBootEnrollKeys{0, 1, 2, 3} + +var _SDBootEnrollKeysNameToValueMap = map[string]SDBootEnrollKeys{ + _SDBootEnrollKeysName[0:7]: 0, + _SDBootEnrollKeysName[7:13]: 1, + _SDBootEnrollKeysName[13:18]: 2, + _SDBootEnrollKeysName[18:21]: 3, +} + +// SDBootEnrollKeysString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func SDBootEnrollKeysString(s string) (SDBootEnrollKeys, error) { + if val, ok := _SDBootEnrollKeysNameToValueMap[s]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to SDBootEnrollKeys values", s) +} + +// SDBootEnrollKeysValues returns all values of the enum +func SDBootEnrollKeysValues() []SDBootEnrollKeys { + return _SDBootEnrollKeysValues +} + +// IsASDBootEnrollKeys returns "true" if the value is listed in the enum definition. "false" otherwise +func (i SDBootEnrollKeys) IsASDBootEnrollKeys() bool { + for _, v := range _SDBootEnrollKeysValues { + if i == v { + return true + } + } + return false +} + +// MarshalText implements the encoding.TextMarshaler interface for SDBootEnrollKeys +func (i SDBootEnrollKeys) MarshalText() ([]byte, error) { + return []byte(i.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface for SDBootEnrollKeys +func (i *SDBootEnrollKeys) UnmarshalText(text []byte) error { + var err error + *i, err = SDBootEnrollKeysString(string(text)) + return err +}