From 7eeba536da326e99e878fe61269d1d39d1615e7c Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 11 Jul 2024 18:16:56 +0400 Subject: [PATCH] feat: provide an option to enforce SecureBoot for TPM enrollment 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 --- cmd/talosctl/cmd/mgmt/cluster/create.go | 4 +++- internal/pkg/encryption/keys/keys.go | 2 +- internal/pkg/encryption/keys/tpm2.go | 19 +++++++++++++++++-- pkg/machinery/config/config/machine.go | 1 + .../config/schemas/config.schema.json | 10 +++++++++- .../types/v1alpha1/v1alpha1_provider.go | 9 +++++++++ .../config/types/v1alpha1/v1alpha1_types.go | 11 ++++++++++- .../types/v1alpha1/v1alpha1_types_doc.go | 10 +++++++++- .../configuration/v1alpha1/config.md | 8 ++++++++ .../content/v1.8/schemas/config.schema.json | 10 +++++++++- 10 files changed, 76 insertions(+), 8 deletions(-) diff --git a/cmd/talosctl/cmd/mgmt/cluster/create.go b/cmd/talosctl/cmd/mgmt/cluster/create.go index 73bff7b42f6..52950ffd414 100644 --- a/cmd/talosctl/cmd/mgmt/cluster/create.go +++ b/cmd/talosctl/cmd/mgmt/cluster/create.go @@ -563,7 +563,9 @@ func create(ctx context.Context) 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: diff --git a/internal/pkg/encryption/keys/keys.go b/internal/pkg/encryption/keys/keys.go index 0e38169f4cf..d53ca2cf19d 100644 --- a/internal/pkg/encryption/keys/keys.go +++ b/internal/pkg/encryption/keys/keys.go @@ -48,7 +48,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, errors.New("malformed config: no key handler can be created") diff --git a/internal/pkg/encryption/keys/tpm2.go b/internal/pkg/encryption/keys/tpm2.go index ca21e49a7a9..95257eecd6b 100644 --- a/internal/pkg/encryption/keys/tpm2.go +++ b/internal/pkg/encryption/keys/tpm2.go @@ -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" @@ -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") + } + + if efi.GetSetupMode() { + return nil, nil, fmt.Errorf("failed to enroll the TPM2 key, as the system is in setup mode") + } + } + key := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, key); err != nil { return nil, nil, err diff --git a/pkg/machinery/config/config/machine.go b/pkg/machinery/config/config/machine.go index 227164859e7..f6b0a302c0e 100644 --- a/pkg/machinery/config/config/machine.go +++ b/pkg/machinery/config/config/machine.go @@ -396,6 +396,7 @@ type EncryptionKeyNodeID interface { // EncryptionKeyTPM encryption key sealed by TPM. type EncryptionKeyTPM interface { + CheckSecurebootOnEnroll() bool String() string } diff --git a/pkg/machinery/config/schemas/config.schema.json b/pkg/machinery/config/schemas/config.schema.json index fd59dd29670..e6135be66dc 100644 --- a/pkg/machinery/config/schemas/config.schema.json +++ b/pkg/machinery/config/schemas/config.schema.json @@ -1649,7 +1649,15 @@ "type": "object" }, "v1alpha1.EncryptionKeyTPM": { - "properties": {}, + "properties": { + "checkSecurebootStatusOnEnroll": { + "type": "boolean", + "title": "checkSecurebootStatusOnEnroll", + "description": "description: \u0026gt;\n Check that Secureboot is enabled in the EFI firmware.\n\nIf Secureboot is not enabled, the enrollment of the key will fail.\n\n\nAs the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.\n", + "markdownDescription": "description: \u003e\n Check that Secureboot is enabled in the EFI firmware.\n\n If Secureboot is not enabled, the enrollment of the key will fail.\n As the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.", + "x-intellij-html-description": "\u003cp\u003edescription: \u0026gt;\n Check that Secureboot is enabled in the EFI firmware.\u003c/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eIf Secureboot is not enabled, the enrollment of the key will fail.\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cp\u003eAs the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.\u003c/p\u003e\n" + } + }, "additionalProperties": false, "type": "object" }, diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go index 9b6c9c3b2b3..d7f06f05fc7 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go @@ -1456,6 +1456,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 diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go index 7ce7209ead9..1b84b804e91 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types.go @@ -1621,7 +1621,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{} diff --git a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go index e985d777c2e..1c691031022 100644 --- a/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go +++ b/pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go @@ -2183,7 +2183,15 @@ func (EncryptionKeyTPM) Doc() *encoder.Doc { FieldName: "tpm", }, }, - Fields: []encoder.Doc{}, + Fields: []encoder.Doc{ + { + Name: "checkSecurebootStatusOnEnroll", + Type: "bool", + Note: "", + Description: "description: >\n Check that Secureboot is enabled in the EFI firmware.\n\n If Secureboot is not enabled, the enrollment of the key will fail.\n As the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.\n", + Comments: [3]string{"" /* encoder.HeadComment */, "description: >" /* encoder.LineComment */, "" /* encoder.FootComment */}, + }, + }, } return doc diff --git a/website/content/v1.8/reference/configuration/v1alpha1/config.md b/website/content/v1.8/reference/configuration/v1alpha1/config.md index c3953251728..90c7fe964b3 100644 --- a/website/content/v1.8/reference/configuration/v1alpha1/config.md +++ b/website/content/v1.8/reference/configuration/v1alpha1/config.md @@ -2397,6 +2397,10 @@ EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by +| Field | Type | Description | Value(s) | +|-------|------|-------------|----------| +|`checkSecurebootStatusOnEnroll` |bool |
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.
| | + @@ -2516,6 +2520,10 @@ EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by +| Field | Type | Description | Value(s) | +|-------|------|-------------|----------| +|`checkSecurebootStatusOnEnroll` |bool |
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.
| | + diff --git a/website/content/v1.8/schemas/config.schema.json b/website/content/v1.8/schemas/config.schema.json index fd59dd29670..e6135be66dc 100644 --- a/website/content/v1.8/schemas/config.schema.json +++ b/website/content/v1.8/schemas/config.schema.json @@ -1649,7 +1649,15 @@ "type": "object" }, "v1alpha1.EncryptionKeyTPM": { - "properties": {}, + "properties": { + "checkSecurebootStatusOnEnroll": { + "type": "boolean", + "title": "checkSecurebootStatusOnEnroll", + "description": "description: \u0026gt;\n Check that Secureboot is enabled in the EFI firmware.\n\nIf Secureboot is not enabled, the enrollment of the key will fail.\n\n\nAs the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.\n", + "markdownDescription": "description: \u003e\n Check that Secureboot is enabled in the EFI firmware.\n\n If Secureboot is not enabled, the enrollment of the key will fail.\n As the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.", + "x-intellij-html-description": "\u003cp\u003edescription: \u0026gt;\n Check that Secureboot is enabled in the EFI firmware.\u003c/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eIf Secureboot is not enabled, the enrollment of the key will fail.\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cp\u003eAs the TPM key is anyways bound to the value of PCR 7,\n changing Secureboot status or configuration\n after the initial enrollment will make the key unusable.\u003c/p\u003e\n" + } + }, "additionalProperties": false, "type": "object" },