feat: implement encryption locking to STATE

Fixes #10676

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
Andrey Smirnov 2025-07-25 21:04:14 +04:00
parent c1e65a3425
commit a5f3000f2e
No known key found for this signature in database
GPG Key ID: FE042E3D4085A811
51 changed files with 1178 additions and 111 deletions

View File

@ -88,6 +88,7 @@ message EncryptionKey {
bytes static_passphrase = 3; bytes static_passphrase = 3;
string kms_endpoint = 4; string kms_endpoint = 4;
bool tpm_check_secureboot_status_on_enroll = 5; bool tpm_check_secureboot_status_on_enroll = 5;
bool lock_to_state = 6;
} }
// EncryptionSpec is the spec for volume encryption. // EncryptionSpec is the spec for volume encryption.

View File

@ -21,6 +21,11 @@ message CertSANSpec {
string fqdn = 3; string fqdn = 3;
} }
// EncryptionSaltSpec describes the salt.
message EncryptionSaltSpec {
bytes disk_salt = 1;
}
// EtcdCertsSpec describes etcd certs secrets. // EtcdCertsSpec describes etcd certs secrets.
message EtcdCertsSpec { message EtcdCertsSpec {
common.PEMEncodedCertificateAndKey etcd = 1; common.PEMEncodedCertificateAndKey etcd = 1;

View File

@ -582,6 +582,12 @@ func create(ctx context.Context, ops createOps) error {
EncryptionKeys: convertEncryptionKeys(keys), EncryptionKeys: convertEncryptionKeys(keys),
} }
if spec.label != constants.StatePartitionLabel {
for idx := range blockCfg.EncryptionSpec.EncryptionKeys {
blockCfg.EncryptionSpec.EncryptionKeys[idx].KeyLockToSTATE = pointer.To(true)
}
}
ctr, err := container.New(blockCfg) ctr, err := container.New(blockCfg)
if err != nil { if err != nil {
return fmt.Errorf("error creating container for %q volume: %w", spec.label, err) return fmt.Errorf("error creating container for %q volume: %w", spec.label, err)

View File

@ -132,6 +132,9 @@ Talos increases the boot partition size to 2 GiB to accommodate larger images (w
description = """\ description = """\
Disk encryption for system volumes is now managed by the `VolumeConfig` machine configuration document. Disk encryption for system volumes is now managed by the `VolumeConfig` machine configuration document.
Legacy configuration in `valpha1` machine configuration is still supported. Legacy configuration in `valpha1` machine configuration is still supported.
New per-key option `lockToSTATE` is added to the `VolumeConfig` document, which allows to lock the volume encryption key to the secret salt in the `STATE` volume.
So, if the `STATE` volume is wiped or replaced, the volume encryption key will not be usable anymore.
""" """
[make_deps] [make_deps]

View File

@ -15,6 +15,8 @@ import (
) )
func TestIdentityGenerate(t *testing.T) { func TestIdentityGenerate(t *testing.T) {
t.Parallel()
var spec1, spec2 cluster.IdentitySpec var spec1, spec2 cluster.IdentitySpec
require.NoError(t, clusteradapter.IdentitySpec(&spec1).Generate()) require.NoError(t, clusteradapter.IdentitySpec(&spec1).Generate())
@ -29,6 +31,8 @@ func TestIdentityGenerate(t *testing.T) {
} }
func TestIdentityConvertMachineID(t *testing.T) { func TestIdentityConvertMachineID(t *testing.T) {
t.Parallel()
spec := cluster.IdentitySpec{ spec := cluster.IdentitySpec{
NodeID: "sou7yy34ykX3n373Zw1DXKb8zD7UnyKT6HT3QDsGH6L", NodeID: "sou7yy34ykX3n373Zw1DXKb8zD7UnyKT6HT3QDsGH6L",
} }

View File

@ -0,0 +1,39 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package secrets
import (
"crypto/rand"
"io"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
)
// EncryptionSalt adapter provides encryption salt generation.
//
//nolint:revive,golint
func EncryptionSalt(r *secrets.EncryptionSaltSpec) encryptionSalt {
return encryptionSalt{
EncryptionSaltSpec: r,
}
}
type encryptionSalt struct {
*secrets.EncryptionSaltSpec
}
// Generate new encryption salt.
func (a encryptionSalt) Generate() error {
buf := make([]byte, constants.DiskEncryptionSaltSize)
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
return err
}
a.EncryptionSaltSpec.DiskSalt = buf
return nil
}

View File

@ -0,0 +1,29 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package secrets_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
secretsadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/secrets"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
)
func TestEncryptionSaltGenerate(t *testing.T) {
t.Parallel()
var spec1, spec2 secrets.EncryptionSaltSpec
require.NoError(t, secretsadapter.EncryptionSalt(&spec1).Generate())
require.NoError(t, secretsadapter.EncryptionSalt(&spec2).Generate())
assert.NotEqual(t, spec1, spec2)
assert.Len(t, spec1.DiskSalt, constants.DiskEncryptionSaltSize)
}

View File

@ -0,0 +1,6 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package secrets implements adapters wrapping resources to provide additional functionality.
package secrets

View File

@ -36,7 +36,7 @@ func Close(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext
case block.EncryptionProviderLUKS2: case block.EncryptionProviderLUKS2:
encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption
handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), volumeContext.GetSystemInformation, volumeContext.TPMLocker) handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), volumeContext.EncryptionHelpers)
if err != nil { if err != nil {
return fmt.Errorf("failed to create encryption handler: %w", err) return fmt.Errorf("failed to create encryption handler: %w", err)
} }

View File

@ -35,7 +35,7 @@ func HandleEncryption(ctx context.Context, logger *zap.Logger, volumeContext Man
case block.EncryptionProviderLUKS2: case block.EncryptionProviderLUKS2:
encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption
handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), volumeContext.GetSystemInformation, volumeContext.TPMLocker) handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), volumeContext.EncryptionHelpers)
if err != nil { if err != nil {
return fmt.Errorf("failed to create encryption handler: %w", err) return fmt.Errorf("failed to create encryption handler: %w", err)
} }

View File

@ -7,14 +7,13 @@ package volumes
import ( import (
"cmp" "cmp"
"context"
"math" "math"
"github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/optional"
"github.com/siderolabs/talos/internal/pkg/encryption"
blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block"
"github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/block"
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
) )
// CompareVolumeConfigs compares two volume configs in the proposed order of provisioning. // CompareVolumeConfigs compares two volume configs in the proposed order of provisioning.
@ -89,7 +88,6 @@ type ManagerContext struct {
DevicesReady bool DevicesReady bool
PreviousWaveProvisioned bool PreviousWaveProvisioned bool
GetSystemInformation func(context.Context) (*hardware.SystemInformation, error) EncryptionHelpers encryption.Helpers
TPMLocker func(context.Context, func() error) error
ShouldCloseVolume bool ShouldCloseVolume bool
} }

View File

@ -112,6 +112,7 @@ func convertEncryptionConfiguration(in cfg.EncryptionConfig, out *block.VolumeCo
for i, key := range in.Keys() { for i, key := range in.Keys() {
out.Encryption.Keys[i].Slot = key.Slot() out.Encryption.Keys[i].Slot = key.Slot()
out.Encryption.Keys[i].LockToSTATE = key.LockToSTATE()
switch { switch {
case key.Static() != nil: case key.Static() != nil:

View File

@ -23,12 +23,15 @@ import (
"go.uber.org/zap" "go.uber.org/zap"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes"
"github.com/siderolabs/talos/internal/pkg/encryption"
"github.com/siderolabs/talos/internal/pkg/encryption/helpers"
blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block"
"github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/proto"
"github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/block"
"github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/hardware"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
) )
// VolumeManagerController manages volumes in the system, converting VolumeConfig resources to VolumeStatuses. // VolumeManagerController manages volumes in the system, converting VolumeConfig resources to VolumeStatuses.
@ -96,6 +99,12 @@ func (ctrl *VolumeManagerController) Inputs() []controller.Input {
Type: hardware.PCRStatusType, Type: hardware.PCRStatusType,
Kind: controller.InputStrong, Kind: controller.InputStrong,
}, },
{
Namespace: secrets.NamespaceName,
Type: secrets.EncryptionSaltType,
ID: optional.Some(secrets.EncryptionSaltID),
Kind: controller.InputWeak,
},
} }
} }
@ -374,19 +383,11 @@ func (ctrl *VolumeManagerController) Run(ctx context.Context, r controller.Runti
Disks: diskSpecs, Disks: diskSpecs,
DevicesReady: devicesReady, DevicesReady: devicesReady,
PreviousWaveProvisioned: vc.TypedSpec().Provisioning.Wave <= fullyProvisionedWave, PreviousWaveProvisioned: vc.TypedSpec().Provisioning.Wave <= fullyProvisionedWave,
GetSystemInformation: func(ctx context.Context) (*hardware.SystemInformation, error) { EncryptionHelpers: encryption.Helpers{
systemInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID) GetSystemInformation: ctrl.getSystemInformation(r),
if err != nil && !state.IsNotFoundError(err) {
return nil, fmt.Errorf("error fetching system information: %w", err)
}
if systemInfo == nil {
return nil, errors.New("system information not available")
}
return systemInfo, nil
},
TPMLocker: hardware.LockPCRStatus(r, constants.UKIPCR, vc.Metadata().ID()), TPMLocker: hardware.LockPCRStatus(r, constants.UKIPCR, vc.Metadata().ID()),
SaltGetter: ctrl.getSaltGetter(r),
},
ShouldCloseVolume: shouldCloseVolume, ShouldCloseVolume: shouldCloseVolume,
}, },
); err != nil { ); err != nil {
@ -591,3 +592,33 @@ func (ctrl *VolumeManagerController) processVolumeConfig(ctx context.Context, lo
prevPhase = volumeContext.Status.Phase prevPhase = volumeContext.Status.Phase
} }
} }
func (ctrl *VolumeManagerController) getSystemInformation(r controller.Reader) helpers.SystemInformationGetter {
return func(ctx context.Context) (*hardware.SystemInformation, error) {
systemInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID)
if err != nil && !state.IsNotFoundError(err) {
return nil, fmt.Errorf("error fetching system information: %w", err)
}
if systemInfo == nil {
return nil, errors.New("system information not available")
}
return systemInfo, nil
}
}
func (ctrl *VolumeManagerController) getSaltGetter(r controller.Reader) helpers.SaltGetter {
return func(ctx context.Context) ([]byte, error) {
salt, err := safe.ReaderGetByID[*secrets.EncryptionSalt](ctx, r, secrets.EncryptionSaltID)
if err != nil && !state.IsNotFoundError(err) {
return nil, fmt.Errorf("error fetching encryption salt: %w", err)
}
if salt == nil {
return nil, errors.New("encryption salt not available")
}
return salt.TypedSpec().DiskSalt, nil
}
}

View File

@ -0,0 +1,109 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package secrets
import (
"context"
"fmt"
"path/filepath"
"github.com/cosi-project/runtime/pkg/controller"
"github.com/cosi-project/runtime/pkg/safe"
"go.uber.org/zap"
secretsadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/secrets"
"github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/block"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
)
// EncryptionSaltController manages secrets.EncryptionSalt in STATE.
type EncryptionSaltController struct {
stateMachine blockautomaton.VolumeMounterAutomaton
}
// Name implements controller.Controller interface.
func (ctrl *EncryptionSaltController) Name() string {
return "secrets.EncryptionSaltController"
}
// Inputs implements controller.Controller interface.
func (ctrl *EncryptionSaltController) Inputs() []controller.Input {
return []controller.Input{
{
Namespace: block.NamespaceName,
Type: block.VolumeMountStatusType,
Kind: controller.InputStrong,
},
{
Namespace: block.NamespaceName,
Type: block.VolumeMountRequestType,
Kind: controller.InputDestroyReady,
},
}
}
// Outputs implements controller.Controller interface.
func (ctrl *EncryptionSaltController) Outputs() []controller.Output {
return []controller.Output{
{
Type: secrets.EncryptionSaltType,
Kind: controller.OutputShared,
},
{
Type: block.VolumeMountRequestType,
Kind: controller.OutputShared,
},
}
}
// Run implements controller.Controller interface.
//
//nolint:gocyclo
func (ctrl *EncryptionSaltController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error {
for {
select {
case <-ctx.Done():
return nil
case <-r.EventCh():
}
if ctrl.stateMachine == nil {
ctrl.stateMachine = blockautomaton.NewVolumeMounter(ctrl.Name(), constants.StatePartitionLabel, ctrl.establishEncryptionSalt)
}
if err := ctrl.stateMachine.Run(ctx, r, logger); err != nil {
return fmt.Errorf("error running volume mounter machine: %w", err)
}
r.ResetRestartBackoff()
}
}
func (ctrl *EncryptionSaltController) establishEncryptionSalt(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error {
rootPath := mountStatus.TypedSpec().Target
var salt secrets.EncryptionSaltSpec
if err := controllers.LoadOrNewFromFile(filepath.Join(rootPath, constants.EncryptionSaltFilename), &salt, func(v *secrets.EncryptionSaltSpec) error {
return secretsadapter.EncryptionSalt(v).Generate()
}); err != nil {
return fmt.Errorf("error caching node identity: %w", err)
}
if err := safe.WriterModify(ctx, r, secrets.NewEncryptionSalt(), func(r *secrets.EncryptionSalt) error {
*r.TypedSpec() = salt
return nil
}); err != nil {
return fmt.Errorf("error modifying resource: %w", err)
}
logger.Info("encryption salt established")
return nil
}

View File

@ -0,0 +1,105 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package secrets_test
import (
"log"
"os"
"path/filepath"
"testing"
"github.com/cosi-project/runtime/pkg/resource"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/resources/block"
"github.com/siderolabs/talos/pkg/machinery/resources/secrets"
)
type EncryptionSaltSuite struct {
ctest.DefaultSuite
}
func (suite *EncryptionSaltSuite) TestDefault() {
statePath := suite.T().TempDir()
mountID := (&secretsctrl.EncryptionSaltController{}).Name() + "-" + constants.StatePartitionLabel
ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) {
asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID)
})
ctest.AssertNoResource[*secrets.EncryptionSalt](suite, secrets.EncryptionSaltID)
volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID)
volumeMountStatus.TypedSpec().Target = statePath
suite.Create(volumeMountStatus)
ctest.AssertResource(suite, secrets.EncryptionSaltID, func(*secrets.EncryptionSalt, *assert.Assertions) {})
ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) {
asrt.True(vms.Metadata().Finalizers().Empty())
})
suite.Destroy(volumeMountStatus)
ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID)
suite.Assert().FileExists(filepath.Join(statePath, constants.EncryptionSaltFilename), "encryption salt file should exist")
contents, err := os.ReadFile(filepath.Join(statePath, constants.EncryptionSaltFilename))
suite.Require().NoError(err, "should be able to read encryption salt file")
log.Printf("contents: %q", contents)
}
func (suite *EncryptionSaltSuite) TestLoad() {
statePath := suite.T().TempDir()
mountID := (&secretsctrl.EncryptionSaltController{}).Name() + "-" + constants.StatePartitionLabel
ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) {
asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID)
})
// using verbatim data here to make sure salt representation is supported in future version fo Talos
suite.Require().NoError(os.WriteFile(filepath.Join(statePath, constants.EncryptionSaltFilename),
[]byte("diskSalt:\n - 240\n - 180\n - 79\n - 128\n - 31\n - 0\n - 19\n - 124\n - 165\n - 74\n - 113\n - 220\n - 27\n - 83\n - 46\n - 74\n - 204\n - 190\n - 217\n - 96\n - 221\n - 2\n - 165\n - 98\n - 245\n - 36\n - 165\n - 151\n - 149\n - 66\n - 113\n - 16\n"), //nolint:lll
0o600))
ctest.AssertNoResource[*secrets.EncryptionSalt](suite, secrets.EncryptionSaltID)
volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID)
volumeMountStatus.TypedSpec().Target = statePath
suite.Create(volumeMountStatus)
ctest.AssertResource(suite, secrets.EncryptionSaltID, func(encryptionSalt *secrets.EncryptionSalt, asrt *assert.Assertions) {
asrt.Equal(
[]byte{0xf0, 0xb4, 0x4f, 0x80, 0x1f, 0x0, 0x13, 0x7c, 0xa5, 0x4a, 0x71, 0xdc, 0x1b, 0x53, 0x2e, 0x4a, 0xcc, 0xbe, 0xd9, 0x60, 0xdd, 0x2, 0xa5, 0x62, 0xf5, 0x24, 0xa5, 0x97, 0x95, 0x42, 0x71, 0x10},
encryptionSalt.TypedSpec().DiskSalt,
)
})
ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) {
asrt.True(vms.Metadata().Finalizers().Empty())
})
suite.Destroy(volumeMountStatus)
ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID)
}
func TestEncryptionSaltSuite(t *testing.T) {
t.Parallel()
suite.Run(t, &EncryptionSaltSuite{
DefaultSuite: ctest.DefaultSuite{
AfterSetup: func(suite *ctest.DefaultSuite) {
suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.EncryptionSaltController{}))
},
},
})
}

View File

@ -378,6 +378,7 @@ func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error
&runtimecontrollers.WatchdogTimerController{}, &runtimecontrollers.WatchdogTimerController{},
&secrets.APICertSANsController{}, &secrets.APICertSANsController{},
&secrets.APIController{}, &secrets.APIController{},
&secrets.EncryptionSaltController{},
&secrets.EtcdController{}, &secrets.EtcdController{},
secrets.NewKubeletController(), secrets.NewKubeletController(),
&secrets.KubernetesCertSANsController{}, &secrets.KubernetesCertSANsController{},

View File

@ -232,6 +232,7 @@ func NewState() (*State, error) {
&runtime.WatchdogTimerStatus{}, &runtime.WatchdogTimerStatus{},
&secrets.API{}, &secrets.API{},
&secrets.CertSAN{}, &secrets.CertSAN{},
&secrets.EncryptionSalt{},
&secrets.Etcd{}, &secrets.Etcd{},
&secrets.EtcdRoot{}, &secrets.EtcdRoot{},
&secrets.Kubelet{}, &secrets.Kubelet{},

View File

@ -128,6 +128,8 @@ func (suite *ResetSuite) TestResetWithSpecEphemeral() {
// TestResetWithSpecStateAndUserDisks resets state partition and user disks on the node. // TestResetWithSpecStateAndUserDisks resets state partition and user disks on the node.
// //
// As ephemeral partition is not reset, so kubelet cert shouldn't change. // As ephemeral partition is not reset, so kubelet cert shouldn't change.
//
//nolint:gocyclo
func (suite *ResetSuite) TestResetWithSpecStateAndUserDisks() { func (suite *ResetSuite) TestResetWithSpecStateAndUserDisks() {
if suite.Capabilities().SecureBooted { if suite.Capabilities().SecureBooted {
// this is because in secure boot mode, the machine config is only applied and cannot be passed as kernel args // this is because in secure boot mode, the machine config is only applied and cannot be passed as kernel args
@ -135,8 +137,25 @@ func (suite *ResetSuite) TestResetWithSpecStateAndUserDisks() {
} }
node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker)
nodeCtx := client.WithNode(suite.ctx, node)
disks, err := suite.Client.Disks(client.WithNode(suite.ctx, node)) suite.T().Logf("resetting STATE + user disk on node %s", node)
config, err := suite.ReadConfigFromNode(nodeCtx)
suite.Require().NoError(err)
// check if EPHEMERAL is encrypted locked to STATE
var lockedToState bool
if volume, ok := config.Volumes().ByName(constants.EphemeralPartitionLabel); ok && volume.Encryption() != nil {
for _, key := range volume.Encryption().Keys() {
if key.LockToSTATE() {
lockedToState = true
}
}
}
disks, err := suite.Client.Disks(nodeCtx)
suite.Require().NoError(err) suite.Require().NoError(err)
suite.Require().NotEmpty(disks.Messages) suite.Require().NotEmpty(disks.Messages)
@ -160,6 +179,8 @@ func (suite *ResetSuite) TestResetWithSpecStateAndUserDisks() {
}, },
) )
if !lockedToState {
// if not locked to STATE, wipe will be successful
suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{
Reboot: true, Reboot: true,
Graceful: true, Graceful: true,
@ -171,6 +192,45 @@ func (suite *ResetSuite) TestResetWithSpecStateAndUserDisks() {
}, },
UserDisksToWipe: userDisksToWipe, UserDisksToWipe: userDisksToWipe,
}, true) }, true)
return
}
suite.T().Logf("verifying that EPHEMERAL partition would fail to unlock after reset, as it is locked to STATE")
// if the EPHEMERAL partition is locked to STATE, it will fail to unlock after reset, so let's verify it
suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{
Reboot: true,
Graceful: true,
SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{
{
Label: constants.StatePartitionLabel,
Wipe: true,
},
},
UserDisksToWipe: userDisksToWipe,
}, false)
// wait for EPHEMERAL failure
rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI,
[]string{constants.EphemeralPartitionLabel},
func(vs *block.VolumeStatus, asrt *assert.Assertions) {
asrt.Equal(block.VolumePhaseFailed, vs.TypedSpec().Phase)
asrt.Contains(vs.TypedSpec().ErrorMessage, "encryption key rejected")
},
)
// now reset EPHEMERAL
suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{
Reboot: true,
Graceful: false,
SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{
{
Label: constants.EphemeralPartitionLabel,
Wipe: true,
},
},
}, true)
} }
// TestResetDuringBoot resets the node while it is in boot sequence. // TestResetDuringBoot resets the node while it is in boot sequence.

View File

@ -28,8 +28,15 @@ import (
const keyHandlerTimeout = time.Second * 10 const keyHandlerTimeout = time.Second * 10
// Helpers provides helper methods for encryption handling.
type Helpers struct {
GetSystemInformation helpers.SystemInformationGetter
TPMLocker helpers.TPMLockFunc
SaltGetter helpers.SaltGetter
}
// NewHandler creates new Handler. // NewHandler creates new Handler.
func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, getSystemInformation helpers.SystemInformationGetter, tpmLocker helpers.TPMLockFunc) (*Handler, error) { func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, helpers Helpers) (*Handler, error) {
cipher, err := luks.ParseCipherKind(encryptionConfig.Cipher) cipher, err := luks.ParseCipherKind(encryptionConfig.Cipher)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse cipher kind: %w", err) return nil, fmt.Errorf("failed to parse cipher kind: %w", err)
@ -60,8 +67,9 @@ func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, getSyste
for _, cfg := range encryptionConfig.Keys { for _, cfg := range encryptionConfig.Keys {
handler, err := keys.NewHandler(cfg, handler, err := keys.NewHandler(cfg,
keys.WithVolumeID(volumeID), keys.WithVolumeID(volumeID),
keys.WithSystemInformationGetter(getSystemInformation), keys.WithSystemInformationGetter(helpers.GetSystemInformation),
keys.WithTPMLocker(tpmLocker), keys.WithTPMLocker(helpers.TPMLocker),
keys.WithSaltGetter(helpers.SaltGetter),
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -81,6 +89,7 @@ func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, getSyste
return &Handler{ return &Handler{
encryptionProvider: provider, encryptionProvider: provider,
keyHandlers: keyHandlers, keyHandlers: keyHandlers,
saltGetter: helpers.SaltGetter,
}, nil }, nil
} }
@ -89,6 +98,7 @@ func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, getSyste
type Handler struct { type Handler struct {
encryptionProvider encryption.Provider encryptionProvider encryption.Provider
keyHandlers []keys.Handler keyHandlers []keys.Handler
saltGetter helpers.SaltGetter
} }
// Open encrypted partition. // Open encrypted partition.

View File

@ -16,3 +16,6 @@ type SystemInformationGetter func(context.Context) (*hardware.SystemInformation,
// TPMLockFunc is a function that ensures that the TPM is locked and PCR state is as expected. // TPMLockFunc is a function that ensures that the TPM is locked and PCR state is as expected.
type TPMLockFunc func(context.Context, func() error) error type TPMLockFunc func(context.Context, func() error) error
// SaltGetter defines the closure which can be used in key handlers to get the encryption salt.
type SaltGetter func(context.Context) ([]byte, error)

View File

@ -19,6 +19,8 @@ import (
var errNoSystemInfoGetter = errors.New("the UUID getter is not set") var errNoSystemInfoGetter = errors.New("the UUID getter is not set")
// NewHandler key using provided config. // NewHandler key using provided config.
//
//nolint:gocyclo
func NewHandler(cfg block.EncryptionKey, options ...KeyOption) (Handler, error) { func NewHandler(cfg block.EncryptionKey, options ...KeyOption) (Handler, error) {
opts, err := NewDefaultOptions(options) opts, err := NewDefaultOptions(options)
if err != nil { if err != nil {
@ -27,6 +29,8 @@ func NewHandler(cfg block.EncryptionKey, options ...KeyOption) (Handler, error)
key := KeyHandler{slot: cfg.Slot} key := KeyHandler{slot: cfg.Slot}
var handler Handler
switch cfg.Type { switch cfg.Type {
case block.EncryptionKeyStatic: case block.EncryptionKeyStatic:
k := cfg.StaticPassphrase k := cfg.StaticPassphrase
@ -34,28 +38,44 @@ func NewHandler(cfg block.EncryptionKey, options ...KeyOption) (Handler, error)
return nil, errors.New("static key must have key data defined") return nil, errors.New("static key must have key data defined")
} }
return NewStaticKeyHandler(key, k), nil handler = NewStaticKeyHandler(key, k)
case block.EncryptionKeyNodeID: case block.EncryptionKeyNodeID:
if opts.GetSystemInformation == nil { if opts.GetSystemInformation == nil {
return nil, fmt.Errorf("failed to create nodeUUID key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter) return nil, fmt.Errorf("failed to create nodeUUID key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter)
} }
return NewNodeIDKeyHandler(key, opts.VolumeID, opts.GetSystemInformation), nil handler = NewNodeIDKeyHandler(key, opts.VolumeID, opts.GetSystemInformation)
case block.EncryptionKeyKMS: case block.EncryptionKeyKMS:
if opts.GetSystemInformation == nil { if opts.GetSystemInformation == nil {
return nil, fmt.Errorf("failed to create KMS key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter) return nil, fmt.Errorf("failed to create KMS key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter)
} }
return NewKMSKeyHandler(key, cfg.KMSEndpoint, opts.GetSystemInformation) handler, err = NewKMSKeyHandler(key, cfg.KMSEndpoint, opts.GetSystemInformation)
if err != nil {
return nil, err
}
case block.EncryptionKeyTPM: case block.EncryptionKeyTPM:
if opts.TPMLocker == nil { if opts.TPMLocker == nil {
return nil, fmt.Errorf("failed to create TPM key handler at slot %d: no TPM lock function", cfg.Slot) return nil, fmt.Errorf("failed to create TPM key handler at slot %d: no TPM lock function", cfg.Slot)
} }
return NewTPMKeyHandler(key, cfg.TPMCheckSecurebootStatusOnEnroll, opts.TPMLocker) handler, err = NewTPMKeyHandler(key, cfg.TPMCheckSecurebootStatusOnEnroll, opts.TPMLocker)
if err != nil {
return nil, err
}
default: default:
return nil, fmt.Errorf("unsupported key type: %s", cfg.Type) return nil, fmt.Errorf("unsupported key type: %s", cfg.Type)
} }
if cfg.LockToSTATE {
if opts.SaltGetter == nil {
return nil, fmt.Errorf("failed to create state-locked key handler at slot %d: no salt getter", cfg.Slot)
}
handler = NewSaltedHandler(handler, opts.SaltGetter)
}
return handler, nil
} }
// Handler manages key lifecycle. // Handler manages key lifecycle.

View File

@ -0,0 +1,52 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package keys_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/siderolabs/talos/internal/pkg/encryption/keys"
"github.com/siderolabs/talos/pkg/machinery/resources/hardware"
)
func TestNodeID(t *testing.T) {
t.Parallel()
handler := keys.NewNodeIDKeyHandler(keys.KeyHandler{}, "test", func(context.Context) (*hardware.SystemInformation, error) {
res := hardware.NewSystemInformation(hardware.SystemInformationID)
res.TypedSpec().UUID = "12345678-1234-5678-1234-567812345678"
return res, nil
})
key, token, err := handler.NewKey(t.Context())
require.NoError(t, err)
require.Nil(t, token)
assert.Equal(t, "12345678-1234-5678-1234-567812345678test", string(key.Value))
key, err = handler.GetKey(t.Context(), nil)
require.NoError(t, err)
assert.Equal(t, "12345678-1234-5678-1234-567812345678test", string(key.Value))
}
func TestNodeIDBadEntropy(t *testing.T) {
t.Parallel()
handler := keys.NewNodeIDKeyHandler(keys.KeyHandler{}, "test", func(context.Context) (*hardware.SystemInformation, error) {
res := hardware.NewSystemInformation(hardware.SystemInformationID)
res.TypedSpec().UUID = "11111111-0000-1111-1111-111111111111" // bad entropy
return res, nil
})
_, _, err := handler.NewKey(t.Context())
require.Error(t, err)
assert.EqualError(t, err, "machine UUID 11111111-0000-1111-1111-111111111111 entropy check failed")
}

View File

@ -14,6 +14,7 @@ type KeyOptions struct {
VolumeID string VolumeID string
GetSystemInformation helpers.SystemInformationGetter GetSystemInformation helpers.SystemInformationGetter
TPMLocker helpers.TPMLockFunc TPMLocker helpers.TPMLockFunc
SaltGetter helpers.SaltGetter
} }
// WithVolumeID passes the partition label to the key handler. // WithVolumeID passes the partition label to the key handler.
@ -43,6 +44,15 @@ func WithTPMLocker(locker helpers.TPMLockFunc) KeyOption {
} }
} }
// WithSaltGetter passes the salt getter to the key handler.
func WithSaltGetter(getter helpers.SaltGetter) KeyOption {
return func(o *KeyOptions) error {
o.SaltGetter = getter
return nil
}
}
// NewDefaultOptions creates new KeyOptions. // NewDefaultOptions creates new KeyOptions.
func NewDefaultOptions(options []KeyOption) (*KeyOptions, error) { func NewDefaultOptions(options []KeyOption) (*KeyOptions, error) {
var opts KeyOptions var opts KeyOptions

View File

@ -0,0 +1,69 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package keys
import (
"context"
"fmt"
"slices"
"github.com/siderolabs/go-blockdevice/v2/encryption"
"github.com/siderolabs/go-blockdevice/v2/encryption/token"
"github.com/siderolabs/talos/internal/pkg/encryption/helpers"
)
// SaltedHandler is a key handler wrapper that salts the key with a provided random salt.
type SaltedHandler struct {
wrapped Handler
saltGetter helpers.SaltGetter
}
// NewSaltedHandler creates a new handler that wraps the provided key handler and uses the provided salt getter.
func NewSaltedHandler(wrapped Handler, saltGetter helpers.SaltGetter) Handler {
return &SaltedHandler{
wrapped: wrapped,
saltGetter: saltGetter,
}
}
// NewKey implements the keys.Handler interface.
func (k *SaltedHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) {
key, token, err := k.wrapped.NewKey(ctx)
if err != nil {
return key, token, err
}
salt, err := k.saltGetter(ctx)
if err != nil {
return nil, nil, fmt.Errorf("failed to get disk encryption key salt: %w", err)
}
key.Value = slices.Concat(key.Value, salt)
return key, token, nil
}
// GetKey implements the keys.Handler interface.
func (k *SaltedHandler) GetKey(ctx context.Context, token token.Token) (*encryption.Key, error) {
key, err := k.wrapped.GetKey(ctx, token)
if err != nil {
return key, err
}
salt, err := k.saltGetter(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get disk encryption key salt: %w", err)
}
key.Value = slices.Concat(key.Value, salt)
return key, nil
}
// Slot implements the keys.Handler interface.
func (k *SaltedHandler) Slot() int {
return k.wrapped.Slot()
}

View File

@ -0,0 +1,40 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package keys_test
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/siderolabs/talos/internal/pkg/encryption/keys"
)
func TestSalted(t *testing.T) {
t.Parallel()
const (
secret = "topsecret"
salt = "salted"
)
inner := keys.NewStaticKeyHandler(keys.KeyHandler{}, []byte(secret))
handler := keys.NewSaltedHandler(inner, func(context.Context) ([]byte, error) {
return []byte(salt), nil
})
key, token, err := handler.NewKey(t.Context())
require.NoError(t, err)
require.Nil(t, token)
assert.Equal(t, secret+salt, string(key.Value))
key, err = handler.GetKey(t.Context(), nil)
require.NoError(t, err)
assert.Equal(t, secret+salt, string(key.Value))
}

View File

@ -0,0 +1,32 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package keys_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/siderolabs/talos/internal/pkg/encryption/keys"
)
func TestStatic(t *testing.T) {
t.Parallel()
const secret = "topsecret"
handler := keys.NewStaticKeyHandler(keys.KeyHandler{}, []byte(secret))
key, token, err := handler.NewKey(t.Context())
require.NoError(t, err)
require.Nil(t, token)
assert.Equal(t, secret, string(key.Value))
key1, err := handler.GetKey(t.Context(), nil)
require.NoError(t, err)
assert.Equal(t, key, key1)
}

View File

@ -647,6 +647,7 @@ type EncryptionKey struct {
StaticPassphrase []byte `protobuf:"bytes,3,opt,name=static_passphrase,json=staticPassphrase,proto3" json:"static_passphrase,omitempty"` StaticPassphrase []byte `protobuf:"bytes,3,opt,name=static_passphrase,json=staticPassphrase,proto3" json:"static_passphrase,omitempty"`
KmsEndpoint string `protobuf:"bytes,4,opt,name=kms_endpoint,json=kmsEndpoint,proto3" json:"kms_endpoint,omitempty"` KmsEndpoint string `protobuf:"bytes,4,opt,name=kms_endpoint,json=kmsEndpoint,proto3" json:"kms_endpoint,omitempty"`
TpmCheckSecurebootStatusOnEnroll bool `protobuf:"varint,5,opt,name=tpm_check_secureboot_status_on_enroll,json=tpmCheckSecurebootStatusOnEnroll,proto3" json:"tpm_check_secureboot_status_on_enroll,omitempty"` TpmCheckSecurebootStatusOnEnroll bool `protobuf:"varint,5,opt,name=tpm_check_secureboot_status_on_enroll,json=tpmCheckSecurebootStatusOnEnroll,proto3" json:"tpm_check_secureboot_status_on_enroll,omitempty"`
LockToState bool `protobuf:"varint,6,opt,name=lock_to_state,json=lockToState,proto3" json:"lock_to_state,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@ -716,6 +717,13 @@ func (x *EncryptionKey) GetTpmCheckSecurebootStatusOnEnroll() bool {
return false return false
} }
func (x *EncryptionKey) GetLockToState() bool {
if x != nil {
return x.LockToState
}
return false
}
// EncryptionSpec is the spec for volume encryption. // EncryptionSpec is the spec for volume encryption.
type EncryptionSpec struct { type EncryptionSpec struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@ -2217,13 +2225,14 @@ const file_resource_definitions_block_block_proto_rawDesc = "" +
"prettySize\x12'\n" + "prettySize\x12'\n" +
"\x0fsecondary_disks\x18\x10 \x03(\tR\x0esecondaryDisks\x12\x12\n" + "\x0fsecondary_disks\x18\x10 \x03(\tR\x0esecondaryDisks\x12\x12\n" +
"\x04uuid\x18\x11 \x01(\tR\x04uuid\x12\x1a\n" + "\x04uuid\x18\x11 \x01(\tR\x04uuid\x12\x1a\n" +
"\bsymlinks\x18\x12 \x03(\tR\bsymlinks\"\x92\x02\n" + "\bsymlinks\x18\x12 \x03(\tR\bsymlinks\"\xb6\x02\n" +
"\rEncryptionKey\x12\x12\n" + "\rEncryptionKey\x12\x12\n" +
"\x04slot\x18\x01 \x01(\x03R\x04slot\x12L\n" + "\x04slot\x18\x01 \x01(\x03R\x04slot\x12L\n" +
"\x04type\x18\x02 \x01(\x0e28.talos.resource.definitions.enums.BlockEncryptionKeyTypeR\x04type\x12+\n" + "\x04type\x18\x02 \x01(\x0e28.talos.resource.definitions.enums.BlockEncryptionKeyTypeR\x04type\x12+\n" +
"\x11static_passphrase\x18\x03 \x01(\fR\x10staticPassphrase\x12!\n" + "\x11static_passphrase\x18\x03 \x01(\fR\x10staticPassphrase\x12!\n" +
"\fkms_endpoint\x18\x04 \x01(\tR\vkmsEndpoint\x12O\n" + "\fkms_endpoint\x18\x04 \x01(\tR\vkmsEndpoint\x12O\n" +
"%tpm_check_secureboot_status_on_enroll\x18\x05 \x01(\bR tpmCheckSecurebootStatusOnEnroll\"\xa5\x02\n" + "%tpm_check_secureboot_status_on_enroll\x18\x05 \x01(\bR tpmCheckSecurebootStatusOnEnroll\x12\"\n" +
"\rlock_to_state\x18\x06 \x01(\bR\vlockToState\"\xa5\x02\n" +
"\x0eEncryptionSpec\x12Y\n" + "\x0eEncryptionSpec\x12Y\n" +
"\bprovider\x18\x01 \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\bprovider\x12C\n" + "\bprovider\x18\x01 \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\bprovider\x12C\n" +
"\x04keys\x18\x02 \x03(\v2/.talos.resource.definitions.block.EncryptionKeyR\x04keys\x12\x16\n" + "\x04keys\x18\x02 \x03(\v2/.talos.resource.definitions.block.EncryptionKeyR\x04keys\x12\x16\n" +

View File

@ -606,6 +606,16 @@ func (m *EncryptionKey) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields) i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields) copy(dAtA[i:], m.unknownFields)
} }
if m.LockToState {
i--
if m.LockToState {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i--
dAtA[i] = 0x30
}
if m.TpmCheckSecurebootStatusOnEnroll { if m.TpmCheckSecurebootStatusOnEnroll {
i-- i--
if m.TpmCheckSecurebootStatusOnEnroll { if m.TpmCheckSecurebootStatusOnEnroll {
@ -2216,6 +2226,9 @@ func (m *EncryptionKey) SizeVT() (n int) {
if m.TpmCheckSecurebootStatusOnEnroll { if m.TpmCheckSecurebootStatusOnEnroll {
n += 2 n += 2
} }
if m.LockToState {
n += 2
}
n += len(m.unknownFields) n += len(m.unknownFields)
return n return n
} }
@ -4552,6 +4565,26 @@ func (m *EncryptionKey) UnmarshalVT(dAtA []byte) error {
} }
} }
m.TpmCheckSecurebootStatusOnEnroll = bool(v != 0) m.TpmCheckSecurebootStatusOnEnroll = bool(v != 0)
case 6:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LockToState", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
m.LockToState = bool(v != 0)
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:]) skippy, err := protohelpers.Skip(dAtA[iNdEx:])

View File

@ -146,6 +146,51 @@ func (x *CertSANSpec) GetFqdn() string {
return "" return ""
} }
// EncryptionSaltSpec describes the salt.
type EncryptionSaltSpec struct {
state protoimpl.MessageState `protogen:"open.v1"`
DiskSalt []byte `protobuf:"bytes,1,opt,name=disk_salt,json=diskSalt,proto3" json:"disk_salt,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *EncryptionSaltSpec) Reset() {
*x = EncryptionSaltSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *EncryptionSaltSpec) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*EncryptionSaltSpec) ProtoMessage() {}
func (x *EncryptionSaltSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use EncryptionSaltSpec.ProtoReflect.Descriptor instead.
func (*EncryptionSaltSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{2}
}
func (x *EncryptionSaltSpec) GetDiskSalt() []byte {
if x != nil {
return x.DiskSalt
}
return nil
}
// EtcdCertsSpec describes etcd certs secrets. // EtcdCertsSpec describes etcd certs secrets.
type EtcdCertsSpec struct { type EtcdCertsSpec struct {
state protoimpl.MessageState `protogen:"open.v1"` state protoimpl.MessageState `protogen:"open.v1"`
@ -159,7 +204,7 @@ type EtcdCertsSpec struct {
func (x *EtcdCertsSpec) Reset() { func (x *EtcdCertsSpec) Reset() {
*x = EtcdCertsSpec{} *x = EtcdCertsSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[2] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -171,7 +216,7 @@ func (x *EtcdCertsSpec) String() string {
func (*EtcdCertsSpec) ProtoMessage() {} func (*EtcdCertsSpec) ProtoMessage() {}
func (x *EtcdCertsSpec) ProtoReflect() protoreflect.Message { func (x *EtcdCertsSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[2] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[3]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -184,7 +229,7 @@ func (x *EtcdCertsSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use EtcdCertsSpec.ProtoReflect.Descriptor instead. // Deprecated: Use EtcdCertsSpec.ProtoReflect.Descriptor instead.
func (*EtcdCertsSpec) Descriptor() ([]byte, []int) { func (*EtcdCertsSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{2} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{3}
} }
func (x *EtcdCertsSpec) GetEtcd() *common.PEMEncodedCertificateAndKey { func (x *EtcdCertsSpec) GetEtcd() *common.PEMEncodedCertificateAndKey {
@ -225,7 +270,7 @@ type EtcdRootSpec struct {
func (x *EtcdRootSpec) Reset() { func (x *EtcdRootSpec) Reset() {
*x = EtcdRootSpec{} *x = EtcdRootSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[3] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -237,7 +282,7 @@ func (x *EtcdRootSpec) String() string {
func (*EtcdRootSpec) ProtoMessage() {} func (*EtcdRootSpec) ProtoMessage() {}
func (x *EtcdRootSpec) ProtoReflect() protoreflect.Message { func (x *EtcdRootSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[3] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[4]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -250,7 +295,7 @@ func (x *EtcdRootSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use EtcdRootSpec.ProtoReflect.Descriptor instead. // Deprecated: Use EtcdRootSpec.ProtoReflect.Descriptor instead.
func (*EtcdRootSpec) Descriptor() ([]byte, []int) { func (*EtcdRootSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{3} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{4}
} }
func (x *EtcdRootSpec) GetEtcdCa() *common.PEMEncodedCertificateAndKey { func (x *EtcdRootSpec) GetEtcdCa() *common.PEMEncodedCertificateAndKey {
@ -273,7 +318,7 @@ type KubeletSpec struct {
func (x *KubeletSpec) Reset() { func (x *KubeletSpec) Reset() {
*x = KubeletSpec{} *x = KubeletSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[4] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -285,7 +330,7 @@ func (x *KubeletSpec) String() string {
func (*KubeletSpec) ProtoMessage() {} func (*KubeletSpec) ProtoMessage() {}
func (x *KubeletSpec) ProtoReflect() protoreflect.Message { func (x *KubeletSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[4] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[5]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -298,7 +343,7 @@ func (x *KubeletSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use KubeletSpec.ProtoReflect.Descriptor instead. // Deprecated: Use KubeletSpec.ProtoReflect.Descriptor instead.
func (*KubeletSpec) Descriptor() ([]byte, []int) { func (*KubeletSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{4} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{5}
} }
func (x *KubeletSpec) GetEndpoint() *common.URL { func (x *KubeletSpec) GetEndpoint() *common.URL {
@ -342,7 +387,7 @@ type KubernetesCertsSpec struct {
func (x *KubernetesCertsSpec) Reset() { func (x *KubernetesCertsSpec) Reset() {
*x = KubernetesCertsSpec{} *x = KubernetesCertsSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[5] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -354,7 +399,7 @@ func (x *KubernetesCertsSpec) String() string {
func (*KubernetesCertsSpec) ProtoMessage() {} func (*KubernetesCertsSpec) ProtoMessage() {}
func (x *KubernetesCertsSpec) ProtoReflect() protoreflect.Message { func (x *KubernetesCertsSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[5] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[6]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -367,7 +412,7 @@ func (x *KubernetesCertsSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use KubernetesCertsSpec.ProtoReflect.Descriptor instead. // Deprecated: Use KubernetesCertsSpec.ProtoReflect.Descriptor instead.
func (*KubernetesCertsSpec) Descriptor() ([]byte, []int) { func (*KubernetesCertsSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{5} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{6}
} }
func (x *KubernetesCertsSpec) GetSchedulerKubeconfig() string { func (x *KubernetesCertsSpec) GetSchedulerKubeconfig() string {
@ -410,7 +455,7 @@ type KubernetesDynamicCertsSpec struct {
func (x *KubernetesDynamicCertsSpec) Reset() { func (x *KubernetesDynamicCertsSpec) Reset() {
*x = KubernetesDynamicCertsSpec{} *x = KubernetesDynamicCertsSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[6] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -422,7 +467,7 @@ func (x *KubernetesDynamicCertsSpec) String() string {
func (*KubernetesDynamicCertsSpec) ProtoMessage() {} func (*KubernetesDynamicCertsSpec) ProtoMessage() {}
func (x *KubernetesDynamicCertsSpec) ProtoReflect() protoreflect.Message { func (x *KubernetesDynamicCertsSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[6] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[7]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -435,7 +480,7 @@ func (x *KubernetesDynamicCertsSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use KubernetesDynamicCertsSpec.ProtoReflect.Descriptor instead. // Deprecated: Use KubernetesDynamicCertsSpec.ProtoReflect.Descriptor instead.
func (*KubernetesDynamicCertsSpec) Descriptor() ([]byte, []int) { func (*KubernetesDynamicCertsSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{6} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{7}
} }
func (x *KubernetesDynamicCertsSpec) GetApiServer() *common.PEMEncodedCertificateAndKey { func (x *KubernetesDynamicCertsSpec) GetApiServer() *common.PEMEncodedCertificateAndKey {
@ -482,7 +527,7 @@ type KubernetesRootSpec struct {
func (x *KubernetesRootSpec) Reset() { func (x *KubernetesRootSpec) Reset() {
*x = KubernetesRootSpec{} *x = KubernetesRootSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[7] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -494,7 +539,7 @@ func (x *KubernetesRootSpec) String() string {
func (*KubernetesRootSpec) ProtoMessage() {} func (*KubernetesRootSpec) ProtoMessage() {}
func (x *KubernetesRootSpec) ProtoReflect() protoreflect.Message { func (x *KubernetesRootSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[7] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[8]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -507,7 +552,7 @@ func (x *KubernetesRootSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use KubernetesRootSpec.ProtoReflect.Descriptor instead. // Deprecated: Use KubernetesRootSpec.ProtoReflect.Descriptor instead.
func (*KubernetesRootSpec) Descriptor() ([]byte, []int) { func (*KubernetesRootSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{7} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{8}
} }
func (x *KubernetesRootSpec) GetName() string { func (x *KubernetesRootSpec) GetName() string {
@ -618,7 +663,7 @@ type MaintenanceRootSpec struct {
func (x *MaintenanceRootSpec) Reset() { func (x *MaintenanceRootSpec) Reset() {
*x = MaintenanceRootSpec{} *x = MaintenanceRootSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[8] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -630,7 +675,7 @@ func (x *MaintenanceRootSpec) String() string {
func (*MaintenanceRootSpec) ProtoMessage() {} func (*MaintenanceRootSpec) ProtoMessage() {}
func (x *MaintenanceRootSpec) ProtoReflect() protoreflect.Message { func (x *MaintenanceRootSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[8] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[9]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -643,7 +688,7 @@ func (x *MaintenanceRootSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use MaintenanceRootSpec.ProtoReflect.Descriptor instead. // Deprecated: Use MaintenanceRootSpec.ProtoReflect.Descriptor instead.
func (*MaintenanceRootSpec) Descriptor() ([]byte, []int) { func (*MaintenanceRootSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{8} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{9}
} }
func (x *MaintenanceRootSpec) GetCa() *common.PEMEncodedCertificateAndKey { func (x *MaintenanceRootSpec) GetCa() *common.PEMEncodedCertificateAndKey {
@ -664,7 +709,7 @@ type MaintenanceServiceCertsSpec struct {
func (x *MaintenanceServiceCertsSpec) Reset() { func (x *MaintenanceServiceCertsSpec) Reset() {
*x = MaintenanceServiceCertsSpec{} *x = MaintenanceServiceCertsSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[9] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -676,7 +721,7 @@ func (x *MaintenanceServiceCertsSpec) String() string {
func (*MaintenanceServiceCertsSpec) ProtoMessage() {} func (*MaintenanceServiceCertsSpec) ProtoMessage() {}
func (x *MaintenanceServiceCertsSpec) ProtoReflect() protoreflect.Message { func (x *MaintenanceServiceCertsSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[9] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[10]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -689,7 +734,7 @@ func (x *MaintenanceServiceCertsSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use MaintenanceServiceCertsSpec.ProtoReflect.Descriptor instead. // Deprecated: Use MaintenanceServiceCertsSpec.ProtoReflect.Descriptor instead.
func (*MaintenanceServiceCertsSpec) Descriptor() ([]byte, []int) { func (*MaintenanceServiceCertsSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{9} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{10}
} }
func (x *MaintenanceServiceCertsSpec) GetCa() *common.PEMEncodedCertificateAndKey { func (x *MaintenanceServiceCertsSpec) GetCa() *common.PEMEncodedCertificateAndKey {
@ -720,7 +765,7 @@ type OSRootSpec struct {
func (x *OSRootSpec) Reset() { func (x *OSRootSpec) Reset() {
*x = OSRootSpec{} *x = OSRootSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[10] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -732,7 +777,7 @@ func (x *OSRootSpec) String() string {
func (*OSRootSpec) ProtoMessage() {} func (*OSRootSpec) ProtoMessage() {}
func (x *OSRootSpec) ProtoReflect() protoreflect.Message { func (x *OSRootSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[10] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[11]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -745,7 +790,7 @@ func (x *OSRootSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use OSRootSpec.ProtoReflect.Descriptor instead. // Deprecated: Use OSRootSpec.ProtoReflect.Descriptor instead.
func (*OSRootSpec) Descriptor() ([]byte, []int) { func (*OSRootSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{10} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{11}
} }
func (x *OSRootSpec) GetIssuingCa() *common.PEMEncodedCertificateAndKey { func (x *OSRootSpec) GetIssuingCa() *common.PEMEncodedCertificateAndKey {
@ -794,7 +839,7 @@ type TrustdCertsSpec struct {
func (x *TrustdCertsSpec) Reset() { func (x *TrustdCertsSpec) Reset() {
*x = TrustdCertsSpec{} *x = TrustdCertsSpec{}
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[11] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[12]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -806,7 +851,7 @@ func (x *TrustdCertsSpec) String() string {
func (*TrustdCertsSpec) ProtoMessage() {} func (*TrustdCertsSpec) ProtoMessage() {}
func (x *TrustdCertsSpec) ProtoReflect() protoreflect.Message { func (x *TrustdCertsSpec) ProtoReflect() protoreflect.Message {
mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[11] mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[12]
if x != nil { if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -819,7 +864,7 @@ func (x *TrustdCertsSpec) ProtoReflect() protoreflect.Message {
// Deprecated: Use TrustdCertsSpec.ProtoReflect.Descriptor instead. // Deprecated: Use TrustdCertsSpec.ProtoReflect.Descriptor instead.
func (*TrustdCertsSpec) Descriptor() ([]byte, []int) { func (*TrustdCertsSpec) Descriptor() ([]byte, []int) {
return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{11} return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{12}
} }
func (x *TrustdCertsSpec) GetServer() *common.PEMEncodedCertificateAndKey { func (x *TrustdCertsSpec) GetServer() *common.PEMEncodedCertificateAndKey {
@ -848,7 +893,9 @@ const file_resource_definitions_secrets_secrets_proto_rawDesc = "" +
"\vCertSANSpec\x12 \n" + "\vCertSANSpec\x12 \n" +
"\x04i_ps\x18\x01 \x03(\v2\r.common.NetIPR\x03iPs\x12\x1b\n" + "\x04i_ps\x18\x01 \x03(\v2\r.common.NetIPR\x03iPs\x12\x1b\n" +
"\tdns_names\x18\x02 \x03(\tR\bdnsNames\x12\x12\n" + "\tdns_names\x18\x02 \x03(\tR\bdnsNames\x12\x12\n" +
"\x04fqdn\x18\x03 \x01(\tR\x04fqdn\"\x9b\x02\n" + "\x04fqdn\x18\x03 \x01(\tR\x04fqdn\"1\n" +
"\x12EncryptionSaltSpec\x12\x1b\n" +
"\tdisk_salt\x18\x01 \x01(\fR\bdiskSalt\"\x9b\x02\n" +
"\rEtcdCertsSpec\x127\n" + "\rEtcdCertsSpec\x127\n" +
"\x04etcd\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x04etcd\x12@\n" + "\x04etcd\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x04etcd\x12@\n" +
"\tetcd_peer\x18\x02 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\betcdPeer\x12B\n" + "\tetcd_peer\x18\x02 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\betcdPeer\x12B\n" +
@ -923,56 +970,57 @@ func file_resource_definitions_secrets_secrets_proto_rawDescGZIP() []byte {
return file_resource_definitions_secrets_secrets_proto_rawDescData return file_resource_definitions_secrets_secrets_proto_rawDescData
} }
var file_resource_definitions_secrets_secrets_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_resource_definitions_secrets_secrets_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
var file_resource_definitions_secrets_secrets_proto_goTypes = []any{ var file_resource_definitions_secrets_secrets_proto_goTypes = []any{
(*APICertsSpec)(nil), // 0: talos.resource.definitions.secrets.APICertsSpec (*APICertsSpec)(nil), // 0: talos.resource.definitions.secrets.APICertsSpec
(*CertSANSpec)(nil), // 1: talos.resource.definitions.secrets.CertSANSpec (*CertSANSpec)(nil), // 1: talos.resource.definitions.secrets.CertSANSpec
(*EtcdCertsSpec)(nil), // 2: talos.resource.definitions.secrets.EtcdCertsSpec (*EncryptionSaltSpec)(nil), // 2: talos.resource.definitions.secrets.EncryptionSaltSpec
(*EtcdRootSpec)(nil), // 3: talos.resource.definitions.secrets.EtcdRootSpec (*EtcdCertsSpec)(nil), // 3: talos.resource.definitions.secrets.EtcdCertsSpec
(*KubeletSpec)(nil), // 4: talos.resource.definitions.secrets.KubeletSpec (*EtcdRootSpec)(nil), // 4: talos.resource.definitions.secrets.EtcdRootSpec
(*KubernetesCertsSpec)(nil), // 5: talos.resource.definitions.secrets.KubernetesCertsSpec (*KubeletSpec)(nil), // 5: talos.resource.definitions.secrets.KubeletSpec
(*KubernetesDynamicCertsSpec)(nil), // 6: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec (*KubernetesCertsSpec)(nil), // 6: talos.resource.definitions.secrets.KubernetesCertsSpec
(*KubernetesRootSpec)(nil), // 7: talos.resource.definitions.secrets.KubernetesRootSpec (*KubernetesDynamicCertsSpec)(nil), // 7: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec
(*MaintenanceRootSpec)(nil), // 8: talos.resource.definitions.secrets.MaintenanceRootSpec (*KubernetesRootSpec)(nil), // 8: talos.resource.definitions.secrets.KubernetesRootSpec
(*MaintenanceServiceCertsSpec)(nil), // 9: talos.resource.definitions.secrets.MaintenanceServiceCertsSpec (*MaintenanceRootSpec)(nil), // 9: talos.resource.definitions.secrets.MaintenanceRootSpec
(*OSRootSpec)(nil), // 10: talos.resource.definitions.secrets.OSRootSpec (*MaintenanceServiceCertsSpec)(nil), // 10: talos.resource.definitions.secrets.MaintenanceServiceCertsSpec
(*TrustdCertsSpec)(nil), // 11: talos.resource.definitions.secrets.TrustdCertsSpec (*OSRootSpec)(nil), // 11: talos.resource.definitions.secrets.OSRootSpec
(*common.PEMEncodedCertificateAndKey)(nil), // 12: common.PEMEncodedCertificateAndKey (*TrustdCertsSpec)(nil), // 12: talos.resource.definitions.secrets.TrustdCertsSpec
(*common.PEMEncodedCertificate)(nil), // 13: common.PEMEncodedCertificate (*common.PEMEncodedCertificateAndKey)(nil), // 13: common.PEMEncodedCertificateAndKey
(*common.NetIP)(nil), // 14: common.NetIP (*common.PEMEncodedCertificate)(nil), // 14: common.PEMEncodedCertificate
(*common.URL)(nil), // 15: common.URL (*common.NetIP)(nil), // 15: common.NetIP
(*common.PEMEncodedKey)(nil), // 16: common.PEMEncodedKey (*common.URL)(nil), // 16: common.URL
(*common.PEMEncodedKey)(nil), // 17: common.PEMEncodedKey
} }
var file_resource_definitions_secrets_secrets_proto_depIdxs = []int32{ var file_resource_definitions_secrets_secrets_proto_depIdxs = []int32{
12, // 0: talos.resource.definitions.secrets.APICertsSpec.client:type_name -> common.PEMEncodedCertificateAndKey 13, // 0: talos.resource.definitions.secrets.APICertsSpec.client:type_name -> common.PEMEncodedCertificateAndKey
12, // 1: talos.resource.definitions.secrets.APICertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey 13, // 1: talos.resource.definitions.secrets.APICertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey
13, // 2: talos.resource.definitions.secrets.APICertsSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 14, // 2: talos.resource.definitions.secrets.APICertsSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate
14, // 3: talos.resource.definitions.secrets.CertSANSpec.i_ps:type_name -> common.NetIP 15, // 3: talos.resource.definitions.secrets.CertSANSpec.i_ps:type_name -> common.NetIP
12, // 4: talos.resource.definitions.secrets.EtcdCertsSpec.etcd:type_name -> common.PEMEncodedCertificateAndKey 13, // 4: talos.resource.definitions.secrets.EtcdCertsSpec.etcd:type_name -> common.PEMEncodedCertificateAndKey
12, // 5: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_peer:type_name -> common.PEMEncodedCertificateAndKey 13, // 5: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_peer:type_name -> common.PEMEncodedCertificateAndKey
12, // 6: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_admin:type_name -> common.PEMEncodedCertificateAndKey 13, // 6: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_admin:type_name -> common.PEMEncodedCertificateAndKey
12, // 7: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_api_server:type_name -> common.PEMEncodedCertificateAndKey 13, // 7: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_api_server:type_name -> common.PEMEncodedCertificateAndKey
12, // 8: talos.resource.definitions.secrets.EtcdRootSpec.etcd_ca:type_name -> common.PEMEncodedCertificateAndKey 13, // 8: talos.resource.definitions.secrets.EtcdRootSpec.etcd_ca:type_name -> common.PEMEncodedCertificateAndKey
15, // 9: talos.resource.definitions.secrets.KubeletSpec.endpoint:type_name -> common.URL 16, // 9: talos.resource.definitions.secrets.KubeletSpec.endpoint:type_name -> common.URL
13, // 10: talos.resource.definitions.secrets.KubeletSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 14, // 10: talos.resource.definitions.secrets.KubeletSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate
12, // 11: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.api_server:type_name -> common.PEMEncodedCertificateAndKey 13, // 11: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.api_server:type_name -> common.PEMEncodedCertificateAndKey
12, // 12: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.api_server_kubelet_client:type_name -> common.PEMEncodedCertificateAndKey 13, // 12: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.api_server_kubelet_client:type_name -> common.PEMEncodedCertificateAndKey
12, // 13: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.front_proxy:type_name -> common.PEMEncodedCertificateAndKey 13, // 13: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.front_proxy:type_name -> common.PEMEncodedCertificateAndKey
15, // 14: talos.resource.definitions.secrets.KubernetesRootSpec.endpoint:type_name -> common.URL 16, // 14: talos.resource.definitions.secrets.KubernetesRootSpec.endpoint:type_name -> common.URL
15, // 15: talos.resource.definitions.secrets.KubernetesRootSpec.local_endpoint:type_name -> common.URL 16, // 15: talos.resource.definitions.secrets.KubernetesRootSpec.local_endpoint:type_name -> common.URL
12, // 16: talos.resource.definitions.secrets.KubernetesRootSpec.issuing_ca:type_name -> common.PEMEncodedCertificateAndKey 13, // 16: talos.resource.definitions.secrets.KubernetesRootSpec.issuing_ca:type_name -> common.PEMEncodedCertificateAndKey
16, // 17: talos.resource.definitions.secrets.KubernetesRootSpec.service_account:type_name -> common.PEMEncodedKey 17, // 17: talos.resource.definitions.secrets.KubernetesRootSpec.service_account:type_name -> common.PEMEncodedKey
12, // 18: talos.resource.definitions.secrets.KubernetesRootSpec.aggregator_ca:type_name -> common.PEMEncodedCertificateAndKey 13, // 18: talos.resource.definitions.secrets.KubernetesRootSpec.aggregator_ca:type_name -> common.PEMEncodedCertificateAndKey
14, // 19: talos.resource.definitions.secrets.KubernetesRootSpec.api_server_ips:type_name -> common.NetIP 15, // 19: talos.resource.definitions.secrets.KubernetesRootSpec.api_server_ips:type_name -> common.NetIP
13, // 20: talos.resource.definitions.secrets.KubernetesRootSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 14, // 20: talos.resource.definitions.secrets.KubernetesRootSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate
12, // 21: talos.resource.definitions.secrets.MaintenanceRootSpec.ca:type_name -> common.PEMEncodedCertificateAndKey 13, // 21: talos.resource.definitions.secrets.MaintenanceRootSpec.ca:type_name -> common.PEMEncodedCertificateAndKey
12, // 22: talos.resource.definitions.secrets.MaintenanceServiceCertsSpec.ca:type_name -> common.PEMEncodedCertificateAndKey 13, // 22: talos.resource.definitions.secrets.MaintenanceServiceCertsSpec.ca:type_name -> common.PEMEncodedCertificateAndKey
12, // 23: talos.resource.definitions.secrets.MaintenanceServiceCertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey 13, // 23: talos.resource.definitions.secrets.MaintenanceServiceCertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey
12, // 24: talos.resource.definitions.secrets.OSRootSpec.issuing_ca:type_name -> common.PEMEncodedCertificateAndKey 13, // 24: talos.resource.definitions.secrets.OSRootSpec.issuing_ca:type_name -> common.PEMEncodedCertificateAndKey
14, // 25: talos.resource.definitions.secrets.OSRootSpec.cert_sani_ps:type_name -> common.NetIP 15, // 25: talos.resource.definitions.secrets.OSRootSpec.cert_sani_ps:type_name -> common.NetIP
13, // 26: talos.resource.definitions.secrets.OSRootSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 14, // 26: talos.resource.definitions.secrets.OSRootSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate
12, // 27: talos.resource.definitions.secrets.TrustdCertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey 13, // 27: talos.resource.definitions.secrets.TrustdCertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey
13, // 28: talos.resource.definitions.secrets.TrustdCertsSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 14, // 28: talos.resource.definitions.secrets.TrustdCertsSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate
29, // [29:29] is the sub-list for method output_type 29, // [29:29] is the sub-list for method output_type
29, // [29:29] is the sub-list for method input_type 29, // [29:29] is the sub-list for method input_type
29, // [29:29] is the sub-list for extension type_name 29, // [29:29] is the sub-list for extension type_name
@ -991,7 +1039,7 @@ func file_resource_definitions_secrets_secrets_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_secrets_secrets_proto_rawDesc), len(file_resource_definitions_secrets_secrets_proto_rawDesc)), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_secrets_secrets_proto_rawDesc), len(file_resource_definitions_secrets_secrets_proto_rawDesc)),
NumEnums: 0, NumEnums: 0,
NumMessages: 12, NumMessages: 13,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View File

@ -196,6 +196,46 @@ func (m *CertSANSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
return len(dAtA) - i, nil return len(dAtA) - i, nil
} }
func (m *EncryptionSaltSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVT(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *EncryptionSaltSpec) MarshalToVT(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVT(dAtA[:size])
}
func (m *EncryptionSaltSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
if len(m.DiskSalt) > 0 {
i -= len(m.DiskSalt)
copy(dAtA[i:], m.DiskSalt)
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DiskSalt)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *EtcdCertsSpec) MarshalVT() (dAtA []byte, err error) { func (m *EtcdCertsSpec) MarshalVT() (dAtA []byte, err error) {
if m == nil { if m == nil {
return nil, nil return nil, nil
@ -1271,6 +1311,20 @@ func (m *CertSANSpec) SizeVT() (n int) {
return n return n
} }
func (m *EncryptionSaltSpec) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.DiskSalt)
if l > 0 {
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
}
n += len(m.unknownFields)
return n
}
func (m *EtcdCertsSpec) SizeVT() (n int) { func (m *EtcdCertsSpec) SizeVT() (n int) {
if m == nil { if m == nil {
return 0 return 0
@ -2035,6 +2089,91 @@ func (m *CertSANSpec) UnmarshalVT(dAtA []byte) error {
} }
return nil return nil
} }
func (m *EncryptionSaltSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: EncryptionSaltSpec: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: EncryptionSaltSpec: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DiskSalt", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return protohelpers.ErrIntOverflow
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return protohelpers.ErrInvalidLength
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return protohelpers.ErrInvalidLength
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.DiskSalt = append(m.DiskSalt[:0], dAtA[iNdEx:postIndex]...)
if m.DiskSalt == nil {
m.DiskSalt = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := protohelpers.Skip(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return protohelpers.ErrInvalidLength
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...)
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *EtcdCertsSpec) UnmarshalVT(dAtA []byte) error { func (m *EtcdCertsSpec) UnmarshalVT(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0

View File

@ -407,6 +407,7 @@ type EncryptionKey interface {
KMS() EncryptionKeyKMS KMS() EncryptionKeyKMS
Slot() int Slot() int
TPM() EncryptionKeyTPM TPM() EncryptionKeyTPM
LockToSTATE() bool
} }
// EncryptionKeyStatic ephemeral encryption key. // EncryptionKeyStatic ephemeral encryption key.

View File

@ -52,6 +52,13 @@
"description": "Enable TPM based disk encryption.\n", "description": "Enable TPM based disk encryption.\n",
"markdownDescription": "Enable TPM based disk encryption.", "markdownDescription": "Enable TPM based disk encryption.",
"x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n" "x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n"
},
"lockToState": {
"type": "boolean",
"title": "lockToState",
"description": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\n",
"markdownDescription": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.",
"x-intellij-html-description": "\u003cp\u003eLock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\u003c/p\u003e\n"
} }
}, },
"additionalProperties": false, "additionalProperties": false,

View File

@ -148,6 +148,13 @@ func (EncryptionKey) Doc() *encoder.Doc {
Description: "Enable TPM based disk encryption.", Description: "Enable TPM based disk encryption.",
Comments: [3]string{"" /* encoder.HeadComment */, "Enable TPM based disk encryption." /* encoder.LineComment */, "" /* encoder.FootComment */}, Comments: [3]string{"" /* encoder.HeadComment */, "Enable TPM based disk encryption." /* encoder.LineComment */, "" /* encoder.FootComment */},
}, },
{
Name: "lockToState",
Type: "bool",
Note: "",
Description: "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.",
Comments: [3]string{"" /* encoder.HeadComment */, "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes." /* encoder.LineComment */, "" /* encoder.FootComment */},
},
}, },
} }

View File

@ -63,6 +63,10 @@ func (o *RawVolumeConfigV1Alpha1) DeepCopy() *RawVolumeConfigV1Alpha1 {
*cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll
} }
} }
if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil {
cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool)
*cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE
}
} }
} }
if o.EncryptionSpec.EncryptionPerfOptions != nil { if o.EncryptionSpec.EncryptionPerfOptions != nil {
@ -119,6 +123,10 @@ func (o *SwapVolumeConfigV1Alpha1) DeepCopy() *SwapVolumeConfigV1Alpha1 {
*cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll
} }
} }
if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil {
cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool)
*cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE
}
} }
} }
if o.EncryptionSpec.EncryptionPerfOptions != nil { if o.EncryptionSpec.EncryptionPerfOptions != nil {
@ -179,6 +187,10 @@ func (o *UserVolumeConfigV1Alpha1) DeepCopy() *UserVolumeConfigV1Alpha1 {
*cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll
} }
} }
if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil {
cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool)
*cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE
}
} }
} }
if o.EncryptionSpec.EncryptionPerfOptions != nil { if o.EncryptionSpec.EncryptionPerfOptions != nil {
@ -235,6 +247,10 @@ func (o *VolumeConfigV1Alpha1) DeepCopy() *VolumeConfigV1Alpha1 {
*cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll
} }
} }
if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil {
cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool)
*cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE
}
} }
} }
if o.EncryptionSpec.EncryptionPerfOptions != nil { if o.EncryptionSpec.EncryptionPerfOptions != nil {

View File

@ -99,6 +99,12 @@ type EncryptionKey struct {
// description: > // description: >
// Enable TPM based disk encryption. // Enable TPM based disk encryption.
KeyTPM *EncryptionKeyTPM `yaml:"tpm,omitempty"` KeyTPM *EncryptionKeyTPM `yaml:"tpm,omitempty"`
// description: >
// Lock the disk encryption key to the random salt stored in the STATE partition.
// This is useful to prevent the volume from being unlocked if STATE partition is compromised
// or replaced. It is recommended to use this option with TPM disk encryption for
// non-STATE volumes.
KeyLockToSTATE *bool `yaml:"lockToState,omitempty"`
} }
// EncryptionKeyStatic represents throw away key type. // EncryptionKeyStatic represents throw away key type.
@ -213,6 +219,11 @@ func (k EncryptionKey) Slot() int {
return k.KeySlot return k.KeySlot
} }
// LockToSTATE implements the config.Provider interface.
func (k EncryptionKey) LockToSTATE() bool {
return pointer.SafeDeref(k.KeyLockToSTATE)
}
// Static implements the config.Provider interface. // Static implements the config.Provider interface.
func (k EncryptionKey) Static() config.EncryptionKeyStatic { func (k EncryptionKey) Static() config.EncryptionKeyStatic {
if k.KeyStatic == nil { if k.KeyStatic == nil {

View File

@ -12,6 +12,7 @@ import (
"slices" "slices"
"github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/optional"
"github.com/siderolabs/go-pointer"
"github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel"
"github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/cel/celenv"
@ -177,6 +178,13 @@ func (s *VolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Op
if !s.ProvisioningSpec.IsZero() { if !s.ProvisioningSpec.IsZero() {
validationErrors = errors.Join(validationErrors, fmt.Errorf("provisioning config is not allowed for the %q volume", s.MetaName)) validationErrors = errors.Join(validationErrors, fmt.Errorf("provisioning config is not allowed for the %q volume", s.MetaName))
} }
for _, key := range s.EncryptionSpec.EncryptionKeys {
if pointer.SafeDeref(key.KeyLockToSTATE) {
// state-locked keys are not allowed
validationErrors = errors.Join(validationErrors, fmt.Errorf("state-locked key is not allowed for the %q volume", s.MetaName))
}
}
} }
extraWarnings, extraErrors := s.ProvisioningSpec.Validate(false) extraWarnings, extraErrors := s.ProvisioningSpec.Validate(false)

View File

@ -9,6 +9,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/siderolabs/go-pointer"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -185,6 +186,37 @@ func TestVolumeConfigValidate(t *testing.T) {
expectedErrors: "provisioning config is not allowed for the \"STATE\" volume", expectedErrors: "provisioning config is not allowed for the \"STATE\" volume",
}, },
{
name: "state encryption lock to state",
cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 {
c := block.NewVolumeConfigV1Alpha1()
c.MetaName = constants.StatePartitionLabel
c.EncryptionSpec = block.EncryptionSpec{
EncryptionProvider: blockres.EncryptionProviderLUKS2,
EncryptionKeys: []block.EncryptionKey{
{
KeySlot: 0,
KeyStatic: &block.EncryptionKeyStatic{
KeyData: "topsecret",
},
},
{
KeySlot: 1,
KeyStatic: &block.EncryptionKeyStatic{
KeyData: "topsecret2",
},
KeyLockToSTATE: pointer.To(true),
},
},
}
return c
},
expectedErrors: "state-locked key is not allowed for the \"STATE\" volume",
},
{ {
name: "valid", name: "valid",

View File

@ -1604,6 +1604,12 @@ func (e *EncryptionKey) TPM() config.EncryptionKeyTPM {
return e.KeyTPM return e.KeyTPM
} }
// LockToSTATE implements the config.Provider interface.
func (e *EncryptionKey) LockToSTATE() bool {
// not supported in v1alpha1
return false
}
// String implements the config.Provider interface. // String implements the config.Provider interface.
func (e *EncryptionKeyNodeID) String() string { func (e *EncryptionKeyNodeID) String() string {
return "nodeid" return "nodeid"

View File

@ -1270,6 +1270,12 @@ const (
// ExtensionSPDXPath is the path to the SBOM file(s) provided by system extensions. // ExtensionSPDXPath is the path to the SBOM file(s) provided by system extensions.
ExtensionSPDXPath = "/usr/local/share/spdx" ExtensionSPDXPath = "/usr/local/share/spdx"
// EncryptionSaltFilename is the filename for the encryption salt file.
EncryptionSaltFilename = "encryption-salt.yaml"
// DiskEncryptionSaltSize is the size of the disk encryption salt in bytes.
DiskEncryptionSaltSize = 32
) )
// See https://linux.die.net/man/3/klogctl // See https://linux.die.net/man/3/klogctl

View File

@ -137,6 +137,7 @@ type EncryptionSpec struct {
type EncryptionKey struct { type EncryptionKey struct {
Slot int `yaml:"slot" protobuf:"1"` Slot int `yaml:"slot" protobuf:"1"`
Type EncryptionKeyType `yaml:"type" protobuf:"2"` Type EncryptionKeyType `yaml:"type" protobuf:"2"`
LockToSTATE bool `yaml:"lockToState,omitempty" protobuf:"6"`
// Only for Type == "static": // Only for Type == "static":
StaticPassphrase yamlutils.StringBytes `yaml:"staticPassphrase,omitempty" protobuf:"3"` StaticPassphrase yamlutils.StringBytes `yaml:"staticPassphrase,omitempty" protobuf:"3"`

View File

@ -2,7 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this // License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Code generated by "deep-copy -type APICertsSpec -type CertSANSpec -type EtcdCertsSpec -type EtcdRootSpec -type KubeletSpec -type KubernetesCertsSpec -type KubernetesDynamicCertsSpec -type KubernetesRootSpec -type MaintenanceServiceCertsSpec -type MaintenanceRootSpec -type OSRootSpec -type TrustdCertsSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. // Code generated by "deep-copy -type APICertsSpec -type CertSANSpec -type EtcdCertsSpec -type EtcdRootSpec -type EncryptionSaltSpec -type KubeletSpec -type KubernetesCertsSpec -type KubernetesDynamicCertsSpec -type KubernetesRootSpec -type MaintenanceServiceCertsSpec -type MaintenanceRootSpec -type OSRootSpec -type TrustdCertsSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT.
package secrets package secrets
@ -75,6 +75,16 @@ func (o EtcdRootSpec) DeepCopy() EtcdRootSpec {
return cp return cp
} }
// DeepCopy generates a deep copy of EncryptionSaltSpec.
func (o EncryptionSaltSpec) DeepCopy() EncryptionSaltSpec {
var cp EncryptionSaltSpec = o
if o.DiskSalt != nil {
cp.DiskSalt = make([]byte, len(o.DiskSalt))
copy(cp.DiskSalt, o.DiskSalt)
}
return cp
}
// DeepCopy generates a deep copy of KubeletSpec. // DeepCopy generates a deep copy of KubeletSpec.
func (o KubeletSpec) DeepCopy() KubeletSpec { func (o KubeletSpec) DeepCopy() KubeletSpec {
var cp KubeletSpec = o var cp KubeletSpec = o

View File

@ -0,0 +1,59 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package secrets
import (
"github.com/cosi-project/runtime/pkg/resource"
"github.com/cosi-project/runtime/pkg/resource/meta"
"github.com/cosi-project/runtime/pkg/resource/protobuf"
"github.com/cosi-project/runtime/pkg/resource/typed"
"github.com/siderolabs/talos/pkg/machinery/proto"
)
// EncryptionSaltType is type of EncryptionSalt resource.
const EncryptionSaltType = resource.Type("EncryptionSalts.secrets.talos.dev")
// EncryptionSaltID is a resource ID of singleton instance.
const EncryptionSaltID = resource.ID("salt")
// EncryptionSalt contains salt data used to mix in into disk encryption keys.
type EncryptionSalt = typed.Resource[EncryptionSaltSpec, EncryptionSaltExtension]
// EncryptionSaltSpec describes the salt.
//
//gotagsrewrite:gen
type EncryptionSaltSpec struct {
DiskSalt []byte `yaml:"diskSalt" protobuf:"1"`
}
// NewEncryptionSalt initializes a EncryptionSalt resource.
func NewEncryptionSalt() *EncryptionSalt {
return typed.NewResource[EncryptionSaltSpec, EncryptionSaltExtension](
resource.NewMetadata(NamespaceName, EncryptionSaltType, EncryptionSaltID, resource.VersionUndefined),
EncryptionSaltSpec{},
)
}
// EncryptionSaltExtension provides auxiliary methods for EncryptionSalt.
type EncryptionSaltExtension struct{}
// ResourceDefinition implements meta.ResourceDefinitionProvider interface.
func (EncryptionSaltExtension) ResourceDefinition() meta.ResourceDefinitionSpec {
return meta.ResourceDefinitionSpec{
Type: EncryptionSaltType,
Aliases: []resource.Type{},
DefaultNamespace: NamespaceName,
Sensitivity: meta.Sensitive,
}
}
func init() {
proto.RegisterDefaultTypes()
if err := protobuf.RegisterDynamic[EncryptionSaltSpec](EncryptionSaltType, &EncryptionSalt{}); err != nil {
panic(err)
}
}

View File

@ -10,4 +10,4 @@ import "github.com/cosi-project/runtime/pkg/resource"
// NamespaceName contains resources containing secret material. // NamespaceName contains resources containing secret material.
const NamespaceName resource.Namespace = "secrets" const NamespaceName resource.Namespace = "secrets"
//go:generate deep-copy -type APICertsSpec -type CertSANSpec -type EtcdCertsSpec -type EtcdRootSpec -type KubeletSpec -type KubernetesCertsSpec -type KubernetesDynamicCertsSpec -type KubernetesRootSpec -type MaintenanceServiceCertsSpec -type MaintenanceRootSpec -type OSRootSpec -type TrustdCertsSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . //go:generate deep-copy -type APICertsSpec -type CertSANSpec -type EtcdCertsSpec -type EtcdRootSpec -type EncryptionSaltSpec -type KubeletSpec -type KubernetesCertsSpec -type KubernetesDynamicCertsSpec -type KubernetesRootSpec -type MaintenanceServiceCertsSpec -type MaintenanceRootSpec -type OSRootSpec -type TrustdCertsSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go .

View File

@ -26,6 +26,7 @@ func TestRegisterResource(t *testing.T) {
for _, resource := range []meta.ResourceWithRD{ for _, resource := range []meta.ResourceWithRD{
&secrets.API{}, &secrets.API{},
&secrets.CertSAN{}, &secrets.CertSAN{},
&secrets.EncryptionSalt{},
&secrets.Etcd{}, &secrets.Etcd{},
&secrets.EtcdRoot{}, &secrets.EtcdRoot{},
&secrets.Kubelet{}, &secrets.Kubelet{},

View File

@ -302,6 +302,7 @@ description: Talos gRPC API reference.
- [resource/definitions/secrets/secrets.proto](#resource/definitions/secrets/secrets.proto) - [resource/definitions/secrets/secrets.proto](#resource/definitions/secrets/secrets.proto)
- [APICertsSpec](#talos.resource.definitions.secrets.APICertsSpec) - [APICertsSpec](#talos.resource.definitions.secrets.APICertsSpec)
- [CertSANSpec](#talos.resource.definitions.secrets.CertSANSpec) - [CertSANSpec](#talos.resource.definitions.secrets.CertSANSpec)
- [EncryptionSaltSpec](#talos.resource.definitions.secrets.EncryptionSaltSpec)
- [EtcdCertsSpec](#talos.resource.definitions.secrets.EtcdCertsSpec) - [EtcdCertsSpec](#talos.resource.definitions.secrets.EtcdCertsSpec)
- [EtcdRootSpec](#talos.resource.definitions.secrets.EtcdRootSpec) - [EtcdRootSpec](#talos.resource.definitions.secrets.EtcdRootSpec)
- [KubeletSpec](#talos.resource.definitions.secrets.KubeletSpec) - [KubeletSpec](#talos.resource.definitions.secrets.KubeletSpec)
@ -989,6 +990,7 @@ EncryptionKey is the spec for volume encryption key.
| static_passphrase | [bytes](#bytes) | | | | static_passphrase | [bytes](#bytes) | | |
| kms_endpoint | [string](#string) | | | | kms_endpoint | [string](#string) | | |
| tpm_check_secureboot_status_on_enroll | [bool](#bool) | | | | tpm_check_secureboot_status_on_enroll | [bool](#bool) | | |
| lock_to_state | [bool](#bool) | | |
@ -5546,6 +5548,21 @@ CertSANSpec describes fields of the cert SANs.
<a name="talos.resource.definitions.secrets.EncryptionSaltSpec"></a>
### EncryptionSaltSpec
EncryptionSaltSpec describes the salt.
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| disk_salt | [bytes](#bytes) | | |
<a name="talos.resource.definitions.secrets.EtcdCertsSpec"></a> <a name="talos.resource.definitions.secrets.EtcdCertsSpec"></a>
### EtcdCertsSpec ### EtcdCertsSpec

View File

@ -177,6 +177,7 @@ EncryptionKey represents configuration for disk encryption key.
|`nodeID` |<a href="#RawVolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | | |`nodeID` |<a href="#RawVolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | |
|`kms` |<a href="#RawVolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | | |`kms` |<a href="#RawVolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | |
|`tpm` |<a href="#RawVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | | |`tpm` |<a href="#RawVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | |
|`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | |

View File

@ -174,6 +174,7 @@ EncryptionKey represents configuration for disk encryption key.
|`nodeID` |<a href="#SwapVolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | | |`nodeID` |<a href="#SwapVolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | |
|`kms` |<a href="#SwapVolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | | |`kms` |<a href="#SwapVolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | |
|`tpm` |<a href="#SwapVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | | |`tpm` |<a href="#SwapVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | |
|`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | |

View File

@ -204,6 +204,7 @@ EncryptionKey represents configuration for disk encryption key.
|`nodeID` |<a href="#UserVolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | | |`nodeID` |<a href="#UserVolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | |
|`kms` |<a href="#UserVolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | | |`kms` |<a href="#UserVolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | |
|`tpm` |<a href="#UserVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | | |`tpm` |<a href="#UserVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | |
|`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | |

View File

@ -174,6 +174,7 @@ EncryptionKey represents configuration for disk encryption key.
|`nodeID` |<a href="#VolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | | |`nodeID` |<a href="#VolumeConfig.encryption.keys..nodeID">EncryptionKeyNodeID</a> |Deterministically generated key from the node UUID and PartitionLabel. | |
|`kms` |<a href="#VolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | | |`kms` |<a href="#VolumeConfig.encryption.keys..kms">EncryptionKeyKMS</a> |KMS managed encryption key. | |
|`tpm` |<a href="#VolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | | |`tpm` |<a href="#VolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | |
|`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | |

View File

@ -52,6 +52,13 @@
"description": "Enable TPM based disk encryption.\n", "description": "Enable TPM based disk encryption.\n",
"markdownDescription": "Enable TPM based disk encryption.", "markdownDescription": "Enable TPM based disk encryption.",
"x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n" "x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n"
},
"lockToState": {
"type": "boolean",
"title": "lockToState",
"description": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\n",
"markdownDescription": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.",
"x-intellij-html-description": "\u003cp\u003eLock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\u003c/p\u003e\n"
} }
}, },
"additionalProperties": false, "additionalProperties": false,

View File

@ -112,6 +112,7 @@ encryption:
- static: - static:
passphrase: supersecret passphrase: supersecret
slot: 0 slot: 0
lockToSTATE: true
``` ```
Take a note that key order does not play any role on which key slot is used. Take a note that key order does not play any role on which key slot is used.
@ -126,8 +127,17 @@ Talos supports two kinds of keys:
- `kms` which is sealed with the network KMS. - `kms` which is sealed with the network KMS.
- `tpm` which is sealed using the TPM and protected with SecureBoot. - `tpm` which is sealed using the TPM and protected with SecureBoot.
> Note: Use static keys only if your STATE partition is encrypted and only for the EPHEMERAL partition. > Note: The `STATE` volume encryption configuration will be stored cleartext in `META` volume, so
> For the STATE partition it will be stored in the META partition, which is not encrypted. > it is not secure to use `static` keys for `STATE` volume.
> Other volumes can use `static` keys as long as `STATE` partition itself is encrypted.
Every key kind also supports `lockToSTATE` option, which means that the key will be locked to the contents of the `STATE` partition:
- if the `STATE` partition is wiped/replaced with new contents, locked to `STATE` volumes will not be unlockable anymore.
- Talos Linux generates a random salt, and stores in the `STATE` partition, which will be mixed into the key derivation function.
It is recommended to use `lockToSTATE` for the `EPHEMERAL` partition and user volumes, so that the data on these partitions is not accessible if the `STATE` partition is wiped or replaced.
If you would like non-`STATE` volumes to survive `STATE` partition wipe, do not enable `lockToSTATE` option.
### Key Rotation ### Key Rotation