Skip to content

Commit

Permalink
feat: provide an option to enforce SecureBoot for TPM enrollment
Browse files Browse the repository at this point in the history
Fixes #8995

There is no security impact, as the actual SecureBoot
state/configuration is measured into the PCR 7 and the disk encryption
key unsealing is tied to this value.

This is more to provide a way to avoid accidentally encrypting to the
TPM while SecureBoot is not enabled.

Signed-off-by: Andrey Smirnov <[email protected]>
(cherry picked from commit cf5effa)
  • Loading branch information
smira committed Jul 22, 2024
1 parent 390b29d commit ddc690d
Show file tree
Hide file tree
Showing 12 changed files with 85 additions and 11 deletions.
5 changes: 4 additions & 1 deletion cmd/talosctl/cmd/mgmt/cluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/dustin/go-humanize"
"github.com/siderolabs/go-blockdevice/blockdevice/encryption"
"github.com/siderolabs/go-kubeconfig"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/go-procfs/procfs"
sideronet "github.com/siderolabs/net"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -465,7 +466,9 @@ func create(ctx context.Context, flags *pflag.FlagSet) (err error) {
provisionOptions = append(provisionOptions, provision.WithKMS(nethelpers.JoinHostPort("0.0.0.0", port)))
case "tpm":
keys = append(keys, &v1alpha1.EncryptionKey{
KeyTPM: &v1alpha1.EncryptionKeyTPM{},
KeyTPM: &v1alpha1.EncryptionKeyTPM{
TPMCheckSecurebootStatusOnEnroll: pointer.To(true),
},
KeySlot: i,
})
default:
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/encryption/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func NewHandler(cfg config.EncryptionKey, options ...KeyOption) (Handler, error)

return NewKMSKeyHandler(key, cfg.KMS().Endpoint(), opts.GetSystemInformation)
case cfg.TPM() != nil:
return NewTPMKeyHandler(key)
return NewTPMKeyHandler(key, cfg.TPM().CheckSecurebootOnEnroll())
}

return nil, fmt.Errorf("malformed config: no key handler can be created")
Expand Down
19 changes: 17 additions & 2 deletions internal/pkg/encryption/keys/tpm2.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"io"

"github.com/foxboron/go-uefi/efi"
"github.com/siderolabs/go-blockdevice/blockdevice/encryption"
"github.com/siderolabs/go-blockdevice/blockdevice/encryption/luks"
"github.com/siderolabs/go-blockdevice/blockdevice/encryption/token"
Expand All @@ -32,17 +34,30 @@ type TPMToken struct {
// TPMKeyHandler seals token using TPM.
type TPMKeyHandler struct {
KeyHandler

checkSecurebootOnEnroll bool
}

// NewTPMKeyHandler creates new TPMKeyHandler.
func NewTPMKeyHandler(key KeyHandler) (*TPMKeyHandler, error) {
func NewTPMKeyHandler(key KeyHandler, checkSecurebootOnEnroll bool) (*TPMKeyHandler, error) {
return &TPMKeyHandler{
KeyHandler: key,
KeyHandler: key,
checkSecurebootOnEnroll: checkSecurebootOnEnroll,
}, nil
}

// NewKey implements Handler interface.
func (h *TPMKeyHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) {
if h.checkSecurebootOnEnroll {
if !efi.GetSecureBoot() {
return nil, nil, fmt.Errorf("failed to enroll the TPM2 key, as SecureBoot is disabled (and checkSecurebootOnEnroll is enabled)")
}

if efi.GetSetupMode() {
return nil, nil, fmt.Errorf("failed to enroll the TPM2 key, as the system is in SecureBoot setup mode (and checkSecurebootOnEnroll is enabled)")
}
}

key := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return nil, nil, err
Expand Down
1 change: 1 addition & 0 deletions pkg/machinery/config/config/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,7 @@ type EncryptionKeyNodeID interface {

// EncryptionKeyTPM encryption key sealed by TPM.
type EncryptionKeyTPM interface {
CheckSecurebootOnEnroll() bool
String() string
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,15 @@
"type": "object"
},
"EncryptionKeyTPM": {
"properties": {},
"properties": {
"checkSecurebootStatusOnEnroll": {
"type": "boolean",
"title": "checkSecurebootStatusOnEnroll",
"description": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\n",
"markdownDescription": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.",
"x-intellij-html-description": "\u003cp\u003eCheck that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object"
},
Expand Down
9 changes: 9 additions & 0 deletions pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1431,6 +1431,15 @@ func (e *EncryptionKeyTPM) String() string {
return "tpm"
}

// CheckSecurebootOnEnroll implements the config.Provider interface.
func (e *EncryptionKeyTPM) CheckSecurebootOnEnroll() bool {
if e == nil {
return false
}

return pointer.SafeDeref(e.TPMCheckSecurebootStatusOnEnroll)
}

// Slot implements the config.Provider interface.
func (e *EncryptionKey) Slot() int {
return e.KeySlot
Expand Down
11 changes: 10 additions & 1 deletion pkg/machinery/config/types/v1alpha1/v1alpha1_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -1596,7 +1596,16 @@ type EncryptionKeyKMS struct {
}

// EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM.
type EncryptionKeyTPM struct{}
type EncryptionKeyTPM struct {
// description: >
// Check that Secureboot is enabled in the EFI firmware.
//
// If Secureboot is not enabled, the enrollment of the key will fail.
// As the TPM key is anyways bound to the value of PCR 7,
// changing Secureboot status or configuration
// after the initial enrollment will make the key unusable.
TPMCheckSecurebootStatusOnEnroll *bool `yaml:"checkSecurebootStatusOnEnroll,omitempty"`
}

// EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.
type EncryptionKeyNodeID struct{}
Expand Down
10 changes: 9 additions & 1 deletion pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pkg/machinery/config/types/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions website/content/v1.6/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ talosctl cluster create [flags]
--bad-rtc launch VM with bad RTC state (QEMU only)
--cidr string CIDR of the cluster network (IPv4, ULA network for IPv6 is derived in automated way) (default "10.5.0.0/24")
--cni-bin-path strings search path for CNI binaries (VM only) (default [/home/user/.talos/cni/bin])
--cni-bundle-url string URL to download CNI bundle from (VM only) (default "https://github.com/siderolabs/talos/releases/download/v1.6.7-dirty/talosctl-cni-bundle-${ARCH}.tar.gz")
--cni-bundle-url string URL to download CNI bundle from (VM only) (default "https://github.com/siderolabs/talos/releases/download/v1.6.7/talosctl-cni-bundle-${ARCH}.tar.gz")
--cni-cache-dir string CNI cache directory path (VM only) (default "/home/user/.talos/cni/cache")
--cni-conf-dir string CNI config directory path (VM only) (default "/home/user/.talos/cni/conf.d")
--config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file
Expand Down Expand Up @@ -2849,7 +2849,7 @@ talosctl upgrade [flags]
--debug debug operation from kernel logs. --wait is set to true when this flag is set
-f, --force force the upgrade (skip checks on etcd health and members, might lead to data loss)
-h, --help help for upgrade
-i, --image string the container image to use for performing the install (default "ghcr.io/siderolabs/installer:v1.6.7-dirty")
-i, --image string the container image to use for performing the install (default "ghcr.io/siderolabs/installer:v1.6.7")
--insecure upgrade using the insecure (encrypted with no auth) maintenance service
-p, --preserve preserve data
-m, --reboot-mode string select the reboot mode during upgrade. Mode "powercycle" bypasses kexec. Valid values are: ["default" "powercycle"]. (default "default")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2377,6 +2377,10 @@ EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by



| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`checkSecurebootStatusOnEnroll` |bool |<details><summary>Check that Secureboot is enabled in the EFI firmware.</summary>If Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.</details> | |




Expand Down Expand Up @@ -2496,6 +2500,10 @@ EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by



| Field | Type | Description | Value(s) |
|-------|------|-------------|----------|
|`checkSecurebootStatusOnEnroll` |bool |<details><summary>Check that Secureboot is enabled in the EFI firmware.</summary>If Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.</details> | |




Expand Down
10 changes: 9 additions & 1 deletion website/content/v1.6/schemas/v1alpha1_config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1236,7 +1236,15 @@
"type": "object"
},
"EncryptionKeyTPM": {
"properties": {},
"properties": {
"checkSecurebootStatusOnEnroll": {
"type": "boolean",
"title": "checkSecurebootStatusOnEnroll",
"description": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\n",
"markdownDescription": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.",
"x-intellij-html-description": "\u003cp\u003eCheck that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\u003c/p\u003e\n"
}
},
"additionalProperties": false,
"type": "object"
},
Expand Down

0 comments on commit ddc690d

Please sign in to comment.