feat: implement ephemeral partition encryption

This PR introduces the first part of disk encryption support.
New config section `systemDiskEncryption` was added into MachineConfig.
For now it contains only Ephemeral partition encryption.

Encryption itself supports two kinds of keys for now:
- node id deterministic key.
- static key which is hardcoded in the config and mainly used for test
purposes.

Talosctl cluster create can now be told to encrypt ephemeral partition
by using `--encrypt-ephemeral` flag.

Additionally:
- updated pkgs library version.
- changed Dockefile to copy cryptsetup deps from pkgs.

Signed-off-by: Artem Chernyshev <artem.0xD2@gmail.com>
This commit is contained in:
Artem Chernyshev
2021-01-31 18:23:09 +03:00
committed by talos-bot
parent e5bd35ae3c
commit 58ff2c9808
26 changed files with 1340 additions and 129 deletions

View File

@@ -358,16 +358,24 @@ local integration_cilium = Step("e2e-cilium-1.8.5", target="e2e-qemu", privilege
"SHORT_INTEGRATION_TEST": "yes",
"CUSTOM_CNI_URL": "https://raw.githubusercontent.com/cilium/cilium/v1.8.5/install/kubernetes/quick-install.yaml",
"REGISTRY": local_registry,
"CLUSTER_CIDR": 2,
});
local integration_uefi = Step("e2e-uefi", target="e2e-qemu", privileged=true, depends_on=[integration_cilium], environment={
"SHORT_INTEGRATION_TEST": "yes",
"WITH_UEFI": "true",
"CLUSTER_CIDR": 3,
"REGISTRY": local_registry,
});
local integration_disk_image = Step("e2e-disk-image", target="e2e-qemu", privileged=true, depends_on=[integration_uefi], environment={
"SHORT_INTEGRATION_TEST": "yes",
"USE_DISK_IMAGE": "true",
"REGISTRY": local_registry,
"CLUSTER_CIDR": 4,
});
local integration_disk_encryption = Step("e2e-encrypted", target="e2e-qemu", privileged=true, depends_on=[integration_disk_image], environment={
"WITH_DISK_ENCRYPTION": "true",
"REGISTRY": local_registry,
"CLUSTER_CIDR": 5,
});
local push_edge = {
@@ -403,13 +411,13 @@ local integration_pipelines = [
Pipeline('integration-qemu', default_pipeline_steps + [integration_qemu, push_edge]) + integration_trigger(['integration-qemu']),
Pipeline('integration-provision-0', default_pipeline_steps + [integration_provision_tests_prepare, integration_provision_tests_track_0]) + integration_trigger(['integration-provision', 'integration-provision-0']),
Pipeline('integration-provision-1', default_pipeline_steps + [integration_provision_tests_prepare, integration_provision_tests_track_1]) + integration_trigger(['integration-provision', 'integration-provision-1']),
Pipeline('integration-misc', default_pipeline_steps + [integration_cilium, integration_uefi, integration_disk_image]) + integration_trigger(['integration-misc']),
Pipeline('integration-misc', default_pipeline_steps + [integration_cilium, integration_uefi, integration_disk_image, integration_disk_encryption]) + integration_trigger(['integration-misc']),
// cron pipelines, triggered on schedule events
Pipeline('cron-integration-qemu', default_pipeline_steps + [integration_qemu, push_edge]) + cron_trigger(['thrice-daily', 'nightly']),
Pipeline('cron-integration-provision-0', default_pipeline_steps + [integration_provision_tests_prepare, integration_provision_tests_track_0]) + cron_trigger(['thrice-daily', 'nightly']),
Pipeline('cron-integration-provision-1', default_pipeline_steps + [integration_provision_tests_prepare, integration_provision_tests_track_1]) + cron_trigger(['thrice-daily', 'nightly']),
Pipeline('cron-integration-misc', default_pipeline_steps + [integration_cilium, integration_uefi, integration_disk_image]) + cron_trigger(['thrice-daily', 'nightly']),
Pipeline('cron-integration-misc', default_pipeline_steps + [integration_cilium, integration_uefi, integration_disk_image, integration_disk_encryption]) + cron_trigger(['thrice-daily', 'nightly']),
];

View File

@@ -17,6 +17,8 @@ FROM ghcr.io/talos-systems/dosfstools:${PKGS} AS pkg-dosfstools
FROM ghcr.io/talos-systems/eudev:${PKGS} AS pkg-eudev
FROM ghcr.io/talos-systems/grub:${PKGS} AS pkg-grub
FROM ghcr.io/talos-systems/iptables:${PKGS} AS pkg-iptables
FROM ghcr.io/talos-systems/libjson-c:${PKGS} AS pkg-libjson-c
FROM ghcr.io/talos-systems/libpopt:${PKGS} AS pkg-libpopt
FROM ghcr.io/talos-systems/libressl:${PKGS} AS pkg-libressl
FROM ghcr.io/talos-systems/libseccomp:${PKGS} AS pkg-libseccomp
FROM ghcr.io/talos-systems/linux-firmware:${PKGS} AS pkg-linux-firmware
@@ -358,6 +360,8 @@ COPY --from=pkg-containerd / /rootfs
COPY --from=pkg-dosfstools / /rootfs
COPY --from=pkg-eudev / /rootfs
COPY --from=pkg-iptables / /rootfs
COPY --from=pkg-libjson-c / /rootfs
COPY --from=pkg-libpopt / /rootfs
COPY --from=pkg-libressl / /rootfs
COPY --from=pkg-libseccomp / /rootfs
COPY --from=pkg-linux-firmware /lib/firmware/bnx2 /rootfs/lib/firmware/bnx2

View File

@@ -510,7 +510,6 @@ func (m *Manifest) zeroDevice(device Device) (err error) {
func (t *Target) Partition(pt *gpt.GPT, pos int, bd *blockdevice.BlockDevice) (err error) {
if t.Skip {
part := pt.Partitions().FindByName(t.Label)
if part != nil {
log.Printf("skipped %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
}

View File

@@ -20,6 +20,7 @@ import (
humanize "github.com/dustin/go-humanize"
"github.com/spf13/cobra"
"github.com/talos-systems/go-blockdevice/blockdevice/encryption"
talosnet "github.com/talos-systems/net"
"k8s.io/client-go/tools/clientcmd"
@@ -42,51 +43,52 @@ import (
)
var (
talosconfig string
nodeImage string
nodeInstallImage string
registryMirrors []string
registryInsecure []string
kubernetesVersion string
nodeVmlinuzPath string
nodeInitramfsPath string
nodeISOPath string
nodeDiskImagePath string
applyConfigEnabled bool
bootloaderEnabled bool
uefiEnabled bool
configDebug bool
networkCIDR string
networkMTU int
networkIPv4 bool
networkIPv6 bool
wireguardCIDR string
nameservers []string
dnsDomain string
workers int
masters int
clusterCpus string
clusterMemory int
clusterDiskSize int
clusterDisks []string
targetArch string
clusterWait bool
clusterWaitTimeout time.Duration
forceInitNodeAsEndpoint bool
forceEndpoint string
inputDir string
cniBinPath []string
cniConfDir string
cniCacheDir string
cniBundleURL string
ports string
dockerHostIP string
withInitNode bool
customCNIUrl string
crashdumpOnFailure bool
skipKubeconfig bool
skipInjectingConfig bool
talosVersion string
talosconfig string
nodeImage string
nodeInstallImage string
registryMirrors []string
registryInsecure []string
kubernetesVersion string
nodeVmlinuzPath string
nodeInitramfsPath string
nodeISOPath string
nodeDiskImagePath string
applyConfigEnabled bool
bootloaderEnabled bool
uefiEnabled bool
configDebug bool
networkCIDR string
networkMTU int
networkIPv4 bool
networkIPv6 bool
wireguardCIDR string
nameservers []string
dnsDomain string
workers int
masters int
clusterCpus string
clusterMemory int
clusterDiskSize int
clusterDisks []string
targetArch string
clusterWait bool
clusterWaitTimeout time.Duration
forceInitNodeAsEndpoint bool
forceEndpoint string
inputDir string
cniBinPath []string
cniConfDir string
cniCacheDir string
cniBundleURL string
ports string
dockerHostIP string
withInitNode bool
customCNIUrl string
crashdumpOnFailure bool
skipKubeconfig bool
skipInjectingConfig bool
talosVersion string
encryptEphemeralPartition bool
)
// createCmd represents the cluster up command.
@@ -304,6 +306,22 @@ func create(ctx context.Context) (err error) {
genOptions = append(genOptions, generate.WithVersionContract(versionContract))
}
if encryptEphemeralPartition {
genOptions = append(genOptions, generate.WithSystemDiskEncryption(
&v1alpha1.SystemDiskEncryptionConfig{
EphemeralPartition: &v1alpha1.EncryptionConfig{
EncryptionProvider: encryption.LUKS2,
EncryptionKeys: []*v1alpha1.EncryptionKey{
{
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
KeySlot: 0,
},
},
},
},
))
}
defaultInternalLB, defaultEndpoint := provisioner.GetLoadBalancers(request.Network)
if defaultInternalLB == "" {
@@ -701,6 +719,7 @@ func init() {
createCmd.Flags().BoolVar(&crashdumpOnFailure, "crashdump", false, "print debug crashdump to stderr when cluster startup fails")
createCmd.Flags().BoolVar(&skipKubeconfig, "skip-kubeconfig", false, "skip merging kubeconfig from the created cluster")
createCmd.Flags().BoolVar(&skipInjectingConfig, "skip-injecting-config", false, "skip injecting config from embedded metadata server, write config files to current directory")
createCmd.Flags().BoolVar(&encryptEphemeralPartition, "encrypt-ephemeral", false, "enable ephemeral partition encryption")
createCmd.Flags().StringVar(&talosVersion, "talos-version", "", "the desired Talos version to generate config for (if not set, defaults to image version)")
Cmd.AddCommand(createCmd)
}

View File

@@ -8,6 +8,7 @@ source ./hack/test/e2e.sh
PROVISIONER=qemu
CLUSTER_NAME=e2e-${PROVISIONER}
CLUSTER_CIDR=${CLUSTER_CIDR:-1}
case "${CI:-false}" in
true)
@@ -45,6 +46,15 @@ case "${USE_DISK_IMAGE:-false}" in
;;
esac
case "${WITH_DISK_ENCRYPTION:-false}" in
false)
DISK_ENCRYPTION_FLAG=""
;;
*)
DISK_ENCRYPTION_FLAG="--encrypt-ephemeral"
;;
esac
function create_cluster {
build_registry_mirrors
@@ -55,7 +65,7 @@ function create_cluster {
--mtu 1450 \
--memory 2048 \
--cpus 2.0 \
--cidr 172.20.1.0/24 \
--cidr 172.20.${CLUSTER_CIDR}.0/24 \
--user-disk /var/lib/extra:100MB \
--user-disk /var/lib/p1:100MB:/var/lib/p2:100MB \
--install-image ${REGISTRY:-ghcr.io}/talos-systems/installer:${INSTALLER_TAG} \
@@ -63,11 +73,12 @@ function create_cluster {
--cni-bundle-url ${ARTIFACTS}/talosctl-cni-bundle-'${ARCH}'.tar.gz \
--crashdump \
${DISK_IMAGE_FLAG} \
${DISK_ENCRYPTION_FLAG} \
${REGISTRY_MIRROR_FLAGS} \
${QEMU_FLAGS} \
${CUSTOM_CNI_FLAG}
"${TALOSCTL}" config node 172.20.1.2
"${TALOSCTL}" config node 172.20.${CLUSTER_CIDR}.2
}
function destroy_cluster() {

View File

@@ -23,6 +23,7 @@ import (
"github.com/talos-systems/talos/pkg/machinery/client"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/config/configloader"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1"
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
@@ -179,6 +180,129 @@ func (suite *ApplyConfigSuite) TestApplyOnReboot() {
suite.Require().NoError(err, "failed to apply deferred configuration (node %q): %w", node)
}
// TestApplyConfigRotateEncryptionSecrets verify key rotation by sequential apply config calls.
func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() {
suite.T().Skip("skip: this test is not stable yet")
node := suite.RandomDiscoveredNode(machine.TypeJoin)
suite.ClearConnectionRefused(suite.ctx, node)
nodeCtx := client.WithNodes(suite.ctx, node)
provider, err := suite.readConfigFromNode(nodeCtx)
suite.Assert().NoError(err)
encryption := provider.Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel)
if encryption == nil {
suite.T().Skip("skipped in not encrypted mode")
}
suite.WaitForBootDone(suite.ctx)
cfg, ok := encryption.(*v1alpha1.EncryptionConfig)
suite.Assert().True(ok)
keySets := [][]*v1alpha1.EncryptionKey{
{
{
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
KeySlot: 0,
},
{
KeyStatic: &v1alpha1.EncryptionKeyStatic{
KeyData: "AlO93jayutOpsDxDS=-",
},
KeySlot: 1,
},
},
{
{
KeyStatic: &v1alpha1.EncryptionKeyStatic{
KeyData: "AlO93jayutOpsDxDS=-",
},
KeySlot: 1,
},
},
{
{
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
KeySlot: 0,
},
{
KeyStatic: &v1alpha1.EncryptionKeyStatic{
KeyData: "AlO93jayutOpsDxDS=-",
},
KeySlot: 1,
},
},
{
{
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
KeySlot: 0,
},
{
KeyStatic: &v1alpha1.EncryptionKeyStatic{
KeyData: "1js4nfhvneJJsak=GVN4Inf5gh",
},
KeySlot: 1,
},
},
}
for _, keys := range keySets {
cfg.EncryptionKeys = keys
data, err := provider.Bytes()
suite.Require().NoError(err)
suite.AssertRebooted(suite.ctx, node, func(nodeCtx context.Context) error {
_, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{
Data: data,
})
if err != nil {
// It is expected that the connection will EOF here, so just log the error
suite.Assert().Nilf("failed to apply configuration (node %q): %w", node, err)
}
return nil
}, assertRebootedRebootTimeout)
// Verify configuration change
var newProvider config.Provider
suite.Require().Nilf(retry.Constant(time.Minute, retry.WithUnits(time.Second)).Retry(func() error {
newProvider, err = suite.readConfigFromNode(nodeCtx)
if err != nil {
return retry.ExpectedError(err)
}
return nil
}), "failed to read updated configuration from node %q: %w", node, err)
e := newProvider.Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel)
for i, k := range e.Keys() {
if keys[i].KeyStatic == nil {
suite.Require().Nil(k.Static())
} else {
suite.Require().Equal(keys[i].Static().Key(), k.Static().Key())
}
if keys[i].KeyNodeID == nil {
suite.Require().Nil(k.NodeID())
} else {
suite.Require().NotNil(keys[i].NodeID())
}
suite.Require().Equal(keys[i].Slot(), k.Slot())
suite.Require().Equal(keys[i].Slot(), k.Slot())
}
suite.WaitForBootDone(suite.ctx)
}
}
func (suite *ApplyConfigSuite) readConfigFromNode(nodeCtx context.Context) (config.Provider, error) {
// Load the current node machine config
cfgData := new(bytes.Buffer)

View File

@@ -0,0 +1,254 @@
// 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 encryption provides modules for the partition encryption handling.
package encryption
import (
"fmt"
"log"
"sort"
"strconv"
"github.com/talos-systems/go-blockdevice/blockdevice"
"github.com/talos-systems/go-blockdevice/blockdevice/encryption"
"github.com/talos-systems/go-blockdevice/blockdevice/encryption/luks"
"github.com/talos-systems/go-blockdevice/blockdevice/partition/gpt"
"github.com/talos-systems/talos/internal/pkg/encryption/keys"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// NewHandler creates new Handler.
func NewHandler(device *blockdevice.BlockDevice, partition *gpt.Partition, encryptionConfig config.Encryption) (*Handler, error) {
keys, err := getKeys(encryptionConfig, partition)
if err != nil {
return nil, err
}
var provider encryption.Provider
switch encryptionConfig.Kind() {
case encryption.LUKS2:
cipher, err := luks.ParseCipherKind(encryptionConfig.Cipher())
if err != nil {
return nil, err
}
provider = luks.New(
cipher,
)
default:
return nil, fmt.Errorf("unknown encryption kind %s", encryptionConfig.Kind())
}
return &Handler{
device: device,
partition: partition,
encryptionConfig: encryptionConfig,
keys: keys,
encryptionProvider: provider,
}, nil
}
// Handler reads encryption config, creates appropriate
// encryption provider, handles encrypted partition open and close.
type Handler struct {
device *blockdevice.BlockDevice
partition *gpt.Partition
encryptionConfig config.Encryption
keys []*encryption.Key
encryptionProvider encryption.Provider
encryptedPath string
}
// Open encrypted partition.
// nolint:gocyclo
func (h *Handler) Open() (string, error) {
partPath, err := h.partition.Path()
if err != nil {
return "", err
}
sb, err := h.partition.SuperBlock()
if err != nil {
return "", err
}
var path string
// encrypt if partition is not encrypted and empty
if sb == nil {
err = h.formatAndEncrypt(partPath)
if err != nil {
return "", err
}
} else if sb.Type() != h.encryptionConfig.Kind() {
return "", fmt.Errorf("failed to encrypt the partition %s, because it is not empty", partPath)
}
var k *encryption.Key
for _, k = range h.keys {
path, err = h.encryptionProvider.Open(partPath, k)
if err != nil {
if err == encryption.ErrEncryptionKeyRejected {
continue
}
return "", err
}
break
}
if path == "" {
return "", fmt.Errorf("failed to open encrypted device %s, no key matched", partPath)
}
log.Printf("mapped encrypted partition %s -> %s", partPath, path)
if err = h.syncKeys(k, partPath); err != nil {
return "", err
}
h.encryptedPath = path
return path, nil
}
// Close encrypted partition.
func (h *Handler) Close() error {
if h.encryptedPath == "" {
return nil
}
if err := h.encryptionProvider.Close(h.encryptedPath); err != nil {
return err
}
log.Printf("closed encrypted partition %s", h.encryptedPath)
return nil
}
func (h *Handler) formatAndEncrypt(path string) error {
log.Printf("encrypting the partition %s (%s)", path, h.partition.Name)
if len(h.keys) == 0 {
return fmt.Errorf("no encryption keys found")
}
key := h.keys[0]
err := h.encryptionProvider.Encrypt(path, key)
if err != nil {
return err
}
for _, extraKey := range h.keys[1:] {
if err = h.encryptionProvider.AddKey(path, key, extraKey); err != nil {
return err
}
}
return nil
}
//nolint:gocyclo
func (h *Handler) syncKeys(k *encryption.Key, path string) error {
keyslots, err := h.encryptionProvider.ReadKeyslots(path)
if err != nil {
return err
}
visited := map[string]bool{}
for _, key := range h.keys {
slot := fmt.Sprintf("%d", key.Slot)
visited[slot] = true
// no need to update the key which we already detected as unchanged
if k.Slot == key.Slot {
continue
}
// keyslot exists
if _, ok := keyslots.Keyslots[slot]; ok {
if err = h.updateKey(k, key, path); err != nil {
return err
}
log.Printf("updated encryption key at slot %d", key.Slot)
} else {
// keyslot does not exist so just add the key
if err = h.encryptionProvider.AddKey(path, k, key); err != nil {
return err
}
log.Printf("added encryption key to slot %d", key.Slot)
}
}
// cleanup deleted key slots
for slot := range keyslots.Keyslots {
if !visited[slot] {
s, err := strconv.ParseInt(slot, 10, 64)
if err != nil {
return err
}
if err = h.encryptionProvider.RemoveKey(path, int(s), k); err != nil {
return err
}
log.Printf("removed key at slot %d", k.Slot)
}
}
return nil
}
func (h *Handler) updateKey(existingKey, newKey *encryption.Key, path string) error {
if valid, err := h.encryptionProvider.CheckKey(path, newKey); err != nil {
return err
} else if !valid {
// re-add the key to the slot
err = h.encryptionProvider.RemoveKey(path, newKey.Slot, existingKey)
if err != nil {
return fmt.Errorf("failed to drop old key during key update %w", err)
}
err = h.encryptionProvider.AddKey(path, existingKey, newKey)
if err != nil {
return fmt.Errorf("failed to add new key during key update %w", err)
}
return err
}
return nil
}
func getKeys(encryptionConfig config.Encryption, partition *gpt.Partition) ([]*encryption.Key, error) {
encryptionKeys := make([]*encryption.Key, len(encryptionConfig.Keys()))
for i, cfg := range encryptionConfig.Keys() {
handler, err := keys.NewHandler(cfg)
if err != nil {
return nil, err
}
k, err := handler.GetKey(keys.WithPartitionLabel(partition.Name))
if err != nil {
return nil, err
}
encryptionKeys[i] = encryption.NewKey(cfg.Slot(), k)
}
//nolint:scopelint
sort.Slice(encryptionKeys, func(i, j int) bool { return encryptionKeys[i].Slot < encryptionKeys[j].Slot })
return encryptionKeys, nil
}

View File

@@ -0,0 +1,14 @@
// 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 encryption_test
import "testing"
func TestEmpty(t *testing.T) {
// added for accurate coverage estimation
//
// please remove it once any unit-test is added
// for this package
}

View File

@@ -0,0 +1,34 @@
// 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 contains various encryption KeyHandler implementations.
package keys
import (
"fmt"
"github.com/talos-systems/talos/pkg/machinery/config"
)
// NewHandler creates a new key handler depending on key handler kind.
func NewHandler(key config.EncryptionKey) (Handler, error) {
switch {
case key.Static() != nil:
k := key.Static().Key()
if k == nil {
return nil, fmt.Errorf("static key must have key data defined")
}
return NewStaticKeyHandler(k)
case key.NodeID() != nil:
return NewNodeIDKeyHandler()
}
return nil, fmt.Errorf("failed to create key handler: malformed config")
}
// Handler represents an interface for fetching encryption keys.
type Handler interface {
GetKey(options ...KeyOption) ([]byte, error)
}

View File

@@ -0,0 +1,57 @@
// 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 (
"fmt"
"github.com/google/uuid"
"github.com/talos-systems/go-smbios/smbios"
)
// NodeIDKeyHandler generates the key based on current node information
// and provided template string.
type NodeIDKeyHandler struct {
}
// NewNodeIDKeyHandler creates new NodeIDKeyHandler.
func NewNodeIDKeyHandler() (*NodeIDKeyHandler, error) {
return &NodeIDKeyHandler{}, nil
}
// GetKey implements KeyHandler interface.
func (h *NodeIDKeyHandler) GetKey(options ...KeyOption) ([]byte, error) {
opts, err := NewDefaultOptions(options)
if err != nil {
return nil, err
}
s, err := smbios.New()
if err != nil {
return nil, err
}
machineUUID, err := s.SystemInformation().UUID()
if err != nil {
return nil, err
}
if machineUUID == uuid.Nil {
return nil, fmt.Errorf("machine UUID is not populated %s", machineUUID)
}
id := machineUUID.String()
// primitive entropy check
counts := map[rune]int{}
for _, s := range id {
counts[s]++
if counts[s] > len(id)/2 {
return nil, fmt.Errorf("machine UUID %s entropy check failed", machineUUID)
}
}
return []byte(id + opts.PartitionLabel), nil
}

View File

@@ -0,0 +1,36 @@
// 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
// KeyOption represents key option callback used in KeyHandler.GetKey func.
type KeyOption func(o *KeyOptions) error
// KeyOptions set of options to be used in KeyHandler.GetKey func.
type KeyOptions struct {
PartitionLabel string
}
// WithPartitionLabel passes the partition label in to GetKey function.
func WithPartitionLabel(label string) KeyOption {
return func(o *KeyOptions) error {
o.PartitionLabel = label
return nil
}
}
// NewDefaultOptions creates new KeyOptions.
func NewDefaultOptions(options []KeyOption) (*KeyOptions, error) {
var opts KeyOptions
for _, o := range options {
err := o(&opts)
if err != nil {
return nil, err
}
}
return &opts, nil
}

View File

@@ -0,0 +1,22 @@
// 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
// StaticKeyHandler just handles the static key value all the time.
type StaticKeyHandler struct {
key []byte
}
// NewStaticKeyHandler creates new EphemeralKeyHandler.
func NewStaticKeyHandler(key []byte) (*StaticKeyHandler, error) {
return &StaticKeyHandler{
key: key,
}, nil
}
// GetKey implements KeyHandler interface.
func (h *StaticKeyHandler) GetKey(options ...KeyOption) ([]byte, error) {
return h.key, nil
}

View File

@@ -4,6 +4,8 @@
package mount
import "github.com/talos-systems/talos/pkg/machinery/config"
const (
// ReadOnly is a flag for setting the mount point as readonly.
ReadOnly Flags = 1 << iota
@@ -29,6 +31,7 @@ type Options struct {
MountFlags Flags
PreMountHooks []Hook
PostUnmountHooks []Hook
Encryption config.Encryption
}
// Option is the functional option func.
@@ -72,6 +75,13 @@ func WithPostUnmountHooks(hooks ...Hook) Option {
}
}
// WithEncryptionConfig partition encryption configuration.
func WithEncryptionConfig(cfg config.Encryption) Option {
return func(args *Options) {
args.Encryption = cfg
}
}
// Hook represents pre/post mount hook.
type Hook func(p *Point) error

View File

@@ -14,7 +14,9 @@ import (
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
"github.com/talos-systems/talos/internal/app/machined/pkg/runtime/disk"
"github.com/talos-systems/talos/internal/pkg/encryption"
"github.com/talos-systems/talos/internal/pkg/partition"
"github.com/talos-systems/talos/pkg/machinery/config"
"github.com/talos-systems/talos/pkg/machinery/constants"
)
@@ -89,8 +91,46 @@ func SystemMountPointForLabel(device *blockdevice.BlockDevice, label string, opt
return nil, err
}
o := NewDefaultOptions(opts...)
preMountHooks := []Hook{}
if o.Encryption != nil {
encryptionHandler, err := encryption.NewHandler(
device,
part,
o.Encryption,
)
if err != nil {
return nil, err
}
preMountHooks = append(preMountHooks,
func(p *Point) error {
var (
err error
path string
)
if path, err = encryptionHandler.Open(); err != nil {
return err
}
p.source = path
return nil
},
)
opts = append(opts,
WithPostUnmountHooks(
func(p *Point) error {
return encryptionHandler.Close()
},
),
)
}
// Format the partition if it does not have any filesystem
preMountHooks = append(preMountHooks, func(p *Point) error {
sb, err := filesystem.Probe(p.source)
@@ -132,6 +172,16 @@ func SystemPartitionMount(r runtime.Runtime, label string, opts ...Option) (err
return fmt.Errorf("failed to find device with partition labeled %s", label)
}
var encryptionConfig config.Encryption
if r.Config() != nil && r.Config().Machine() != nil {
encryptionConfig = r.Config().Machine().SystemDiskEncryption().Get(label)
}
if encryptionConfig != nil {
opts = append(opts, WithEncryptionConfig(encryptionConfig))
}
mountpoint, err := SystemMountPointForLabel(device.BlockDevice, label, opts...)
if err != nil {
return err

View File

@@ -45,6 +45,7 @@ type MachineConfig interface {
Kubelet() Kubelet
Sysctls() map[string]string
Registries() Registries
SystemDiskEncryption() SystemDiskEncryption
}
// Disk represents the options available for partitioning, formatting, and
@@ -342,3 +343,31 @@ type CoreDNS interface {
type AdminKubeconfig interface {
CertLifetime() time.Duration
}
// EncryptionKey defines settings for the partition encryption key handling.
type EncryptionKey interface {
Static() EncryptionKeyStatic
NodeID() EncryptionKeyNodeID
Slot() int
}
// EncryptionKeyStatic ephemeral encryption key.
type EncryptionKeyStatic interface {
Key() []byte
}
// EncryptionKeyNodeID deterministically generated encryption key.
type EncryptionKeyNodeID interface {
}
// Encryption defines settings for the partition encryption.
type Encryption interface {
Kind() string
Cipher() string
Keys() []EncryptionKey
}
// SystemDiskEncryption accumulates settings for all system partitions encryption.
type SystemDiskEncryption interface {
Get(label string) Encryption
}

View File

@@ -19,7 +19,7 @@ import (
"github.com/talos-systems/talos/pkg/machinery/config/types/v1alpha1/machine"
)
// NewConfigBundle returns a new bundle
// NewConfigBundle returns a new bundle.
// nolint: gocyclo
func NewConfigBundle(opts ...Option) (*v1alpha1.ConfigBundle, error) {
options := DefaultOptions()

View File

@@ -80,9 +80,10 @@ type Input struct {
NetworkConfig *v1alpha1.NetworkConfig
CNIConfig *v1alpha1.CNIConfig
RegistryMirrors map[string]*v1alpha1.RegistryMirrorConfig
RegistryConfig map[string]*v1alpha1.RegistryConfig
MachineDisks []*v1alpha1.MachineDisk
RegistryMirrors map[string]*v1alpha1.RegistryMirrorConfig
RegistryConfig map[string]*v1alpha1.RegistryConfig
MachineDisks []*v1alpha1.MachineDisk
SystemDiskEncryptionConfig *v1alpha1.SystemDiskEncryptionConfig
Debug bool
Persist bool
@@ -454,28 +455,29 @@ func NewInput(clustername, endpoint, kubernetesVersion string, secrets *SecretsB
}
input = &Input{
Certs: secrets.Certs,
ControlPlaneEndpoint: endpoint,
PodNet: []string{podNet},
ServiceNet: []string{serviceNet},
ServiceDomain: options.DNSDomain,
ClusterName: clustername,
KubernetesVersion: kubernetesVersion,
Secrets: secrets.Secrets,
TrustdInfo: secrets.TrustdInfo,
AdditionalSubjectAltNames: additionalSubjectAltNames,
AdditionalMachineCertSANs: additionalMachineCertSANs,
InstallDisk: options.InstallDisk,
InstallImage: options.InstallImage,
InstallExtraKernelArgs: options.InstallExtraKernelArgs,
NetworkConfig: options.NetworkConfig,
CNIConfig: options.CNIConfig,
RegistryMirrors: options.RegistryMirrors,
RegistryConfig: options.RegistryConfig,
Debug: options.Debug,
Persist: options.Persist,
AllowSchedulingOnMasters: options.AllowSchedulingOnMasters,
MachineDisks: options.MachineDisks,
Certs: secrets.Certs,
ControlPlaneEndpoint: endpoint,
PodNet: []string{podNet},
ServiceNet: []string{serviceNet},
ServiceDomain: options.DNSDomain,
ClusterName: clustername,
KubernetesVersion: kubernetesVersion,
Secrets: secrets.Secrets,
TrustdInfo: secrets.TrustdInfo,
AdditionalSubjectAltNames: additionalSubjectAltNames,
AdditionalMachineCertSANs: additionalMachineCertSANs,
InstallDisk: options.InstallDisk,
InstallImage: options.InstallImage,
InstallExtraKernelArgs: options.InstallExtraKernelArgs,
NetworkConfig: options.NetworkConfig,
CNIConfig: options.CNIConfig,
RegistryMirrors: options.RegistryMirrors,
RegistryConfig: options.RegistryConfig,
Debug: options.Debug,
Persist: options.Persist,
AllowSchedulingOnMasters: options.AllowSchedulingOnMasters,
MachineDisks: options.MachineDisks,
SystemDiskEncryptionConfig: options.SystemDiskEncryptionConfig,
}
return input, nil

View File

@@ -38,7 +38,8 @@ func initUd(in *Input) (*v1alpha1.Config, error) {
RegistryMirrors: in.RegistryMirrors,
RegistryConfig: in.RegistryConfig,
},
MachineDisks: in.MachineDisks,
MachineDisks: in.MachineDisks,
MachineSystemDiskEncryption: in.SystemDiskEncryptionConfig,
}
certSANs := in.GetAPIServerSANs()

View File

@@ -39,7 +39,8 @@ func workerUd(in *Input) (*v1alpha1.Config, error) {
RegistryMirrors: in.RegistryMirrors,
RegistryConfig: in.RegistryConfig,
},
MachineDisks: in.MachineDisks,
MachineDisks: in.MachineDisks,
MachineSystemDiskEncryption: in.SystemDiskEncryptionConfig,
}
controlPlaneURL, err := url.Parse(in.ControlPlaneEndpoint)

View File

@@ -163,23 +163,33 @@ func WithVersionContract(versionContract *config.VersionContract) GenOption {
}
}
// WithSystemDiskEncryption specifies encryption settings for the system disk partitions.
func WithSystemDiskEncryption(cfg *v1alpha1.SystemDiskEncryptionConfig) GenOption {
return func(o *GenOptions) error {
o.SystemDiskEncryptionConfig = cfg
return nil
}
}
// GenOptions describes generate parameters.
type GenOptions struct {
EndpointList []string
InstallDisk string
InstallImage string
InstallExtraKernelArgs []string
AdditionalSubjectAltNames []string
NetworkConfig *v1alpha1.NetworkConfig
CNIConfig *v1alpha1.CNIConfig
RegistryMirrors map[string]*v1alpha1.RegistryMirrorConfig
RegistryConfig map[string]*v1alpha1.RegistryConfig
DNSDomain string
Debug bool
Persist bool
AllowSchedulingOnMasters bool
MachineDisks []*v1alpha1.MachineDisk
VersionContract *config.VersionContract
EndpointList []string
InstallDisk string
InstallImage string
InstallExtraKernelArgs []string
AdditionalSubjectAltNames []string
NetworkConfig *v1alpha1.NetworkConfig
CNIConfig *v1alpha1.CNIConfig
RegistryMirrors map[string]*v1alpha1.RegistryMirrorConfig
RegistryConfig map[string]*v1alpha1.RegistryConfig
DNSDomain string
Debug bool
Persist bool
AllowSchedulingOnMasters bool
MachineDisks []*v1alpha1.MachineDisk
VersionContract *config.VersionContract
SystemDiskEncryptionConfig *v1alpha1.SystemDiskEncryptionConfig
}
// DefaultGenOptions returns default options.

View File

@@ -233,6 +233,15 @@ func (m *MachineConfig) Registries() config.Registries {
return &m.MachineRegistries
}
// SystemDiskEncryption implements the config.Provider interface.
func (m *MachineConfig) SystemDiskEncryption() config.SystemDiskEncryption {
if m.MachineSystemDiskEncryption == nil {
return &SystemDiskEncryptionConfig{}
}
return m.MachineSystemDiskEncryption
}
// Image implements the config.Provider interface.
func (k *KubeletConfig) Image() string {
image := k.KubeletImage
@@ -1179,3 +1188,65 @@ func (p *DiskPartition) Size() uint64 {
func (p *DiskPartition) MountPoint() string {
return p.DiskMountPoint
}
// Kind implements the config.Provider interface.
func (e *EncryptionConfig) Kind() string {
return e.EncryptionProvider
}
// Cipher implements the config.Provider interface.
func (e *EncryptionConfig) Cipher() string {
return e.EncryptionCipher
}
// Keys implements the config.Provider interface.
func (e *EncryptionConfig) Keys() []config.EncryptionKey {
keys := make([]config.EncryptionKey, len(e.EncryptionKeys))
for i, key := range e.EncryptionKeys {
keys[i] = key
}
return keys
}
// Static implements the config.Provider interface.
func (e *EncryptionKey) Static() config.EncryptionKeyStatic {
if e.KeyStatic == nil {
return nil
}
return e.KeyStatic
}
// NodeID implements the config.Provider interface.
func (e *EncryptionKey) NodeID() config.EncryptionKeyNodeID {
if e.KeyNodeID == nil {
return nil
}
return e.KeyNodeID
}
// Slot implements the config.Provider interface.
func (e *EncryptionKey) Slot() int {
return e.KeySlot
}
// Key implements the config.Provider interface.
func (e *EncryptionKeyStatic) Key() []byte {
return []byte(e.KeyData)
}
// Get implements the config.Provider interface.
func (e *SystemDiskEncryptionConfig) Get(label string) config.Encryption {
if label == constants.EphemeralPartitionLabel {
if e.EphemeralPartition == nil {
return nil
}
return e.EphemeralPartition
}
return nil
}

View File

@@ -198,6 +198,18 @@ var (
"net.ipv4.ip_forward": "0",
}
machineSystemDiskEncryptionExample = &SystemDiskEncryptionConfig{
EphemeralPartition: &EncryptionConfig{
EncryptionProvider: "luks2",
EncryptionKeys: []*EncryptionKey{
{
KeyNodeID: &EncryptionKeyNodeID{},
KeySlot: 0,
},
},
},
}
clusterConfigExample = struct {
ControlPlane *ControlPlaneConfig `yaml:"controlPlane"`
ClusterName string `yaml:"clusterName"`
@@ -533,6 +545,12 @@ type MachineConfig struct {
// examples:
// - value: machineConfigRegistriesExample
MachineRegistries RegistriesConfig `yaml:"registries,omitempty"`
// description: |
// Machine system disk encryption configuration.
// Defines each system partition encryption parameters.
// examples:
// - value: machineSystemDiskEncryptionExample
MachineSystemDiskEncryption *SystemDiskEncryptionConfig `yaml:"systemDiskEncryption,omitempty"`
}
// ClusterConfig represents the cluster-wide config values.
@@ -1051,6 +1069,45 @@ type DiskPartition struct {
DiskMountPoint string `yaml:"mountpoint,omitempty"`
}
// EncryptionConfig represents partition encryption settings.
type EncryptionConfig struct {
// description: >
// Encryption provider to use for the encryption.
// examples:
// - value: '"luks2"'
EncryptionProvider string `yaml:"provider"`
// description: >
// Defines the encryption keys generation and storage method.
EncryptionKeys []*EncryptionKey `yaml:"keys"`
// description: >
// Cipher kind to use for the encryption.
// Depends on the encryption provider.
EncryptionCipher string `yaml:"cipher,omitempty"`
}
// EncryptionKey represents configuration for disk encryption key.
type EncryptionKey struct {
// 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: >
// Key slot number for luks2 encryption.
KeySlot int `yaml:"slot"`
}
// EncryptionKeyStatic represents throw away key type.
type EncryptionKeyStatic struct {
// description: >
// Defines the static passphrase value.
KeyData string `yaml:"passphrase,omitempty"`
}
// EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.
type EncryptionKeyNodeID struct{}
// Env represents a set of environment variables.
type Env = map[string]string
@@ -1396,3 +1453,10 @@ type RegistryTLSConfig struct {
// Skip TLS server certificate verification (not recommended).
TLSInsecureSkipVerify bool `yaml:"insecureSkipVerify,omitempty"`
}
// SystemDiskEncryptionConfig specifies system disk partitions encryption settings.
type SystemDiskEncryptionConfig struct {
// description: |
// Ephemeral partition encryption.
EphemeralPartition *EncryptionConfig `yaml:"ephemeral,omitempty"`
}

View File

@@ -11,41 +11,46 @@ import (
)
var (
ConfigDoc encoder.Doc
MachineConfigDoc encoder.Doc
ClusterConfigDoc encoder.Doc
KubeletConfigDoc encoder.Doc
NetworkConfigDoc encoder.Doc
InstallConfigDoc encoder.Doc
TimeConfigDoc encoder.Doc
RegistriesConfigDoc encoder.Doc
PodCheckpointerDoc encoder.Doc
CoreDNSDoc encoder.Doc
EndpointDoc encoder.Doc
ControlPlaneConfigDoc encoder.Doc
APIServerConfigDoc encoder.Doc
ControllerManagerConfigDoc encoder.Doc
ProxyConfigDoc encoder.Doc
SchedulerConfigDoc encoder.Doc
EtcdConfigDoc encoder.Doc
ClusterNetworkConfigDoc encoder.Doc
CNIConfigDoc encoder.Doc
AdminKubeconfigConfigDoc encoder.Doc
MachineDiskDoc encoder.Doc
DiskPartitionDoc encoder.Doc
MachineFileDoc encoder.Doc
ExtraHostDoc encoder.Doc
DeviceDoc encoder.Doc
DHCPOptionsDoc encoder.Doc
DeviceWireguardConfigDoc encoder.Doc
DeviceWireguardPeerDoc encoder.Doc
BondDoc encoder.Doc
VlanDoc encoder.Doc
RouteDoc encoder.Doc
RegistryMirrorConfigDoc encoder.Doc
RegistryConfigDoc encoder.Doc
RegistryAuthConfigDoc encoder.Doc
RegistryTLSConfigDoc encoder.Doc
ConfigDoc encoder.Doc
MachineConfigDoc encoder.Doc
ClusterConfigDoc encoder.Doc
KubeletConfigDoc encoder.Doc
NetworkConfigDoc encoder.Doc
InstallConfigDoc encoder.Doc
TimeConfigDoc encoder.Doc
RegistriesConfigDoc encoder.Doc
PodCheckpointerDoc encoder.Doc
CoreDNSDoc encoder.Doc
EndpointDoc encoder.Doc
ControlPlaneConfigDoc encoder.Doc
APIServerConfigDoc encoder.Doc
ControllerManagerConfigDoc encoder.Doc
ProxyConfigDoc encoder.Doc
SchedulerConfigDoc encoder.Doc
EtcdConfigDoc encoder.Doc
ClusterNetworkConfigDoc encoder.Doc
CNIConfigDoc encoder.Doc
AdminKubeconfigConfigDoc encoder.Doc
MachineDiskDoc encoder.Doc
DiskPartitionDoc encoder.Doc
EncryptionConfigDoc encoder.Doc
EncryptionKeyDoc encoder.Doc
EncryptionKeyStaticDoc encoder.Doc
EncryptionKeyNodeIDDoc encoder.Doc
MachineFileDoc encoder.Doc
ExtraHostDoc encoder.Doc
DeviceDoc encoder.Doc
DHCPOptionsDoc encoder.Doc
DeviceWireguardConfigDoc encoder.Doc
DeviceWireguardPeerDoc encoder.Doc
BondDoc encoder.Doc
VlanDoc encoder.Doc
RouteDoc encoder.Doc
RegistryMirrorConfigDoc encoder.Doc
RegistryConfigDoc encoder.Doc
RegistryAuthConfigDoc encoder.Doc
RegistryTLSConfigDoc encoder.Doc
SystemDiskEncryptionConfigDoc encoder.Doc
)
func init() {
@@ -107,7 +112,7 @@ func init() {
FieldName: "machine",
},
}
MachineConfigDoc.Fields = make([]encoder.Doc, 13)
MachineConfigDoc.Fields = make([]encoder.Doc, 14)
MachineConfigDoc.Fields[0].Name = "type"
MachineConfigDoc.Fields[0].Type = "string"
MachineConfigDoc.Fields[0].Note = ""
@@ -213,6 +218,13 @@ func init() {
MachineConfigDoc.Fields[12].Comments[encoder.LineComment] = "Used to configure the machine's container image registry mirrors."
MachineConfigDoc.Fields[12].AddExample("", machineConfigRegistriesExample)
MachineConfigDoc.Fields[13].Name = "systemDiskEncryption"
MachineConfigDoc.Fields[13].Type = "SystemDiskEncryptionConfig"
MachineConfigDoc.Fields[13].Note = ""
MachineConfigDoc.Fields[13].Description = "Machine system disk encryption configuration.\nDefines each system partition encryption parameters."
MachineConfigDoc.Fields[13].Comments[encoder.LineComment] = "Machine system disk encryption configuration."
MachineConfigDoc.Fields[13].AddExample("", machineSystemDiskEncryptionExample)
ClusterConfigDoc.Type = "ClusterConfig"
ClusterConfigDoc.Comments[encoder.LineComment] = "ClusterConfig represents the cluster-wide config values."
@@ -912,6 +924,87 @@ func init() {
DiskPartitionDoc.Fields[1].Description = "Where to mount the partition."
DiskPartitionDoc.Fields[1].Comments[encoder.LineComment] = "Where to mount the partition."
EncryptionConfigDoc.Type = "EncryptionConfig"
EncryptionConfigDoc.Comments[encoder.LineComment] = "EncryptionConfig represents partition encryption settings."
EncryptionConfigDoc.Description = "EncryptionConfig represents partition encryption settings."
EncryptionConfigDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "SystemDiskEncryptionConfig",
FieldName: "ephemeral",
},
}
EncryptionConfigDoc.Fields = make([]encoder.Doc, 3)
EncryptionConfigDoc.Fields[0].Name = "provider"
EncryptionConfigDoc.Fields[0].Type = "string"
EncryptionConfigDoc.Fields[0].Note = ""
EncryptionConfigDoc.Fields[0].Description = "Encryption provider to use for the encryption."
EncryptionConfigDoc.Fields[0].Comments[encoder.LineComment] = "Encryption provider to use for the encryption."
EncryptionConfigDoc.Fields[0].AddExample("", "luks2")
EncryptionConfigDoc.Fields[1].Name = "keys"
EncryptionConfigDoc.Fields[1].Type = "[]EncryptionKey"
EncryptionConfigDoc.Fields[1].Note = ""
EncryptionConfigDoc.Fields[1].Description = "Defines the encryption keys generation and storage method."
EncryptionConfigDoc.Fields[1].Comments[encoder.LineComment] = "Defines the encryption keys generation and storage method."
EncryptionConfigDoc.Fields[2].Name = "cipher"
EncryptionConfigDoc.Fields[2].Type = "string"
EncryptionConfigDoc.Fields[2].Note = ""
EncryptionConfigDoc.Fields[2].Description = "Cipher kind to use for the encryption. Depends on the encryption provider."
EncryptionConfigDoc.Fields[2].Comments[encoder.LineComment] = "Cipher kind to use for the encryption. Depends on the encryption provider."
EncryptionKeyDoc.Type = "EncryptionKey"
EncryptionKeyDoc.Comments[encoder.LineComment] = "EncryptionKey represents configuration for disk encryption key."
EncryptionKeyDoc.Description = "EncryptionKey represents configuration for disk encryption key."
EncryptionKeyDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "EncryptionConfig",
FieldName: "keys",
},
}
EncryptionKeyDoc.Fields = make([]encoder.Doc, 3)
EncryptionKeyDoc.Fields[0].Name = "static"
EncryptionKeyDoc.Fields[0].Type = "EncryptionKeyStatic"
EncryptionKeyDoc.Fields[0].Note = ""
EncryptionKeyDoc.Fields[0].Description = "Key which value is stored in the configuration file."
EncryptionKeyDoc.Fields[0].Comments[encoder.LineComment] = "Key which value is stored in the configuration file."
EncryptionKeyDoc.Fields[1].Name = "nodeID"
EncryptionKeyDoc.Fields[1].Type = "EncryptionKeyNodeID"
EncryptionKeyDoc.Fields[1].Note = ""
EncryptionKeyDoc.Fields[1].Description = "Deterministically generated key from the node UUID and PartitionLabel."
EncryptionKeyDoc.Fields[1].Comments[encoder.LineComment] = "Deterministically generated key from the node UUID and PartitionLabel."
EncryptionKeyDoc.Fields[2].Name = "slot"
EncryptionKeyDoc.Fields[2].Type = "int"
EncryptionKeyDoc.Fields[2].Note = ""
EncryptionKeyDoc.Fields[2].Description = "Key slot number for luks2 encryption."
EncryptionKeyDoc.Fields[2].Comments[encoder.LineComment] = "Key slot number for luks2 encryption."
EncryptionKeyStaticDoc.Type = "EncryptionKeyStatic"
EncryptionKeyStaticDoc.Comments[encoder.LineComment] = "EncryptionKeyStatic represents throw away key type."
EncryptionKeyStaticDoc.Description = "EncryptionKeyStatic represents throw away key type."
EncryptionKeyStaticDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "EncryptionKey",
FieldName: "static",
},
}
EncryptionKeyStaticDoc.Fields = make([]encoder.Doc, 1)
EncryptionKeyStaticDoc.Fields[0].Name = "passphrase"
EncryptionKeyStaticDoc.Fields[0].Type = "string"
EncryptionKeyStaticDoc.Fields[0].Note = ""
EncryptionKeyStaticDoc.Fields[0].Description = "Defines the static passphrase value."
EncryptionKeyStaticDoc.Fields[0].Comments[encoder.LineComment] = "Defines the static passphrase value."
EncryptionKeyNodeIDDoc.Type = "EncryptionKeyNodeID"
EncryptionKeyNodeIDDoc.Comments[encoder.LineComment] = "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel."
EncryptionKeyNodeIDDoc.Description = "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel."
EncryptionKeyNodeIDDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "EncryptionKey",
FieldName: "nodeID",
},
}
EncryptionKeyNodeIDDoc.Fields = make([]encoder.Doc, 0)
MachineFileDoc.Type = "MachineFile"
MachineFileDoc.Comments[encoder.LineComment] = "MachineFile represents a file to write to disk."
MachineFileDoc.Description = "MachineFile represents a file to write to disk."
@@ -1473,6 +1566,24 @@ func init() {
RegistryTLSConfigDoc.Fields[2].Note = ""
RegistryTLSConfigDoc.Fields[2].Description = "Skip TLS server certificate verification (not recommended)."
RegistryTLSConfigDoc.Fields[2].Comments[encoder.LineComment] = "Skip TLS server certificate verification (not recommended)."
SystemDiskEncryptionConfigDoc.Type = "SystemDiskEncryptionConfig"
SystemDiskEncryptionConfigDoc.Comments[encoder.LineComment] = "SystemDiskEncryptionConfig specifies system disk partitions encryption settings."
SystemDiskEncryptionConfigDoc.Description = "SystemDiskEncryptionConfig specifies system disk partitions encryption settings."
SystemDiskEncryptionConfigDoc.AddExample("", machineSystemDiskEncryptionExample)
SystemDiskEncryptionConfigDoc.AppearsIn = []encoder.Appearance{
{
TypeName: "MachineConfig",
FieldName: "systemDiskEncryption",
},
}
SystemDiskEncryptionConfigDoc.Fields = make([]encoder.Doc, 1)
SystemDiskEncryptionConfigDoc.Fields[0].Name = "ephemeral"
SystemDiskEncryptionConfigDoc.Fields[0].Type = "EncryptionConfig"
SystemDiskEncryptionConfigDoc.Fields[0].Note = ""
SystemDiskEncryptionConfigDoc.Fields[0].Description = "Ephemeral partition encryption."
SystemDiskEncryptionConfigDoc.Fields[0].Comments[encoder.LineComment] = "Ephemeral partition encryption."
}
func (_ Config) Doc() *encoder.Doc {
@@ -1563,6 +1674,22 @@ func (_ DiskPartition) Doc() *encoder.Doc {
return &DiskPartitionDoc
}
func (_ EncryptionConfig) Doc() *encoder.Doc {
return &EncryptionConfigDoc
}
func (_ EncryptionKey) Doc() *encoder.Doc {
return &EncryptionKeyDoc
}
func (_ EncryptionKeyStatic) Doc() *encoder.Doc {
return &EncryptionKeyStaticDoc
}
func (_ EncryptionKeyNodeID) Doc() *encoder.Doc {
return &EncryptionKeyNodeIDDoc
}
func (_ MachineFile) Doc() *encoder.Doc {
return &MachineFileDoc
}
@@ -1615,6 +1742,10 @@ func (_ RegistryTLSConfig) Doc() *encoder.Doc {
return &RegistryTLSConfigDoc
}
func (_ SystemDiskEncryptionConfig) Doc() *encoder.Doc {
return &SystemDiskEncryptionConfigDoc
}
// GetConfigurationDoc returns documentation for the file ./v1alpha1_types_doc.go.
func GetConfigurationDoc() *encoder.FileDoc {
return &encoder.FileDoc{
@@ -1643,6 +1774,10 @@ func GetConfigurationDoc() *encoder.FileDoc {
&AdminKubeconfigConfigDoc,
&MachineDiskDoc,
&DiskPartitionDoc,
&EncryptionConfigDoc,
&EncryptionKeyDoc,
&EncryptionKeyStaticDoc,
&EncryptionKeyNodeIDDoc,
&MachineFileDoc,
&ExtraHostDoc,
&DeviceDoc,
@@ -1656,6 +1791,7 @@ func GetConfigurationDoc() *encoder.FileDoc {
&RegistryConfigDoc,
&RegistryAuthConfigDoc,
&RegistryTLSConfigDoc,
&SystemDiskEncryptionConfigDoc,
},
}
}

View File

@@ -119,6 +119,28 @@ func (c *Config) Validate(mode config.RuntimeMode) error {
result = multierror.Append(result, fmt.Errorf("%q is not a valid DNS name", c.ClusterConfig.ClusterNetwork.DNSDomain))
}
for _, label := range []string{constants.EphemeralPartitionLabel} {
encryptionConfig := c.MachineConfig.SystemDiskEncryption().Get(label)
if encryptionConfig != nil {
if len(encryptionConfig.Keys()) == 0 {
result = multierror.Append(result, fmt.Errorf("no encryption keys provided for the ephemeral partition encryption"))
}
slotsInUse := map[int]bool{}
for _, key := range encryptionConfig.Keys() {
if slotsInUse[key.Slot()] {
result = multierror.Append(result, fmt.Errorf("encryption key slot %d is already in use", key.Slot()))
}
slotsInUse[key.Slot()] = true
if key.NodeID() == nil && key.Static() == nil {
result = multierror.Append(result, fmt.Errorf("encryption key at slot %d doesn't have any settings", key.Slot()))
}
}
}
}
return result.ErrorOrNil()
}

View File

@@ -88,6 +88,7 @@ talosctl cluster create [flags]
--disk-image-path string disk image to use
--dns-domain string the dns domain to use for cluster (default "cluster.local")
--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
--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)
-h, --help help for create

View File

@@ -658,6 +658,38 @@ registries:
<hr />
<div class="dd">
<code>systemDiskEncryption</code> <i><a href="#systemdiskencryptionconfig">SystemDiskEncryptionConfig</a></i>
</div>
<div class="dt">
Machine system disk encryption configuration.
Defines each system partition encryption parameters.
Examples:
``` yaml
systemDiskEncryption:
# Ephemeral partition encryption.
ephemeral:
provider: luks2 # Encryption provider to use for the encryption.
# Defines the encryption keys generation and storage method.
keys:
- # Deterministically generated key from the node UUID and PartitionLabel.
nodeID: {}
slot: 0 # Key slot number for luks2 encryption.
```
</div>
<hr />
@@ -2693,6 +2725,167 @@ Where to mount the partition.
## EncryptionConfig
EncryptionConfig represents partition encryption settings.
Appears in:
- <code><a href="#systemdiskencryptionconfig">SystemDiskEncryptionConfig</a>.ephemeral</code>
<hr />
<div class="dd">
<code>provider</code> <i>string</i>
</div>
<div class="dt">
Encryption provider to use for the encryption.
Examples:
``` yaml
provider: luks2
```
</div>
<hr />
<div class="dd">
<code>keys</code> <i>[]<a href="#encryptionkey">EncryptionKey</a></i>
</div>
<div class="dt">
Defines the encryption keys generation and storage method.
</div>
<hr />
<div class="dd">
<code>cipher</code> <i>string</i>
</div>
<div class="dt">
Cipher kind to use for the encryption. Depends on the encryption provider.
</div>
<hr />
## EncryptionKey
EncryptionKey represents configuration for disk encryption key.
Appears in:
- <code><a href="#encryptionconfig">EncryptionConfig</a>.keys</code>
<hr />
<div class="dd">
<code>static</code> <i><a href="#encryptionkeystatic">EncryptionKeyStatic</a></i>
</div>
<div class="dt">
Key which value is stored in the configuration file.
</div>
<hr />
<div class="dd">
<code>nodeID</code> <i><a href="#encryptionkeynodeid">EncryptionKeyNodeID</a></i>
</div>
<div class="dt">
Deterministically generated key from the node UUID and PartitionLabel.
</div>
<hr />
<div class="dd">
<code>slot</code> <i>int</i>
</div>
<div class="dt">
Key slot number for luks2 encryption.
</div>
<hr />
## EncryptionKeyStatic
EncryptionKeyStatic represents throw away key type.
Appears in:
- <code><a href="#encryptionkey">EncryptionKey</a>.static</code>
<hr />
<div class="dd">
<code>passphrase</code> <i>string</i>
</div>
<div class="dt">
Defines the static passphrase value.
</div>
<hr />
## EncryptionKeyNodeID
EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.
Appears in:
- <code><a href="#encryptionkey">EncryptionKey</a>.nodeID</code>
## MachineFile
MachineFile represents a file to write to disk.
@@ -4199,3 +4392,42 @@ Skip TLS server certificate verification (not recommended).
## SystemDiskEncryptionConfig
SystemDiskEncryptionConfig specifies system disk partitions encryption settings.
Appears in:
- <code><a href="#machineconfig">MachineConfig</a>.systemDiskEncryption</code>
``` yaml
# Ephemeral partition encryption.
ephemeral:
provider: luks2 # Encryption provider to use for the encryption.
# Defines the encryption keys generation and storage method.
keys:
- # Deterministically generated key from the node UUID and PartitionLabel.
nodeID: {}
slot: 0 # Key slot number for luks2 encryption.
```
<hr />
<div class="dd">
<code>ephemeral</code> <i><a href="#encryptionconfig">EncryptionConfig</a></i>
</div>
<div class="dt">
Ephemeral partition encryption.
</div>
<hr />