mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 12:26:21 +02:00
feat: support encryption config for user volumes
No big changes, mostly wiring things together: * implement encryption config (identical to existing v1alpha1 one) for user volume configuration * provide validation, some small fixes * add support for encrypted user volumes in `talosctl cluster create` Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
This commit is contained in:
parent
9616f6e8d2
commit
ae94377d15
@ -61,6 +61,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/imager/quirks"
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
blockres "github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/version"
|
||||
"github.com/siderolabs/talos/pkg/provision"
|
||||
"github.com/siderolabs/talos/pkg/provision/access"
|
||||
@ -110,6 +111,7 @@ const (
|
||||
talosVersionFlag = "talos-version"
|
||||
encryptStatePartitionFlag = "encrypt-state"
|
||||
encryptEphemeralPartitionFlag = "encrypt-ephemeral"
|
||||
encryptUserVolumeFlag = "encrypt-user-volumes"
|
||||
enableKubeSpanFlag = "with-kubespan"
|
||||
forceEndpointFlag = "endpoint"
|
||||
kubePrismFlag = "kubeprism-port"
|
||||
@ -176,6 +178,7 @@ var (
|
||||
talosVersion string
|
||||
encryptStatePartition bool
|
||||
encryptEphemeralPartition bool
|
||||
encryptUserVolumes bool
|
||||
useVIP bool
|
||||
enableKubeSpan bool
|
||||
enableClusterDiscovery bool
|
||||
@ -314,6 +317,58 @@ func downloadBootAssets(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEncryptionKeys(cidr4 netip.Prefix, versionContract *config.VersionContract, provisionOptions *[]provision.Option) ([]*v1alpha1.EncryptionKey, error) {
|
||||
var keys []*v1alpha1.EncryptionKey
|
||||
|
||||
for i, key := range diskEncryptionKeyTypes {
|
||||
switch key {
|
||||
case "uuid":
|
||||
keys = append(keys, &v1alpha1.EncryptionKey{
|
||||
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
|
||||
KeySlot: i,
|
||||
})
|
||||
case "kms":
|
||||
var ip netip.Addr
|
||||
|
||||
// get bridge IP
|
||||
ip, err := sideronet.NthIPInNetwork(cidr4, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
const port = 4050
|
||||
|
||||
keys = append(keys, &v1alpha1.EncryptionKey{
|
||||
KeyKMS: &v1alpha1.EncryptionKeyKMS{
|
||||
KMSEndpoint: "grpc://" + nethelpers.JoinHostPort(ip.String(), port),
|
||||
},
|
||||
KeySlot: i,
|
||||
})
|
||||
|
||||
*provisionOptions = append(*provisionOptions, provision.WithKMS(nethelpers.JoinHostPort("0.0.0.0", port)))
|
||||
case "tpm":
|
||||
keyTPM := &v1alpha1.EncryptionKeyTPM{}
|
||||
|
||||
if versionContract.SecureBootEnrollEnforcementSupported() {
|
||||
keyTPM.TPMCheckSecurebootStatusOnEnroll = pointer.To(true)
|
||||
}
|
||||
|
||||
keys = append(keys, &v1alpha1.EncryptionKey{
|
||||
KeyTPM: keyTPM,
|
||||
KeySlot: i,
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown key type %q", key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
return nil, errors.New("no disk encryption key types enabled")
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo,cyclop
|
||||
func create(ctx context.Context) error {
|
||||
if err := downloadBootAssets(ctx); err != nil {
|
||||
@ -517,9 +572,14 @@ func create(ctx context.Context) error {
|
||||
provisionOptions = append(provisionOptions, provision.WithDockerPorts(portList))
|
||||
}
|
||||
|
||||
disks, userVolumePatches, err := getDisks(provisioner)
|
||||
if err != nil {
|
||||
return err
|
||||
// should have at least a single primary disk
|
||||
disks := []*provision.Disk{
|
||||
{
|
||||
Size: uint64(clusterDiskSize) * 1024 * 1024,
|
||||
SkipPreallocate: !clusterDiskPreallocate,
|
||||
Driver: "virtio",
|
||||
BlockSize: diskBlockSize,
|
||||
},
|
||||
}
|
||||
|
||||
if inputDir != "" {
|
||||
@ -577,55 +637,19 @@ func create(ctx context.Context) error {
|
||||
genOptions = append(genOptions, generate.WithVersionContract(versionContract))
|
||||
}
|
||||
|
||||
extraDisks, userVolumePatches, err := getDisks(provisioner, cidr4, versionContract, &provisionOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disks = slices.Concat(disks, extraDisks)
|
||||
|
||||
if encryptStatePartition || encryptEphemeralPartition {
|
||||
diskEncryptionConfig := &v1alpha1.SystemDiskEncryptionConfig{}
|
||||
|
||||
var keys []*v1alpha1.EncryptionKey
|
||||
|
||||
for i, key := range diskEncryptionKeyTypes {
|
||||
switch key {
|
||||
case "uuid":
|
||||
keys = append(keys, &v1alpha1.EncryptionKey{
|
||||
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
|
||||
KeySlot: i,
|
||||
})
|
||||
case "kms":
|
||||
var ip netip.Addr
|
||||
|
||||
// get bridge IP
|
||||
ip, err = sideronet.NthIPInNetwork(cidr4, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
const port = 4050
|
||||
|
||||
keys = append(keys, &v1alpha1.EncryptionKey{
|
||||
KeyKMS: &v1alpha1.EncryptionKeyKMS{
|
||||
KMSEndpoint: "grpc://" + nethelpers.JoinHostPort(ip.String(), port),
|
||||
},
|
||||
KeySlot: i,
|
||||
})
|
||||
|
||||
provisionOptions = append(provisionOptions, provision.WithKMS(nethelpers.JoinHostPort("0.0.0.0", port)))
|
||||
case "tpm":
|
||||
keyTPM := &v1alpha1.EncryptionKeyTPM{}
|
||||
|
||||
if versionContract.SecureBootEnrollEnforcementSupported() {
|
||||
keyTPM.TPMCheckSecurebootStatusOnEnroll = pointer.To(true)
|
||||
}
|
||||
|
||||
keys = append(keys, &v1alpha1.EncryptionKey{
|
||||
KeyTPM: keyTPM,
|
||||
KeySlot: i,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unknown key type %q", key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(keys) == 0 {
|
||||
return errors.New("no disk encryption key types enabled")
|
||||
keys, err := getEncryptionKeys(cidr4, versionContract, &provisionOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if encryptStatePartition {
|
||||
@ -1165,20 +1189,53 @@ func parseCPUShare(cpus string) (int64, error) {
|
||||
return nano.Num().Int64(), nil
|
||||
}
|
||||
|
||||
func getDisks(provisioner provision.Provisioner) ([]*provision.Disk, []configpatcher.Patch, error) {
|
||||
//nolint:gocyclo
|
||||
func getDisks(provisioner provision.Provisioner, cidr4 netip.Prefix, versionContract *config.VersionContract, provisionOptions *[]provision.Option) ([]*provision.Disk, []configpatcher.Patch, error) {
|
||||
const GPTAlignment = 2 * 1024 * 1024 // 2 MB
|
||||
|
||||
// should have at least a single primary disk
|
||||
disks := []*provision.Disk{
|
||||
{
|
||||
Size: uint64(clusterDiskSize) * 1024 * 1024,
|
||||
SkipPreallocate: !clusterDiskPreallocate,
|
||||
Driver: "virtio",
|
||||
BlockSize: diskBlockSize,
|
||||
},
|
||||
var (
|
||||
userVolumes []*block.UserVolumeConfigV1Alpha1
|
||||
encryptionSpec block.EncryptionSpec
|
||||
)
|
||||
|
||||
if encryptUserVolumes {
|
||||
encryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2
|
||||
|
||||
keys, err := getEncryptionKeys(
|
||||
cidr4,
|
||||
versionContract,
|
||||
provisionOptions,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
encryptionSpec.EncryptionKeys = xslices.Map(keys, func(k *v1alpha1.EncryptionKey) block.EncryptionKey {
|
||||
r := block.EncryptionKey{
|
||||
KeySlot: k.KeySlot,
|
||||
}
|
||||
|
||||
if k.KeyKMS != nil {
|
||||
r.KeyKMS = pointer.To(block.EncryptionKeyKMS(*k.KeyKMS))
|
||||
}
|
||||
|
||||
if k.KeyTPM != nil {
|
||||
r.KeyTPM = pointer.To(block.EncryptionKeyTPM(*k.KeyTPM))
|
||||
}
|
||||
|
||||
if k.KeyNodeID != nil {
|
||||
r.KeyNodeID = pointer.To(block.EncryptionKeyNodeID(*k.KeyNodeID))
|
||||
}
|
||||
|
||||
if k.KeyStatic != nil {
|
||||
r.KeyStatic = pointer.To(block.EncryptionKeyStatic(*k.KeyStatic))
|
||||
}
|
||||
|
||||
return r
|
||||
})
|
||||
}
|
||||
|
||||
var userVolumes []*block.UserVolumeConfigV1Alpha1
|
||||
disks := make([]*provision.Disk, 0, len(clusterUserVolumes))
|
||||
|
||||
for diskID, disk := range clusterUserVolumes {
|
||||
var (
|
||||
@ -1203,6 +1260,7 @@ func getDisks(provisioner provision.Provisioner) ([]*provision.Disk, []configpat
|
||||
ProvisioningMinSize: block.MustByteSize(volumeSize),
|
||||
ProvisioningMaxSize: block.MustByteSize(volumeSize),
|
||||
}
|
||||
userVolume.EncryptionSpec = encryptionSpec
|
||||
|
||||
userVolumes = append(userVolumes, userVolume)
|
||||
diskSize += userVolume.ProvisioningSpec.ProvisioningMaxSize.Value()
|
||||
@ -1308,6 +1366,7 @@ func init() {
|
||||
createCmd.Flags().BoolVar(&skipInjectingConfig, "skip-injecting-config", false, "skip injecting config from embedded metadata server, write config files to current directory")
|
||||
createCmd.Flags().BoolVar(&encryptStatePartition, encryptStatePartitionFlag, false, "enable state partition encryption")
|
||||
createCmd.Flags().BoolVar(&encryptEphemeralPartition, encryptEphemeralPartitionFlag, false, "enable ephemeral partition encryption")
|
||||
createCmd.Flags().BoolVar(&encryptUserVolumes, encryptUserVolumeFlag, false, "enable ephemeral partition encryption")
|
||||
createCmd.Flags().StringArrayVar(&diskEncryptionKeyTypes, diskEncryptionKeyTypesFlag, []string{"uuid"}, "encryption key types to use for disk encryption (uuid, kms)")
|
||||
createCmd.Flags().StringVar(&talosVersion, talosVersionFlag, "", "the desired Talos version to generate config for (if not set, defaults to image version)")
|
||||
createCmd.Flags().BoolVar(&useVIP, useVIPFlag, false, "use a virtual IP for the controlplane endpoint instead of the loadbalancer")
|
||||
@ -1349,6 +1408,7 @@ func init() {
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, talosVersionFlag)
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, encryptStatePartitionFlag)
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, encryptEphemeralPartitionFlag)
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, encryptUserVolumeFlag)
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, enableKubeSpanFlag)
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, forceEndpointFlag)
|
||||
createCmd.MarkFlagsMutuallyExclusive(inputDirFlag, kubePrismFlag)
|
||||
|
||||
@ -115,7 +115,7 @@ case "${WITH_DISK_ENCRYPTION:-false}" in
|
||||
false)
|
||||
;;
|
||||
*)
|
||||
QEMU_FLAGS+=("--encrypt-ephemeral" "--encrypt-state" "--disk-encryption-key-types=kms")
|
||||
QEMU_FLAGS+=("--encrypt-ephemeral" "--encrypt-state" "--encrypt-user-volumes" "--disk-encryption-key-types=kms")
|
||||
;;
|
||||
esac
|
||||
|
||||
@ -184,7 +184,7 @@ case "${WITH_TRUSTED_BOOT_ISO:-false}" in
|
||||
;;
|
||||
*)
|
||||
INSTALLER_IMAGE=${INSTALLER_IMAGE}-amd64-secureboot
|
||||
QEMU_FLAGS+=("--iso-path=_out/metal-amd64-secureboot.iso" "--with-tpm2" "--encrypt-ephemeral" "--encrypt-state" "--disk-encryption-key-types=tpm")
|
||||
QEMU_FLAGS+=("--iso-path=_out/metal-amd64-secureboot.iso" "--with-tpm2" "--encrypt-ephemeral" "--encrypt-state" "--encrypt-user-volumes" "--disk-encryption-key-types=tpm")
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@ -168,6 +168,10 @@ func (ctrl *UserVolumeConfigController) Run(ctx context.Context, r controller.Ru
|
||||
GID: 0,
|
||||
}
|
||||
|
||||
if err := convertEncryptionConfiguration(userVolumeConfig.Encryption(), v.TypedSpec()); err != nil {
|
||||
return fmt.Errorf("error apply encryption configuration: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
); err != nil {
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
// 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 block_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/resource"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block"
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/container"
|
||||
blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/config"
|
||||
)
|
||||
|
||||
type UserVolumeConfigSuite struct {
|
||||
ctest.DefaultSuite
|
||||
}
|
||||
|
||||
func TestUserVolumeConfigSuite(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
suite.Run(t, &UserVolumeConfigSuite{
|
||||
DefaultSuite: ctest.DefaultSuite{
|
||||
Timeout: 3 * time.Second,
|
||||
AfterSetup: func(suite *ctest.DefaultSuite) {
|
||||
suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.UserVolumeConfigController{}))
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *UserVolumeConfigSuite) TestReconcile() {
|
||||
uv1 := blockcfg.NewUserVolumeConfigV1Alpha1()
|
||||
uv1.MetaName = "data1"
|
||||
suite.Require().NoError(uv1.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`)))
|
||||
uv1.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("10GiB")
|
||||
uv1.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustByteSize("100GiB")
|
||||
uv1.FilesystemSpec.FilesystemType = block.FilesystemTypeXFS
|
||||
|
||||
uv2 := blockcfg.NewUserVolumeConfigV1Alpha1()
|
||||
uv2.MetaName = "data2"
|
||||
suite.Require().NoError(uv2.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`)))
|
||||
uv2.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustByteSize("1TiB")
|
||||
uv2.EncryptionSpec = blockcfg.EncryptionSpec{
|
||||
EncryptionProvider: block.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []blockcfg.EncryptionKey{
|
||||
{
|
||||
KeySlot: 0,
|
||||
KeyTPM: &blockcfg.EncryptionKeyTPM{},
|
||||
},
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyStatic: &blockcfg.EncryptionKeyStatic{KeyData: "secret"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctr, err := container.New(uv1, uv2)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
cfg := config.NewMachineConfig(ctr)
|
||||
suite.Create(cfg)
|
||||
|
||||
userVolumes := []string{
|
||||
constants.UserVolumePrefix + "data1",
|
||||
constants.UserVolumePrefix + "data2",
|
||||
}
|
||||
|
||||
ctest.AssertResources(suite, userVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) {
|
||||
asrt.Contains(vc.Metadata().Labels().Raw(), block.UserVolumeLabel)
|
||||
|
||||
asrt.Equal(block.VolumeTypePartition, vc.TypedSpec().Type)
|
||||
asrt.Contains(userVolumes, vc.TypedSpec().Provisioning.PartitionSpec.Label)
|
||||
|
||||
locator, err := vc.TypedSpec().Locator.Match.MarshalText()
|
||||
asrt.NoError(err)
|
||||
|
||||
asrt.Contains(string(locator), vc.TypedSpec().Provisioning.PartitionSpec.Label)
|
||||
|
||||
asrt.Contains([]string{"data1", "data2"}, vc.TypedSpec().Mount.TargetPath)
|
||||
asrt.Equal(constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID)
|
||||
})
|
||||
|
||||
ctest.AssertResources(suite, userVolumes, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) {
|
||||
asrt.Contains(vmr.Metadata().Labels().Raw(), block.UserVolumeLabel)
|
||||
})
|
||||
|
||||
// simulate other controllers working - add finalizers for volume config & mount request
|
||||
for _, volumeID := range userVolumes {
|
||||
suite.AddFinalizer(block.NewVolumeConfig(block.NamespaceName, volumeID).Metadata(), "test")
|
||||
suite.AddFinalizer(block.NewVolumeMountRequest(block.NamespaceName, volumeID).Metadata(), "test")
|
||||
}
|
||||
|
||||
// drop the first volume
|
||||
ctr, err = container.New(uv2)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
newCfg := config.NewMachineConfig(ctr)
|
||||
newCfg.Metadata().SetVersion(cfg.Metadata().Version())
|
||||
suite.Update(newCfg)
|
||||
|
||||
// controller should tear down removed volumes
|
||||
ctest.AssertResources(suite, userVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) {
|
||||
if vc.Metadata().ID() == userVolumes[0] {
|
||||
asrt.Equal(resource.PhaseTearingDown, vc.Metadata().Phase())
|
||||
} else {
|
||||
asrt.Equal(resource.PhaseRunning, vc.Metadata().Phase())
|
||||
}
|
||||
})
|
||||
|
||||
// controller should tear down removed volume resources
|
||||
ctest.AssertResources(suite, userVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) {
|
||||
if vc.Metadata().ID() == userVolumes[0] {
|
||||
asrt.Equal(resource.PhaseTearingDown, vc.Metadata().Phase())
|
||||
} else {
|
||||
asrt.Equal(resource.PhaseRunning, vc.Metadata().Phase())
|
||||
}
|
||||
})
|
||||
|
||||
ctest.AssertResources(suite, userVolumes, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) {
|
||||
if vmr.Metadata().ID() == userVolumes[0] {
|
||||
asrt.Equal(resource.PhaseTearingDown, vmr.Metadata().Phase())
|
||||
} else {
|
||||
asrt.Equal(resource.PhaseRunning, vmr.Metadata().Phase())
|
||||
}
|
||||
})
|
||||
|
||||
// remove finalizers
|
||||
suite.RemoveFinalizer(block.NewVolumeConfig(block.NamespaceName, userVolumes[0]).Metadata(), "test")
|
||||
suite.RemoveFinalizer(block.NewVolumeMountRequest(block.NamespaceName, userVolumes[0]).Metadata(), "test")
|
||||
|
||||
// now the resources should be removed
|
||||
ctest.AssertNoResource[*block.VolumeConfig](suite, userVolumes[0])
|
||||
ctest.AssertNoResource[*block.VolumeMountRequest](suite, userVolumes[0])
|
||||
}
|
||||
@ -15,7 +15,6 @@ import (
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
"github.com/cosi-project/runtime/pkg/state"
|
||||
"github.com/siderolabs/gen/optional"
|
||||
"github.com/siderolabs/go-blockdevice/v2/encryption"
|
||||
"go.uber.org/zap"
|
||||
|
||||
machinedruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
|
||||
@ -87,20 +86,14 @@ func systemDiskMatch() cel.Expression {
|
||||
return cel.MustExpression(cel.ParseBooleanExpression("system_disk", celenv.DiskLocator()))
|
||||
}
|
||||
|
||||
func (ctrl *VolumeConfigController) convertEncryption(in cfg.Encryption, out *block.VolumeConfigSpec) error {
|
||||
func convertEncryptionConfiguration(in cfg.EncryptionConfig, out *block.VolumeConfigSpec) error {
|
||||
if in == nil {
|
||||
out.Encryption = block.EncryptionSpec{}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
switch in.Provider() {
|
||||
case encryption.LUKS2:
|
||||
out.Encryption.Provider = block.EncryptionProviderLUKS2
|
||||
default:
|
||||
return fmt.Errorf("unsupported encryption provider: %s", in.Provider())
|
||||
}
|
||||
|
||||
out.Encryption.Provider = in.Provider()
|
||||
out.Encryption.Cipher = in.Cipher()
|
||||
out.Encryption.KeySize = in.KeySize()
|
||||
out.Encryption.BlockSize = in.BlockSize()
|
||||
@ -271,7 +264,7 @@ func (ctrl *VolumeConfigController) manageEphemeral(config cfg.Config) func(vc *
|
||||
Match: labelVolumeMatch(constants.EphemeralPartitionLabel),
|
||||
}
|
||||
|
||||
if err := ctrl.convertEncryption(
|
||||
if err := convertEncryptionConfiguration(
|
||||
config.Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel),
|
||||
vc.TypedSpec(),
|
||||
); err != nil {
|
||||
@ -331,7 +324,7 @@ func (ctrl *VolumeConfigController) manageStateConfigPresent(config cfg.Config)
|
||||
Match: labelVolumeMatch(constants.StatePartitionLabel),
|
||||
}
|
||||
|
||||
if err := ctrl.convertEncryption(
|
||||
if err := convertEncryptionConfiguration(
|
||||
config.Machine().SystemDiskEncryption().Get(constants.StatePartitionLabel),
|
||||
vc.TypedSpec(),
|
||||
); err != nil {
|
||||
@ -374,7 +367,7 @@ func (ctrl *VolumeConfigController) manageStateNoConfig(encryptionMeta *runtime.
|
||||
return fmt.Errorf("error unmarshalling state encryption meta key: %w", err)
|
||||
}
|
||||
|
||||
if err := ctrl.convertEncryption(
|
||||
if err := convertEncryptionConfiguration(
|
||||
encryptionFromMeta,
|
||||
vc.TypedSpec(),
|
||||
); err != nil {
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/cel"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/machine"
|
||||
"github.com/siderolabs/talos/pkg/machinery/nethelpers"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
)
|
||||
|
||||
// MachineConfig defines the requirements for a config that pertains to machine
|
||||
@ -431,9 +432,9 @@ type EncryptionKeyTPM interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// Encryption defines settings for the partition encryption.
|
||||
type Encryption interface {
|
||||
Provider() string
|
||||
// EncryptionConfig defines settings for the partition encryption.
|
||||
type EncryptionConfig interface {
|
||||
Provider() block.EncryptionProviderType
|
||||
Cipher() string
|
||||
KeySize() uint
|
||||
BlockSize() uint64
|
||||
@ -443,7 +444,7 @@ type Encryption interface {
|
||||
|
||||
// SystemDiskEncryption accumulates settings for all system partitions encryption.
|
||||
type SystemDiskEncryption interface {
|
||||
Get(label string) Encryption
|
||||
Get(label string) EncryptionConfig
|
||||
}
|
||||
|
||||
// Features describe individual Talos features that can be switched on or off.
|
||||
|
||||
@ -87,6 +87,7 @@ type UserVolumeConfig interface {
|
||||
UserVolumeConfigSignal()
|
||||
Provisioning() VolumeProvisioningConfig
|
||||
Filesystem() FilesystemConfig
|
||||
Encryption() EncryptionConfig
|
||||
}
|
||||
|
||||
// FilesystemConfig defines the interface to access filesystem configuration.
|
||||
|
||||
@ -16,6 +16,158 @@
|
||||
"type": "object",
|
||||
"description": "DiskSelector selects a disk for the volume."
|
||||
},
|
||||
"block.EncryptionKey": {
|
||||
"properties": {
|
||||
"slot": {
|
||||
"type": "integer",
|
||||
"title": "slot",
|
||||
"description": "Key slot number for LUKS2 encryption.\n",
|
||||
"markdownDescription": "Key slot number for LUKS2 encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKey slot number for LUKS2 encryption.\u003c/p\u003e\n"
|
||||
},
|
||||
"static": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyStatic",
|
||||
"title": "static",
|
||||
"description": "Key which value is stored in the configuration file.\n",
|
||||
"markdownDescription": "Key which value is stored in the configuration file.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKey which value is stored in the configuration file.\u003c/p\u003e\n"
|
||||
},
|
||||
"nodeID": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyNodeID",
|
||||
"title": "nodeID",
|
||||
"description": "Deterministically generated key from the node UUID and PartitionLabel.\n",
|
||||
"markdownDescription": "Deterministically generated key from the node UUID and PartitionLabel.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDeterministically generated key from the node UUID and PartitionLabel.\u003c/p\u003e\n"
|
||||
},
|
||||
"kms": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyKMS",
|
||||
"title": "kms",
|
||||
"description": "KMS managed encryption key.\n",
|
||||
"markdownDescription": "KMS managed encryption key.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKMS managed encryption key.\u003c/p\u003e\n"
|
||||
},
|
||||
"tpm": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyTPM",
|
||||
"title": "tpm",
|
||||
"description": "Enable TPM based disk encryption.\n",
|
||||
"markdownDescription": "Enable TPM based disk encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKey represents configuration for disk encryption key."
|
||||
},
|
||||
"block.EncryptionKeyKMS": {
|
||||
"properties": {
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"title": "endpoint",
|
||||
"description": "KMS endpoint to Seal/Unseal the key.\n",
|
||||
"markdownDescription": "KMS endpoint to Seal/Unseal the key.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKMS endpoint to Seal/Unseal the key.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server."
|
||||
},
|
||||
"block.EncryptionKeyNodeID": {
|
||||
"properties": {},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel."
|
||||
},
|
||||
"block.EncryptionKeyStatic": {
|
||||
"properties": {
|
||||
"passphrase": {
|
||||
"type": "string",
|
||||
"title": "passphrase",
|
||||
"description": "Defines the static passphrase value.\n",
|
||||
"markdownDescription": "Defines the static passphrase value.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the static passphrase value.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyStatic represents throw away key type."
|
||||
},
|
||||
"block.EncryptionKeyTPM": {
|
||||
"properties": {
|
||||
"checkSecurebootStatusOnEnroll": {
|
||||
"type": "boolean",
|
||||
"title": "checkSecurebootStatusOnEnroll",
|
||||
"description": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\n",
|
||||
"markdownDescription": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.",
|
||||
"x-intellij-html-description": "\u003cp\u003eCheck that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM."
|
||||
},
|
||||
"block.EncryptionSpec": {
|
||||
"properties": {
|
||||
"provider": {
|
||||
"enum": [
|
||||
"luks2"
|
||||
],
|
||||
"title": "provider",
|
||||
"description": "Encryption provider to use for the encryption.\n",
|
||||
"markdownDescription": "Encryption provider to use for the encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEncryption provider to use for the encryption.\u003c/p\u003e\n"
|
||||
},
|
||||
"keys": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/block.EncryptionKey"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "keys",
|
||||
"description": "Defines the encryption keys generation and storage method.\n",
|
||||
"markdownDescription": "Defines the encryption keys generation and storage method.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the encryption keys generation and storage method.\u003c/p\u003e\n"
|
||||
},
|
||||
"cipher": {
|
||||
"enum": [
|
||||
"aes-xts-plain64",
|
||||
"xchacha12,aes-adiantum-plain64",
|
||||
"xchacha20,aes-adiantum-plain64"
|
||||
],
|
||||
"title": "cipher",
|
||||
"description": "Cipher to use for the encryption. Depends on the encryption provider.\n",
|
||||
"markdownDescription": "Cipher to use for the encryption. Depends on the encryption provider.",
|
||||
"x-intellij-html-description": "\u003cp\u003eCipher to use for the encryption. Depends on the encryption provider.\u003c/p\u003e\n"
|
||||
},
|
||||
"keySize": {
|
||||
"type": "integer",
|
||||
"title": "keySize",
|
||||
"description": "Defines the encryption key length.\n",
|
||||
"markdownDescription": "Defines the encryption key length.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the encryption key length.\u003c/p\u003e\n"
|
||||
},
|
||||
"blockSize": {
|
||||
"type": "integer",
|
||||
"title": "blockSize",
|
||||
"description": "Defines the encryption sector size.\n",
|
||||
"markdownDescription": "Defines the encryption sector size.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the encryption sector size.\u003c/p\u003e\n"
|
||||
},
|
||||
"options": {
|
||||
"enum": [
|
||||
"no_read_workqueue",
|
||||
"no_write_workqueue",
|
||||
"same_cpu_crypt"
|
||||
],
|
||||
"title": "options",
|
||||
"description": "Additional –perf parameters for the LUKS2 encryption.\n",
|
||||
"markdownDescription": "Additional --perf parameters for the LUKS2 encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eAdditional \u0026ndash;perf parameters for the LUKS2 encryption.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionSpec represents volume encryption settings."
|
||||
},
|
||||
"block.FilesystemSpec": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@ -108,6 +260,13 @@
|
||||
"description": "The filesystem describes how the volume is formatted.\n",
|
||||
"markdownDescription": "The filesystem describes how the volume is formatted.",
|
||||
"x-intellij-html-description": "\u003cp\u003eThe filesystem describes how the volume is formatted.\u003c/p\u003e\n"
|
||||
},
|
||||
"encryption": {
|
||||
"$ref": "#/$defs/block.EncryptionSpec",
|
||||
"title": "encryption",
|
||||
"description": "The encryption describes how the volume is encrypted.\n",
|
||||
"markdownDescription": "The encryption describes how the volume is encrypted.",
|
||||
"x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
@ -5,6 +5,6 @@
|
||||
// Package block provides block device and volume configuration documents.
|
||||
package block
|
||||
|
||||
//go:generate docgen -output block_doc.go block.go volume_config.go user_volume_config.go
|
||||
//go:generate docgen -output block_doc.go block.go encryption.go volume_config.go user_volume_config.go
|
||||
|
||||
//go:generate deep-copy -type UserVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go .
|
||||
|
||||
@ -10,6 +10,232 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
)
|
||||
|
||||
func (EncryptionSpec) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "EncryptionSpec",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionSpec represents volume encryption settings." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "EncryptionSpec represents volume encryption settings.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "UserVolumeConfigV1Alpha1",
|
||||
FieldName: "encryption",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "provider",
|
||||
Type: "EncryptionProviderType",
|
||||
Note: "",
|
||||
Description: "Encryption provider to use for the encryption.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Encryption provider to use for the encryption." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Values: []string{
|
||||
"luks2",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "keys",
|
||||
Type: "[]EncryptionKey",
|
||||
Note: "",
|
||||
Description: "Defines the encryption keys generation and storage method.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Defines the encryption keys generation and storage method." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "cipher",
|
||||
Type: "string",
|
||||
Note: "",
|
||||
Description: "Cipher to use for the encryption. Depends on the encryption provider.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Cipher to use for the encryption. Depends on the encryption provider." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Values: []string{
|
||||
"aes-xts-plain64",
|
||||
"xchacha12,aes-adiantum-plain64",
|
||||
"xchacha20,aes-adiantum-plain64",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "keySize",
|
||||
Type: "uint",
|
||||
Note: "",
|
||||
Description: "Defines the encryption key length.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Defines the encryption key length." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "blockSize",
|
||||
Type: "uint64",
|
||||
Note: "",
|
||||
Description: "Defines the encryption sector size.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Defines the encryption sector size." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "options",
|
||||
Type: "[]string",
|
||||
Note: "",
|
||||
Description: "Additional --perf parameters for the LUKS2 encryption.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Additional --perf parameters for the LUKS2 encryption." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Values: []string{
|
||||
"no_read_workqueue",
|
||||
"no_write_workqueue",
|
||||
"same_cpu_crypt",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
doc.AddExample("", exampleEncryptionSpec())
|
||||
|
||||
doc.Fields[2].AddExample("", "aes-xts-plain64")
|
||||
doc.Fields[4].AddExample("", 4096)
|
||||
doc.Fields[5].AddExample("", []string{"no_read_workqueue", "no_write_workqueue"})
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (EncryptionKey) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "EncryptionKey",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKey represents configuration for disk encryption key." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "EncryptionKey represents configuration for disk encryption key.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "EncryptionSpec",
|
||||
FieldName: "keys",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "slot",
|
||||
Type: "int",
|
||||
Note: "",
|
||||
Description: "Key slot number for LUKS2 encryption.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Key slot number for LUKS2 encryption." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "static",
|
||||
Type: "EncryptionKeyStatic",
|
||||
Note: "",
|
||||
Description: "Key which value is stored in the configuration file.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Key which value is stored in the configuration file." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "nodeID",
|
||||
Type: "EncryptionKeyNodeID",
|
||||
Note: "",
|
||||
Description: "Deterministically generated key from the node UUID and PartitionLabel.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Deterministically generated key from the node UUID and PartitionLabel." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "kms",
|
||||
Type: "EncryptionKeyKMS",
|
||||
Note: "",
|
||||
Description: "KMS managed encryption key.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "KMS managed encryption key." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "tpm",
|
||||
Type: "EncryptionKeyTPM",
|
||||
Note: "",
|
||||
Description: "Enable TPM based disk encryption.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Enable TPM based disk encryption." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (EncryptionKeyStatic) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "EncryptionKeyStatic",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyStatic represents throw away key type." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "EncryptionKeyStatic represents throw away key type.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "EncryptionKey",
|
||||
FieldName: "static",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "passphrase",
|
||||
Type: "string",
|
||||
Note: "",
|
||||
Description: "Defines the static passphrase value.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Defines the static passphrase value." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (EncryptionKeyKMS) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "EncryptionKeyKMS",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "EncryptionKey",
|
||||
FieldName: "kms",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "endpoint",
|
||||
Type: "string",
|
||||
Note: "",
|
||||
Description: "KMS endpoint to Seal/Unseal the key.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "KMS endpoint to Seal/Unseal the key." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
doc.AddExample("", exampleKMSKey())
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (EncryptionKeyTPM) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "EncryptionKeyTPM",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "EncryptionKey",
|
||||
FieldName: "tpm",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{
|
||||
{
|
||||
Name: "checkSecurebootStatusOnEnroll",
|
||||
Type: "bool",
|
||||
Note: "",
|
||||
Description: "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "Check that Secureboot is enabled in the EFI firmware." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (EncryptionKeyNodeID) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "EncryptionKeyNodeID",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
Description: "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.",
|
||||
AppearsIn: []encoder.Appearance{
|
||||
{
|
||||
TypeName: "EncryptionKey",
|
||||
FieldName: "nodeID",
|
||||
},
|
||||
},
|
||||
Fields: []encoder.Doc{},
|
||||
}
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
func (VolumeConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
doc := &encoder.Doc{
|
||||
Type: "VolumeConfig",
|
||||
@ -148,6 +374,13 @@ func (UserVolumeConfigV1Alpha1) Doc() *encoder.Doc {
|
||||
Description: "The filesystem describes how the volume is formatted.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "The filesystem describes how the volume is formatted." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
{
|
||||
Name: "encryption",
|
||||
Type: "EncryptionSpec",
|
||||
Note: "",
|
||||
Description: "The encryption describes how the volume is encrypted.",
|
||||
Comments: [3]string{"" /* encoder.HeadComment */, "The encryption describes how the volume is encrypted." /* encoder.LineComment */, "" /* encoder.FootComment */},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -191,6 +424,12 @@ func GetFileDoc() *encoder.FileDoc {
|
||||
Name: "block",
|
||||
Description: "Package block provides block device and volume configuration documents.\n",
|
||||
Structs: []*encoder.Doc{
|
||||
EncryptionSpec{}.Doc(),
|
||||
EncryptionKey{}.Doc(),
|
||||
EncryptionKeyStatic{}.Doc(),
|
||||
EncryptionKeyKMS{}.Doc(),
|
||||
EncryptionKeyTPM{}.Doc(),
|
||||
EncryptionKeyNodeID{}.Doc(),
|
||||
VolumeConfigV1Alpha1{}.Doc(),
|
||||
ProvisioningSpec{}.Doc(),
|
||||
DiskSelector{}.Doc(),
|
||||
|
||||
@ -29,6 +29,36 @@ func (o *UserVolumeConfigV1Alpha1) DeepCopy() *UserVolumeConfigV1Alpha1 {
|
||||
cp.ProvisioningSpec.ProvisioningMaxSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.raw))
|
||||
copy(cp.ProvisioningSpec.ProvisioningMaxSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.raw)
|
||||
}
|
||||
if o.EncryptionSpec.EncryptionKeys != nil {
|
||||
cp.EncryptionSpec.EncryptionKeys = make([]EncryptionKey, len(o.EncryptionSpec.EncryptionKeys))
|
||||
copy(cp.EncryptionSpec.EncryptionKeys, o.EncryptionSpec.EncryptionKeys)
|
||||
for i3 := range o.EncryptionSpec.EncryptionKeys {
|
||||
if o.EncryptionSpec.EncryptionKeys[i3].KeyStatic != nil {
|
||||
cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = new(EncryptionKeyStatic)
|
||||
*cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = *o.EncryptionSpec.EncryptionKeys[i3].KeyStatic
|
||||
}
|
||||
if o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID != nil {
|
||||
cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = new(EncryptionKeyNodeID)
|
||||
*cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = *o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID
|
||||
}
|
||||
if o.EncryptionSpec.EncryptionKeys[i3].KeyKMS != nil {
|
||||
cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = new(EncryptionKeyKMS)
|
||||
*cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = *o.EncryptionSpec.EncryptionKeys[i3].KeyKMS
|
||||
}
|
||||
if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM != nil {
|
||||
cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = new(EncryptionKeyTPM)
|
||||
*cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM
|
||||
if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll != nil {
|
||||
cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = new(bool)
|
||||
*cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.EncryptionSpec.EncryptionPerfOptions != nil {
|
||||
cp.EncryptionSpec.EncryptionPerfOptions = make([]string, len(o.EncryptionSpec.EncryptionPerfOptions))
|
||||
copy(cp.EncryptionSpec.EncryptionPerfOptions, o.EncryptionSpec.EncryptionPerfOptions)
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
||||
289
pkg/machinery/config/types/block/encryption.go
Normal file
289
pkg/machinery/config/types/block/encryption.go
Normal file
@ -0,0 +1,289 @@
|
||||
// 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 block
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
"github.com/siderolabs/go-pointer"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
)
|
||||
|
||||
//docgen:jsonschema
|
||||
|
||||
// EncryptionSpec represents volume encryption settings.
|
||||
//
|
||||
// examples:
|
||||
// - value: exampleEncryptionSpec()
|
||||
type EncryptionSpec struct {
|
||||
// description: >
|
||||
// Encryption provider to use for the encryption.
|
||||
// values:
|
||||
// - luks2
|
||||
EncryptionProvider block.EncryptionProviderType `yaml:"provider"`
|
||||
// description: >
|
||||
// Defines the encryption keys generation and storage method.
|
||||
EncryptionKeys []EncryptionKey `yaml:"keys"`
|
||||
// description: >
|
||||
// Cipher to use for the encryption.
|
||||
// Depends on the encryption provider.
|
||||
// values:
|
||||
// - aes-xts-plain64
|
||||
// - xchacha12,aes-adiantum-plain64
|
||||
// - xchacha20,aes-adiantum-plain64
|
||||
// examples:
|
||||
// - value: '"aes-xts-plain64"'
|
||||
EncryptionCipher string `yaml:"cipher,omitempty"`
|
||||
// description: >
|
||||
// Defines the encryption key length.
|
||||
EncryptionKeySize uint `yaml:"keySize,omitempty"`
|
||||
// description: >
|
||||
// Defines the encryption sector size.
|
||||
// examples:
|
||||
// - value: '4096'
|
||||
EncryptionBlockSize uint64 `yaml:"blockSize,omitempty"`
|
||||
// description: >
|
||||
// Additional --perf parameters for the LUKS2 encryption.
|
||||
// values:
|
||||
// - no_read_workqueue
|
||||
// - no_write_workqueue
|
||||
// - same_cpu_crypt
|
||||
// examples:
|
||||
// - value: >
|
||||
// []string{"no_read_workqueue","no_write_workqueue"}
|
||||
EncryptionPerfOptions []string `yaml:"options,omitempty"`
|
||||
}
|
||||
|
||||
func exampleEncryptionSpec() *EncryptionSpec {
|
||||
return &EncryptionSpec{
|
||||
EncryptionProvider: block.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []EncryptionKey{
|
||||
{
|
||||
KeySlot: 0,
|
||||
KeyStatic: &EncryptionKeyStatic{
|
||||
KeyData: "exampleKey",
|
||||
},
|
||||
},
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyKMS: &EncryptionKeyKMS{
|
||||
KMSEndpoint: "https://example-kms-endpoint.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
EncryptionCipher: "aes-xts-plain64",
|
||||
EncryptionBlockSize: 4096,
|
||||
}
|
||||
}
|
||||
|
||||
// EncryptionKey represents configuration for disk encryption key.
|
||||
type EncryptionKey struct {
|
||||
// description: >
|
||||
// Key slot number for LUKS2 encryption.
|
||||
KeySlot int `yaml:"slot"`
|
||||
// description: >
|
||||
// Key which value is stored in the configuration file.
|
||||
KeyStatic *EncryptionKeyStatic `yaml:"static,omitempty"`
|
||||
// description: >
|
||||
// Deterministically generated key from the node UUID and PartitionLabel.
|
||||
KeyNodeID *EncryptionKeyNodeID `yaml:"nodeID,omitempty"`
|
||||
// description: >
|
||||
// KMS managed encryption key.
|
||||
KeyKMS *EncryptionKeyKMS `yaml:"kms,omitempty"`
|
||||
// description: >
|
||||
// Enable TPM based disk encryption.
|
||||
KeyTPM *EncryptionKeyTPM `yaml:"tpm,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptionKeyStatic represents throw away key type.
|
||||
type EncryptionKeyStatic struct {
|
||||
// description: >
|
||||
// Defines the static passphrase value.
|
||||
KeyData string `yaml:"passphrase,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server.
|
||||
//
|
||||
// examples:
|
||||
// - value: exampleKMSKey()
|
||||
type EncryptionKeyKMS struct {
|
||||
// description: >
|
||||
// KMS endpoint to Seal/Unseal the key.
|
||||
KMSEndpoint string `yaml:"endpoint"`
|
||||
}
|
||||
|
||||
// EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM.
|
||||
type EncryptionKeyTPM struct {
|
||||
// description: >
|
||||
// Check that Secureboot is enabled in the EFI firmware.
|
||||
//
|
||||
// If Secureboot is not enabled, the enrollment of the key will fail.
|
||||
// As the TPM key is anyways bound to the value of PCR 7,
|
||||
// changing Secureboot status or configuration
|
||||
// after the initial enrollment will make the key unusable.
|
||||
TPMCheckSecurebootStatusOnEnroll *bool `yaml:"checkSecurebootStatusOnEnroll,omitempty"`
|
||||
}
|
||||
|
||||
// EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.
|
||||
type EncryptionKeyNodeID struct{}
|
||||
|
||||
func exampleKMSKey() *EncryptionKeyKMS {
|
||||
return &EncryptionKeyKMS{
|
||||
KMSEndpoint: "https://192.168.88.21:4443",
|
||||
}
|
||||
}
|
||||
|
||||
// Validate implements config.Validator interface.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func (s EncryptionSpec) Validate() ([]string, error) {
|
||||
if s.EncryptionProvider == block.EncryptionProviderNone && len(s.EncryptionKeys) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var errs error
|
||||
|
||||
switch s.EncryptionProvider {
|
||||
case block.EncryptionProviderLUKS2:
|
||||
case block.EncryptionProviderNone:
|
||||
fallthrough
|
||||
default:
|
||||
errs = errors.Join(errs, fmt.Errorf("unsupported encryption provider: %s", s.EncryptionProvider))
|
||||
}
|
||||
|
||||
if len(s.EncryptionKeys) == 0 {
|
||||
errs = errors.Join(errs, errors.New("encryption keys are required"))
|
||||
}
|
||||
|
||||
slotsInUse := make(map[int]struct{}, len(s.EncryptionKeys))
|
||||
|
||||
for _, key := range s.EncryptionKeys {
|
||||
if _, ok := slotsInUse[key.KeySlot]; ok {
|
||||
errs = errors.Join(errs, fmt.Errorf("duplicate key slot %d", key.KeySlot))
|
||||
}
|
||||
|
||||
slotsInUse[key.KeySlot] = struct{}{}
|
||||
|
||||
if key.KeyStatic == nil && key.KeyNodeID == nil && key.KeyKMS == nil && key.KeyTPM == nil {
|
||||
errs = errors.Join(errs, fmt.Errorf("at least one encryption key type must be specified for slot %d", key.KeySlot))
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errs
|
||||
}
|
||||
|
||||
// Provider implements the config.Provider interface.
|
||||
func (s EncryptionSpec) Provider() block.EncryptionProviderType {
|
||||
return s.EncryptionProvider
|
||||
}
|
||||
|
||||
// Cipher implements the config.Provider interface.
|
||||
func (s EncryptionSpec) Cipher() string {
|
||||
return s.EncryptionCipher
|
||||
}
|
||||
|
||||
// KeySize implements the config.Provider interface.
|
||||
func (s EncryptionSpec) KeySize() uint {
|
||||
return s.EncryptionKeySize
|
||||
}
|
||||
|
||||
// BlockSize implements the config.Provider interface.
|
||||
func (s EncryptionSpec) BlockSize() uint64 {
|
||||
return s.EncryptionBlockSize
|
||||
}
|
||||
|
||||
// Options implements the config.Provider interface.
|
||||
func (s EncryptionSpec) Options() []string {
|
||||
return s.EncryptionPerfOptions
|
||||
}
|
||||
|
||||
// Keys implements the config.Provider interface.
|
||||
func (s EncryptionSpec) Keys() []config.EncryptionKey {
|
||||
return xslices.Map(s.EncryptionKeys, func(k EncryptionKey) config.EncryptionKey { return k })
|
||||
}
|
||||
|
||||
// Slot implements the config.Provider interface.
|
||||
func (k EncryptionKey) Slot() int {
|
||||
return k.KeySlot
|
||||
}
|
||||
|
||||
// Static implements the config.Provider interface.
|
||||
func (k EncryptionKey) Static() config.EncryptionKeyStatic {
|
||||
if k.KeyStatic == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return k.KeyStatic
|
||||
}
|
||||
|
||||
// NodeID implements the config.Provider interface.
|
||||
func (k EncryptionKey) NodeID() config.EncryptionKeyNodeID {
|
||||
if k.KeyNodeID == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return k.KeyNodeID
|
||||
}
|
||||
|
||||
// KMS implements the config.Provider interface.
|
||||
func (k EncryptionKey) KMS() config.EncryptionKeyKMS {
|
||||
if k.KeyKMS == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return k.KeyKMS
|
||||
}
|
||||
|
||||
// TPM implements the config.Provider interface.
|
||||
func (k EncryptionKey) TPM() config.EncryptionKeyTPM {
|
||||
if k.KeyTPM == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return k.KeyTPM
|
||||
}
|
||||
|
||||
// String implements the config.Provider interface.
|
||||
func (e *EncryptionKeyNodeID) String() string {
|
||||
return "nodeid"
|
||||
}
|
||||
|
||||
// String implements the config.Provider interface.
|
||||
func (e *EncryptionKeyTPM) String() string {
|
||||
return "tpm"
|
||||
}
|
||||
|
||||
// CheckSecurebootOnEnroll implements the config.Provider interface.
|
||||
func (e *EncryptionKeyTPM) CheckSecurebootOnEnroll() bool {
|
||||
if e == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return pointer.SafeDeref(e.TPMCheckSecurebootStatusOnEnroll)
|
||||
}
|
||||
|
||||
// Key implements the config.Provider interface.
|
||||
func (e *EncryptionKeyStatic) Key() []byte {
|
||||
return []byte(e.KeyData)
|
||||
}
|
||||
|
||||
// String implements the config.Provider interface.
|
||||
func (e *EncryptionKeyStatic) String() string {
|
||||
return "static"
|
||||
}
|
||||
|
||||
// Endpoint implements the config.Provider interface.
|
||||
func (e *EncryptionKeyKMS) Endpoint() string {
|
||||
return e.KMSEndpoint
|
||||
}
|
||||
|
||||
// String implements the config.Provider interface.
|
||||
func (e *EncryptionKeyKMS) String() string {
|
||||
return "kms"
|
||||
}
|
||||
16
pkg/machinery/config/types/block/testdata/uservolumeconfig_encrypted.yaml
vendored
Normal file
16
pkg/machinery/config/types/block/testdata/uservolumeconfig_encrypted.yaml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: UserVolumeConfig
|
||||
name: secret-store
|
||||
provisioning:
|
||||
diskSelector:
|
||||
match: '!system_disk'
|
||||
minSize: 10GiB
|
||||
encryption:
|
||||
provider: luks2
|
||||
keys:
|
||||
- slot: 0
|
||||
tpm: {}
|
||||
- slot: 1
|
||||
static:
|
||||
passphrase: topsecret
|
||||
cipher: aes-xts-plain64
|
||||
@ -69,6 +69,9 @@ type UserVolumeConfigV1Alpha1 struct {
|
||||
// description: |
|
||||
// The filesystem describes how the volume is formatted.
|
||||
FilesystemSpec FilesystemSpec `yaml:"filesystem,omitempty"`
|
||||
// description: |
|
||||
// The encryption describes how the volume is encrypted.
|
||||
EncryptionSpec EncryptionSpec `yaml:"encryption,omitempty"`
|
||||
}
|
||||
|
||||
// NewUserVolumeConfigV1Alpha1 creates a new user volume config document.
|
||||
@ -93,6 +96,21 @@ func exampleUserVolumeConfigV1Alpha1() *UserVolumeConfigV1Alpha1 {
|
||||
cfg.FilesystemSpec = FilesystemSpec{
|
||||
FilesystemType: block.FilesystemTypeXFS,
|
||||
}
|
||||
cfg.EncryptionSpec = EncryptionSpec{
|
||||
EncryptionProvider: block.EncryptionProviderLUKS2,
|
||||
EncryptionKeys: []EncryptionKey{
|
||||
{
|
||||
KeySlot: 0,
|
||||
KeyTPM: &EncryptionKeyTPM{},
|
||||
},
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyStatic: &EncryptionKeyStatic{
|
||||
KeyData: "topsecret",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
@ -150,6 +168,10 @@ func (s *UserVolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validatio
|
||||
warnings = append(warnings, extraWarnings...)
|
||||
validationErrors = errors.Join(validationErrors, extraErrors)
|
||||
|
||||
extraWarnings, extraErrors = s.EncryptionSpec.Validate()
|
||||
warnings = append(warnings, extraWarnings...)
|
||||
validationErrors = errors.Join(validationErrors, extraErrors)
|
||||
|
||||
return warnings, validationErrors
|
||||
}
|
||||
|
||||
@ -166,6 +188,15 @@ func (s *UserVolumeConfigV1Alpha1) Filesystem() config.FilesystemConfig {
|
||||
return s.FilesystemSpec
|
||||
}
|
||||
|
||||
// Encryption implements config.UserVolumeConfig interface.
|
||||
func (s *UserVolumeConfigV1Alpha1) Encryption() config.EncryptionConfig {
|
||||
if s.EncryptionSpec.EncryptionProvider == block.EncryptionProviderNone {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s.EncryptionSpec
|
||||
}
|
||||
|
||||
// FilesystemSpec configures the filesystem for the volume.
|
||||
type FilesystemSpec struct {
|
||||
// description: |
|
||||
|
||||
@ -41,6 +41,33 @@ func TestUserVolumeConfigMarshalUnmarshal(t *testing.T) {
|
||||
c.ProvisioningSpec.ProvisioningMaxSize = block.MustByteSize("100GiB")
|
||||
c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeXFS
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "encrypted",
|
||||
filename: "uservolumeconfig_encrypted.yaml",
|
||||
cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
|
||||
c := block.NewUserVolumeConfigV1Alpha1()
|
||||
c.MetaName = "secret-store"
|
||||
|
||||
require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`)))
|
||||
c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
|
||||
c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2
|
||||
c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64"
|
||||
c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 0,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyStatic: &block.EncryptionKeyStatic{
|
||||
KeyData: "topsecret",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
@ -175,6 +202,72 @@ func TestUserVolumeConfigValidate(t *testing.T) {
|
||||
|
||||
expectedErrors: "unsupported filesystem type: iso9660",
|
||||
},
|
||||
{
|
||||
name: "no encryption provider",
|
||||
|
||||
cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
|
||||
c := block.NewUserVolumeConfigV1Alpha1()
|
||||
c.MetaName = constants.EphemeralPartitionLabel
|
||||
|
||||
require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`)))
|
||||
c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
|
||||
c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 0,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
}
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedErrors: "unsupported encryption provider: none",
|
||||
},
|
||||
{
|
||||
name: "no encryption keys",
|
||||
|
||||
cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
|
||||
c := block.NewUserVolumeConfigV1Alpha1()
|
||||
c.MetaName = constants.EphemeralPartitionLabel
|
||||
|
||||
require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`)))
|
||||
c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
|
||||
c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedErrors: "encryption keys are required",
|
||||
},
|
||||
{
|
||||
name: "invalid encryption key slots",
|
||||
|
||||
cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
|
||||
c := block.NewUserVolumeConfigV1Alpha1()
|
||||
c.MetaName = constants.EphemeralPartitionLabel
|
||||
|
||||
require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`)))
|
||||
c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
|
||||
c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2
|
||||
c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
{
|
||||
KeySlot: 0,
|
||||
},
|
||||
{
|
||||
KeySlot: 1,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
}
|
||||
|
||||
return c
|
||||
},
|
||||
|
||||
expectedErrors: "at least one encryption key type must be specified for slot 0\nduplicate key slot 1",
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
|
||||
@ -187,6 +280,27 @@ func TestUserVolumeConfigValidate(t *testing.T) {
|
||||
c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
|
||||
c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid encrypted",
|
||||
|
||||
cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 {
|
||||
c := block.NewUserVolumeConfigV1Alpha1()
|
||||
c.MetaName = constants.EphemeralPartitionLabel
|
||||
|
||||
require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`)))
|
||||
c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB")
|
||||
c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2
|
||||
c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64"
|
||||
c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{
|
||||
{
|
||||
KeySlot: 0,
|
||||
KeyTPM: &block.EncryptionKeyTPM{},
|
||||
},
|
||||
}
|
||||
|
||||
return c
|
||||
},
|
||||
},
|
||||
|
||||
@ -19,7 +19,6 @@ import (
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/siderolabs/crypto/x509"
|
||||
"github.com/siderolabs/gen/xslices"
|
||||
"github.com/siderolabs/go-blockdevice/v2/encryption"
|
||||
"github.com/siderolabs/go-pointer"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/machinery/cel"
|
||||
@ -27,6 +26,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/config"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/machine"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/resources/block"
|
||||
)
|
||||
|
||||
// Verify interfaces.
|
||||
@ -1532,12 +1532,15 @@ func (p *DiskPartition) MountPoint() string {
|
||||
}
|
||||
|
||||
// Provider implements the config.Provider interface.
|
||||
func (e *EncryptionConfig) Provider() string {
|
||||
func (e *EncryptionConfig) Provider() block.EncryptionProviderType {
|
||||
if e.EncryptionProvider == "" {
|
||||
return encryption.LUKS2
|
||||
return block.EncryptionProviderLUKS2
|
||||
}
|
||||
|
||||
return e.EncryptionProvider
|
||||
// the provider is validated in the machine config validation
|
||||
provider, _ := block.EncryptionProviderTypeString(e.EncryptionProvider) //nolint:errcheck
|
||||
|
||||
return provider
|
||||
}
|
||||
|
||||
// Cipher implements the config.Provider interface.
|
||||
@ -1646,7 +1649,7 @@ func (e *EncryptionKeyKMS) String() string {
|
||||
}
|
||||
|
||||
// Get implements the config.Provider interface.
|
||||
func (e *SystemDiskEncryptionConfig) Get(label string) config.Encryption {
|
||||
func (e *SystemDiskEncryptionConfig) Get(label string) config.EncryptionConfig {
|
||||
switch label {
|
||||
case constants.StatePartitionLabel:
|
||||
if e.StatePartition == nil {
|
||||
|
||||
@ -32,7 +32,6 @@ require (
|
||||
github.com/siderolabs/crypto v0.5.1
|
||||
github.com/siderolabs/gen v0.8.0
|
||||
github.com/siderolabs/go-api-signature v0.3.6
|
||||
github.com/siderolabs/go-blockdevice/v2 v2.0.16
|
||||
github.com/siderolabs/go-pointer v1.0.1
|
||||
github.com/siderolabs/net v0.4.0
|
||||
github.com/siderolabs/protoenc v0.2.2
|
||||
|
||||
@ -117,8 +117,6 @@ github.com/siderolabs/gen v0.8.0 h1:Pj93+hexkk5hQ7izjJ6YXnEWc8vlzOmDwFz13/VzS7o=
|
||||
github.com/siderolabs/gen v0.8.0/go.mod h1:an3a2Y53O7kUjnnK8Bfu3gewtvnIOu5RTU6HalFtXQQ=
|
||||
github.com/siderolabs/go-api-signature v0.3.6 h1:wDIsXbpl7Oa/FXvxB6uz4VL9INA9fmr3EbmjEZYFJrU=
|
||||
github.com/siderolabs/go-api-signature v0.3.6/go.mod h1:hoH13AfunHflxbXfh+NoploqV13ZTDfQ1mQJWNVSW9U=
|
||||
github.com/siderolabs/go-blockdevice/v2 v2.0.16 h1:QeQ72S7M/rwXV1nah/uzyBPeF/PLCEwuSqj1hFeZYQU=
|
||||
github.com/siderolabs/go-blockdevice/v2 v2.0.16/go.mod h1:74htzCV913UzaLZ4H+NBXkwWlYnBJIq5m/379ZEcu8w=
|
||||
github.com/siderolabs/go-pointer v1.0.1 h1:f7Yi4IK1jptS8yrT9GEbwhmGcVxvPQgBUG/weH3V3DM=
|
||||
github.com/siderolabs/go-pointer v1.0.1/go.mod h1:C8Q/3pNHT4RE9e4rYR9PHeS6KPMlStRBgYrJQJNy/vA=
|
||||
github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg=
|
||||
|
||||
@ -167,6 +167,7 @@ talosctl cluster create [flags]
|
||||
--docker-host-ip string Host IP to forward exposed ports to (Docker provisioner only) (default "0.0.0.0")
|
||||
--encrypt-ephemeral enable ephemeral partition encryption
|
||||
--encrypt-state enable state partition encryption
|
||||
--encrypt-user-volumes enable ephemeral partition encryption
|
||||
--endpoint string use endpoint instead of provider defaults
|
||||
-p, --exposed-ports string Comma-separated list of ports/protocols to expose on init node. Ex -p <hostPort>:<containerPort>/<protocol (tcp or udp)> (Docker provisioner only)
|
||||
--extra-boot-kernel-args string add extra kernel args to the initial boot from vmlinuz and initramfs (QEMU only)
|
||||
|
||||
@ -33,6 +33,37 @@ provisioning:
|
||||
# The filesystem describes how the volume is formatted.
|
||||
filesystem:
|
||||
type: xfs # Filesystem type. Default is `xfs`.
|
||||
# The encryption describes how the volume is encrypted.
|
||||
encryption:
|
||||
provider: luks2 # Encryption provider to use for the encryption.
|
||||
# Defines the encryption keys generation and storage method.
|
||||
keys:
|
||||
- slot: 0 # Key slot number for LUKS2 encryption.
|
||||
# Enable TPM based disk encryption.
|
||||
tpm: {}
|
||||
|
||||
# # KMS managed encryption key.
|
||||
# kms:
|
||||
# endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key.
|
||||
- slot: 1 # Key slot number for LUKS2 encryption.
|
||||
# Key which value is stored in the configuration file.
|
||||
static:
|
||||
passphrase: topsecret # Defines the static passphrase value.
|
||||
|
||||
# # KMS managed encryption key.
|
||||
# kms:
|
||||
# endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key.
|
||||
|
||||
# # Cipher to use for the encryption. Depends on the encryption provider.
|
||||
# cipher: aes-xts-plain64
|
||||
|
||||
# # Defines the encryption sector size.
|
||||
# blockSize: 4096
|
||||
|
||||
# # Additional --perf parameters for the LUKS2 encryption.
|
||||
# options:
|
||||
# - no_read_workqueue
|
||||
# - no_write_workqueue
|
||||
{{< /highlight >}}
|
||||
|
||||
|
||||
@ -41,6 +72,7 @@ filesystem:
|
||||
|`name` |string |<details><summary>Name of the volume.</summary><br />Name might be between 1 and 34 characters long and can only contain:<br />lowercase and uppercase ASCII letters, digits, and hyphens.</details> | |
|
||||
|`provisioning` |<a href="#UserVolumeConfig.provisioning">ProvisioningSpec</a> |The provisioning describes how the volume is provisioned. | |
|
||||
|`filesystem` |<a href="#UserVolumeConfig.filesystem">FilesystemSpec</a> |The filesystem describes how the volume is formatted. | |
|
||||
|`encryption` |<a href="#UserVolumeConfig.encryption">EncryptionSpec</a> |The encryption describes how the volume is encrypted. | |
|
||||
|
||||
|
||||
|
||||
@ -104,5 +136,147 @@ FilesystemSpec configures the filesystem for the volume.
|
||||
|
||||
|
||||
|
||||
## encryption {#UserVolumeConfig.encryption}
|
||||
|
||||
EncryptionSpec represents volume encryption settings.
|
||||
|
||||
|
||||
|
||||
{{< highlight yaml >}}
|
||||
encryption:
|
||||
provider: luks2 # Encryption provider to use for the encryption.
|
||||
# Defines the encryption keys generation and storage method.
|
||||
keys:
|
||||
- slot: 0 # Key slot number for LUKS2 encryption.
|
||||
# Key which value is stored in the configuration file.
|
||||
static:
|
||||
passphrase: exampleKey # Defines the static passphrase value.
|
||||
|
||||
# # KMS managed encryption key.
|
||||
# kms:
|
||||
# endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key.
|
||||
- slot: 1 # Key slot number for LUKS2 encryption.
|
||||
# KMS managed encryption key.
|
||||
kms:
|
||||
endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key.
|
||||
cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider.
|
||||
blockSize: 4096 # Defines the encryption sector size.
|
||||
|
||||
# # Additional --perf parameters for the LUKS2 encryption.
|
||||
# options:
|
||||
# - no_read_workqueue
|
||||
# - no_write_workqueue
|
||||
{{< /highlight >}}
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`provider` |EncryptionProviderType |Encryption provider to use for the encryption. |`luks2`<br /> |
|
||||
|`keys` |<a href="#UserVolumeConfig.encryption.keys.">[]EncryptionKey</a> |Defines the encryption keys generation and storage method. | |
|
||||
|`cipher` |string |Cipher to use for the encryption. Depends on the encryption provider. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
cipher: aes-xts-plain64
|
||||
{{< /highlight >}}</details> |`aes-xts-plain64`<br />`xchacha12,aes-adiantum-plain64`<br />`xchacha20,aes-adiantum-plain64`<br /> |
|
||||
|`keySize` |uint |Defines the encryption key length. | |
|
||||
|`blockSize` |uint64 |Defines the encryption sector size. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
blockSize: 4096
|
||||
{{< /highlight >}}</details> | |
|
||||
|`options` |[]string |Additional --perf parameters for the LUKS2 encryption. <details><summary>Show example(s)</summary>{{< highlight yaml >}}
|
||||
options:
|
||||
- no_read_workqueue
|
||||
- no_write_workqueue
|
||||
{{< /highlight >}}</details> |`no_read_workqueue`<br />`no_write_workqueue`<br />`same_cpu_crypt`<br /> |
|
||||
|
||||
|
||||
|
||||
|
||||
### keys[] {#UserVolumeConfig.encryption.keys.}
|
||||
|
||||
EncryptionKey represents configuration for disk encryption key.
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`slot` |int |Key slot number for LUKS2 encryption. | |
|
||||
|`static` |<a href="#UserVolumeConfig.encryption.keys..static">EncryptionKeyStatic</a> |Key which value is stored in the configuration file. | |
|
||||
|`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. | |
|
||||
|`tpm` |<a href="#UserVolumeConfig.encryption.keys..tpm">EncryptionKeyTPM</a> |Enable TPM based disk encryption. | |
|
||||
|
||||
|
||||
|
||||
|
||||
#### static {#UserVolumeConfig.encryption.keys..static}
|
||||
|
||||
EncryptionKeyStatic represents throw away key type.
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`passphrase` |string |Defines the static passphrase value. | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### nodeID {#UserVolumeConfig.encryption.keys..nodeID}
|
||||
|
||||
EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### kms {#UserVolumeConfig.encryption.keys..kms}
|
||||
|
||||
EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server.
|
||||
|
||||
|
||||
|
||||
{{< highlight yaml >}}
|
||||
encryption:
|
||||
keys:
|
||||
- kms:
|
||||
endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key.
|
||||
{{< /highlight >}}
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`endpoint` |string |KMS endpoint to Seal/Unseal the key. | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### tpm {#UserVolumeConfig.encryption.keys..tpm}
|
||||
|
||||
EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM.
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Value(s) |
|
||||
|-------|------|-------------|----------|
|
||||
|`checkSecurebootStatusOnEnroll` |bool |<details><summary>Check that Secureboot is enabled in the EFI firmware.</summary>If Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.</details> | |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -16,6 +16,158 @@
|
||||
"type": "object",
|
||||
"description": "DiskSelector selects a disk for the volume."
|
||||
},
|
||||
"block.EncryptionKey": {
|
||||
"properties": {
|
||||
"slot": {
|
||||
"type": "integer",
|
||||
"title": "slot",
|
||||
"description": "Key slot number for LUKS2 encryption.\n",
|
||||
"markdownDescription": "Key slot number for LUKS2 encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKey slot number for LUKS2 encryption.\u003c/p\u003e\n"
|
||||
},
|
||||
"static": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyStatic",
|
||||
"title": "static",
|
||||
"description": "Key which value is stored in the configuration file.\n",
|
||||
"markdownDescription": "Key which value is stored in the configuration file.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKey which value is stored in the configuration file.\u003c/p\u003e\n"
|
||||
},
|
||||
"nodeID": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyNodeID",
|
||||
"title": "nodeID",
|
||||
"description": "Deterministically generated key from the node UUID and PartitionLabel.\n",
|
||||
"markdownDescription": "Deterministically generated key from the node UUID and PartitionLabel.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDeterministically generated key from the node UUID and PartitionLabel.\u003c/p\u003e\n"
|
||||
},
|
||||
"kms": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyKMS",
|
||||
"title": "kms",
|
||||
"description": "KMS managed encryption key.\n",
|
||||
"markdownDescription": "KMS managed encryption key.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKMS managed encryption key.\u003c/p\u003e\n"
|
||||
},
|
||||
"tpm": {
|
||||
"$ref": "#/$defs/block.EncryptionKeyTPM",
|
||||
"title": "tpm",
|
||||
"description": "Enable TPM based disk encryption.\n",
|
||||
"markdownDescription": "Enable TPM based disk encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKey represents configuration for disk encryption key."
|
||||
},
|
||||
"block.EncryptionKeyKMS": {
|
||||
"properties": {
|
||||
"endpoint": {
|
||||
"type": "string",
|
||||
"title": "endpoint",
|
||||
"description": "KMS endpoint to Seal/Unseal the key.\n",
|
||||
"markdownDescription": "KMS endpoint to Seal/Unseal the key.",
|
||||
"x-intellij-html-description": "\u003cp\u003eKMS endpoint to Seal/Unseal the key.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server."
|
||||
},
|
||||
"block.EncryptionKeyNodeID": {
|
||||
"properties": {},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel."
|
||||
},
|
||||
"block.EncryptionKeyStatic": {
|
||||
"properties": {
|
||||
"passphrase": {
|
||||
"type": "string",
|
||||
"title": "passphrase",
|
||||
"description": "Defines the static passphrase value.\n",
|
||||
"markdownDescription": "Defines the static passphrase value.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the static passphrase value.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyStatic represents throw away key type."
|
||||
},
|
||||
"block.EncryptionKeyTPM": {
|
||||
"properties": {
|
||||
"checkSecurebootStatusOnEnroll": {
|
||||
"type": "boolean",
|
||||
"title": "checkSecurebootStatusOnEnroll",
|
||||
"description": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\n",
|
||||
"markdownDescription": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.",
|
||||
"x-intellij-html-description": "\u003cp\u003eCheck that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail. As the TPM key is anyways bound to the value of PCR 7, changing Secureboot status or configuration after the initial enrollment will make the key unusable.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM."
|
||||
},
|
||||
"block.EncryptionSpec": {
|
||||
"properties": {
|
||||
"provider": {
|
||||
"enum": [
|
||||
"luks2"
|
||||
],
|
||||
"title": "provider",
|
||||
"description": "Encryption provider to use for the encryption.\n",
|
||||
"markdownDescription": "Encryption provider to use for the encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eEncryption provider to use for the encryption.\u003c/p\u003e\n"
|
||||
},
|
||||
"keys": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/block.EncryptionKey"
|
||||
},
|
||||
"type": "array",
|
||||
"title": "keys",
|
||||
"description": "Defines the encryption keys generation and storage method.\n",
|
||||
"markdownDescription": "Defines the encryption keys generation and storage method.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the encryption keys generation and storage method.\u003c/p\u003e\n"
|
||||
},
|
||||
"cipher": {
|
||||
"enum": [
|
||||
"aes-xts-plain64",
|
||||
"xchacha12,aes-adiantum-plain64",
|
||||
"xchacha20,aes-adiantum-plain64"
|
||||
],
|
||||
"title": "cipher",
|
||||
"description": "Cipher to use for the encryption. Depends on the encryption provider.\n",
|
||||
"markdownDescription": "Cipher to use for the encryption. Depends on the encryption provider.",
|
||||
"x-intellij-html-description": "\u003cp\u003eCipher to use for the encryption. Depends on the encryption provider.\u003c/p\u003e\n"
|
||||
},
|
||||
"keySize": {
|
||||
"type": "integer",
|
||||
"title": "keySize",
|
||||
"description": "Defines the encryption key length.\n",
|
||||
"markdownDescription": "Defines the encryption key length.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the encryption key length.\u003c/p\u003e\n"
|
||||
},
|
||||
"blockSize": {
|
||||
"type": "integer",
|
||||
"title": "blockSize",
|
||||
"description": "Defines the encryption sector size.\n",
|
||||
"markdownDescription": "Defines the encryption sector size.",
|
||||
"x-intellij-html-description": "\u003cp\u003eDefines the encryption sector size.\u003c/p\u003e\n"
|
||||
},
|
||||
"options": {
|
||||
"enum": [
|
||||
"no_read_workqueue",
|
||||
"no_write_workqueue",
|
||||
"same_cpu_crypt"
|
||||
],
|
||||
"title": "options",
|
||||
"description": "Additional –perf parameters for the LUKS2 encryption.\n",
|
||||
"markdownDescription": "Additional --perf parameters for the LUKS2 encryption.",
|
||||
"x-intellij-html-description": "\u003cp\u003eAdditional \u0026ndash;perf parameters for the LUKS2 encryption.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"description": "EncryptionSpec represents volume encryption settings."
|
||||
},
|
||||
"block.FilesystemSpec": {
|
||||
"properties": {
|
||||
"type": {
|
||||
@ -108,6 +260,13 @@
|
||||
"description": "The filesystem describes how the volume is formatted.\n",
|
||||
"markdownDescription": "The filesystem describes how the volume is formatted.",
|
||||
"x-intellij-html-description": "\u003cp\u003eThe filesystem describes how the volume is formatted.\u003c/p\u003e\n"
|
||||
},
|
||||
"encryption": {
|
||||
"$ref": "#/$defs/block.EncryptionSpec",
|
||||
"title": "encryption",
|
||||
"description": "The encryption describes how the volume is encrypted.\n",
|
||||
"markdownDescription": "The encryption describes how the volume is encrypted.",
|
||||
"x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
|
||||
@ -225,6 +225,8 @@ provisioning:
|
||||
match: disk.transport == 'nvme' && !system_disk
|
||||
```
|
||||
|
||||
> Note: Currently, encryption for `EPHEMERAL` and `STATE` volumes is configured using [another config document]({{< relref "../../reference/configuration/v1alpha1/config#Config.machine.systemDiskEncryption" >}}).
|
||||
|
||||
### `IMAGECACHE` Volume
|
||||
|
||||
This system volume is not provisioned by default, and it only gets created if the [Image Cache]({{< relref "image-cache" >}}) feature is enabled.
|
||||
@ -245,6 +247,8 @@ The volume name must be unique across all user volumes, and it should be between
|
||||
The volume label is derived from the volume name as `u-<volume-name>`, and it is used to identify the volume on the disk after initial provisioning.
|
||||
The volume mount location is `/var/mnt/<volume-name>`, and it gets automatically propagated into the `kubelet` container to provide additional features like `subPath` mounts.
|
||||
|
||||
Disk encryption can be optionally enabled for user volumes.
|
||||
|
||||
### Creating User Volumes
|
||||
|
||||
To create a user volume, append the following [document]({{< relref "../../reference/configuration/block/uservolumeconfig" >}}) to the machine configuration:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user