feat: support systemd-boot ISO enroll keys option

Fixes #8196

Example (profile excerpt):

```yaml
output:
  kind: iso
  isoOptions:
    sdBootEnrollKeys: force
  outFormat: raw
```

Defaults are still same (`if-safe` unless explicitly overridden).

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
Andrey Smirnov 2024-02-07 21:44:32 +04:00
parent afa71d6b02
commit 087b50f429
No known key found for this signature in database
GPG Key ID: FE042E3D4085A811
9 changed files with 138 additions and 8 deletions

View File

@ -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]

View File

@ -1,5 +0,0 @@
# systemd-boot configuration
timeout 10
secure-boot-enroll if-safe

View File

@ -0,0 +1,5 @@
# systemd-boot configuration
timeout 10
secure-boot-enroll {{ .SecureBootEnroll }}

View File

@ -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

View File

@ -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,

View File

@ -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
}

View File

@ -37,6 +37,9 @@ var Default = map[string]Profile{
Output: Output{
Kind: OutKindISO,
OutFormat: OutFormatRaw,
ISOOptions: &ISOOptions{
SDBootEnrollKeys: SDBootEnrollKeysIfSafe,
},
},
},
// Metal images

View File

@ -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
)

View File

@ -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
}