Skip to content

Commit

Permalink
feat: add KMS assisted encryption key handler
Browse files Browse the repository at this point in the history
Talos now supports new type of encryption keys which rely on Sealing/Unsealing randomly generated bytes with a KMS server:

```
systemDiskEncryption:
  ephemeral:
    keys:
      - kms:
          endpoint: https://1.2.3.4:443
        slot: 0
```
gRPC API definitions and a simple reference implementation of the KMS server can be found in this
[repository](https://github.com/siderolabs/kms-client/blob/main/cmd/kms-server/main.go).

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
  • Loading branch information
Unix4ever committed Jul 7, 2023
1 parent dafbe9d commit ce63abb
Show file tree
Hide file tree
Showing 62 changed files with 1,220 additions and 362 deletions.
Binary file modified api/api.descriptors
Binary file not shown.
1 change: 1 addition & 0 deletions api/resource/definitions/runtime/runtime.proto
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ message MountStatusSpec {
string target = 2;
string filesystem_type = 3;
repeated string options = 4;
bool encrypted = 5;
}

// PlatformMetadataSpec describes platform metadata properties.
Expand Down
6 changes: 3 additions & 3 deletions cmd/installer/cmd/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var imageCmd = &cobra.Command{
Short: "",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := runImageCmd(); err != nil {
if err := runImageCmd(cmd.Context()); err != nil {
log.Fatal(err)
}
},
Expand All @@ -49,7 +49,7 @@ func init() {
}

//nolint:gocyclo
func runImageCmd() (err error) {
func runImageCmd(ctx context.Context) (err error) {
p, err := platform.NewPlatform(options.Platform)
if err != nil {
return err
Expand Down Expand Up @@ -90,7 +90,7 @@ func runImageCmd() (err error) {
}
}

if err = install.Install(p, runtime.SequenceNoop, options); err != nil {
if err = install.Install(ctx, p, runtime.SequenceNoop, options); err != nil {
return err
}

Expand Down
7 changes: 4 additions & 3 deletions cmd/installer/cmd/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package cmd

import (
"context"
"errors"
"fmt"
"log"
Expand All @@ -24,15 +25,15 @@ var installCmd = &cobra.Command{
Short: "",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) (err error) {
return runInstallCmd()
return runInstallCmd(cmd.Context())
},
}

func init() {
rootCmd.AddCommand(installCmd)
}

func runInstallCmd() (err error) {
func runInstallCmd(ctx context.Context) (err error) {
log.Printf("running Talos installer %s", version.NewVersion().Tag)

seq := runtime.SequenceInstall
Expand Down Expand Up @@ -74,5 +75,5 @@ func runInstallCmd() (err error) {
}
}

return install.Install(p, seq, options)
return install.Install(ctx, p, seq, options)
}
14 changes: 7 additions & 7 deletions cmd/installer/pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Options struct {
}

// Install installs Talos.
func Install(p runtime.Platform, seq runtime.Sequence, opts *Options) (err error) {
func Install(ctx context.Context, p runtime.Platform, seq runtime.Sequence, opts *Options) (err error) {
cmdline := procfs.NewCmdline("")
cmdline.Append(constants.KernelParamPlatform, p.Name())

Expand All @@ -63,12 +63,12 @@ func Install(p runtime.Platform, seq runtime.Sequence, opts *Options) (err error
return err
}

i, err := NewInstaller(cmdline, seq, opts)
i, err := NewInstaller(ctx, cmdline, seq, opts)
if err != nil {
return err
}

if err = i.Install(seq); err != nil {
if err = i.Install(ctx, seq); err != nil {
return err
}

Expand All @@ -87,14 +87,14 @@ type Installer struct {
}

// NewInstaller initializes and returns an Installer.
func NewInstaller(cmdline *procfs.Cmdline, seq runtime.Sequence, opts *Options) (i *Installer, err error) {
func NewInstaller(ctx context.Context, cmdline *procfs.Cmdline, seq runtime.Sequence, opts *Options) (i *Installer, err error) {
i = &Installer{
cmdline: cmdline,
options: opts,
}

if !i.options.Zero {
i.bootloader, err = bootloader.Probe(i.options.Disk)
i.bootloader, err = bootloader.Probe(ctx, i.options.Disk)
if err != nil && !os.IsNotExist(err) {
return nil, fmt.Errorf("failed to probe bootloader: %w", err)
}
Expand All @@ -121,7 +121,7 @@ func NewInstaller(cmdline *procfs.Cmdline, seq runtime.Sequence, opts *Options)
// to the target locations.
//
//nolint:gocyclo,cyclop
func (i *Installer) Install(seq runtime.Sequence) (err error) {
func (i *Installer) Install(ctx context.Context, seq runtime.Sequence) (err error) {
errataBTF()

if seq == runtime.SequenceUpgrade {
Expand Down Expand Up @@ -196,7 +196,7 @@ func (i *Installer) Install(seq runtime.Sequence) (err error) {

var mountpoint *mount.Point

mountpoint, err = mount.SystemMountPointForLabel(bd, label)
mountpoint, err = mount.SystemMountPointForLabel(ctx, bd, label)
if err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/installer/pkg/install/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package install

import (
"bufio"
"context"
"errors"
"fmt"
"log"
Expand Down Expand Up @@ -496,11 +497,11 @@ func (m *Manifest) restoreContents(targets []*Target) error {
}

// SystemMountpoints returns list of system mountpoints for the manifest.
func (m *Manifest) SystemMountpoints(opts ...mount.Option) (*mount.Points, error) {
func (m *Manifest) SystemMountpoints(ctx context.Context, opts ...mount.Option) (*mount.Points, error) {
mountpoints := mount.NewMountPoints()

for dev := range m.Targets {
mp, err := mount.SystemMountPointsForDevice(dev, opts...)
mp, err := mount.SystemMountPointsForDevice(ctx, dev, opts...)
if err != nil {
return nil, err
}
Expand Down
8 changes: 6 additions & 2 deletions cmd/installer/pkg/install/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package install_test

import (
"context"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -98,6 +99,9 @@ func (suite *manifestSuite) skipIfNotRoot() {
}

func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, current, next string, verifyConfigPersistence, verifyEphemeralPersistence bool) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

bd, err := blockdevice.Open(suite.loopbackDevice.Name())
suite.Require().NoError(err)

Expand Down Expand Up @@ -151,7 +155,7 @@ func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, curren

// query mount points directly for the device

mountpoints, err := mount.SystemMountPointsForDevice(suite.loopbackDevice.Name())
mountpoints, err := mount.SystemMountPointsForDevice(ctx, suite.loopbackDevice.Name())
suite.Require().NoError(err)

suite.Assert().Equal(4, mountpoints.Len())
Expand All @@ -160,7 +164,7 @@ func (suite *manifestSuite) verifyBlockdevice(manifest *install.Manifest, curren

tempDir := suite.T().TempDir()

mountpoints, err = manifest.SystemMountpoints()
mountpoints, err = manifest.SystemMountpoints(ctx)
suite.Require().NoError(err)

suite.Assert().Equal(4, mountpoints.Len())
Expand Down
54 changes: 42 additions & 12 deletions cmd/talosctl/cmd/mgmt/cluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const (
apiServerBalancerPortFlag = "api-server-balancer-port"
tpm2EnabledFlag = "with-tpm2"
secureBootEnabledFlag = "with-secureboot"
diskEncryptionKeyTypesFlag = "disk-encryption-key-types"
)

var (
Expand Down Expand Up @@ -161,6 +162,7 @@ var (
packetReorder float64
packetCorrupt float64
bandwidth int
diskEncryptionKeyTypes []string
)

// createCmd represents the cluster up command.
Expand Down Expand Up @@ -431,27 +433,54 @@ func create(ctx context.Context, flags *pflag.FlagSet) (err error) {
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
}

port := 4050

keys = append(keys, &v1alpha1.EncryptionKey{
KeyKMS: &v1alpha1.EncryptionKeyKMS{
KMSEndpoint: "http://" + nethelpers.JoinHostPort(ip.String(), port),
},
KeySlot: i,
})

provisionOptions = append(provisionOptions, provision.WithKMS(nethelpers.JoinHostPort("0.0.0.0", port)))
default:
return fmt.Errorf("unknown key type %q", key)
}
}

if len(keys) == 0 {
return fmt.Errorf("no disk encryption key types enabled")
}

if encryptStatePartition {
diskEncryptionConfig.StatePartition = &v1alpha1.EncryptionConfig{
EncryptionProvider: encryption.LUKS2,
EncryptionKeys: []*v1alpha1.EncryptionKey{
{
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
KeySlot: 0,
},
},
EncryptionKeys: keys,
}
}

if encryptEphemeralPartition {
diskEncryptionConfig.EphemeralPartition = &v1alpha1.EncryptionConfig{
EncryptionProvider: encryption.LUKS2,
EncryptionKeys: []*v1alpha1.EncryptionKey{
{
KeyNodeID: &v1alpha1.EncryptionKeyNodeID{},
KeySlot: 0,
},
},
EncryptionKeys: keys,
}
}

Expand Down Expand Up @@ -958,6 +987,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().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")
createCmd.Flags().BoolVar(&enableClusterDiscovery, withClusterDiscoveryFlag, true, "enable cluster discovery")
Expand Down
86 changes: 86 additions & 0 deletions cmd/talosctl/cmd/mgmt/kms_launch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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 mgmt

import (
"context"
"errors"
"fmt"
"log"
"net"

"github.com/siderolabs/kms-client/api/kms"
"github.com/siderolabs/kms-client/pkg/server"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"

grpclog "github.com/siderolabs/talos/pkg/grpc/middleware/log"
)

var kmsLaunchCmdFlags struct {
addr string
key []byte
}

// kmsLaunchCmd represents the kms-launch command.
var kmsLaunchCmd = &cobra.Command{
Use: "kms-launch",
Short: "Internal command used by QEMU provisioner",
Long: ``,
Args: cobra.NoArgs,
Hidden: true,
RunE: func(cmd *cobra.Command, args []string) error {
if kmsLaunchCmdFlags.key == nil {
return fmt.Errorf("no key provided to the KMS server")
}

srv := server.NewServer(func(_ context.Context, nodeUUID string) ([]byte, error) {
return kmsLaunchCmdFlags.key, nil
})

lis, err := net.Listen("tcp", kmsLaunchCmdFlags.addr)
if err != nil {
return err
}

log.Printf("starting KMS server on %s", kmsLaunchCmdFlags.addr)

logMiddleware := grpclog.NewMiddleware(log.New(log.Writer(), "", log.Flags()))

s := grpc.NewServer(
grpc.UnaryInterceptor(logMiddleware.UnaryInterceptor()),
grpc.StreamInterceptor(logMiddleware.StreamInterceptor()),
)
kms.RegisterKMSServiceServer(s, srv)

eg, ctx := errgroup.WithContext(cmd.Context())

eg.Go(func() error {
err := s.Serve(lis)
if errors.Is(err, context.Canceled) {
return nil
}

return err
})

eg.Go(func() error {
<-ctx.Done()

s.Stop()

return nil
})

return s.Serve(lis)
},
}

func init() {
kmsLaunchCmd.Flags().StringVar(&kmsLaunchCmdFlags.addr, "kms-addr", "localhost", "KMS listen address (IP or host)")
kmsLaunchCmd.Flags().BytesBase64Var(&kmsLaunchCmdFlags.key, "kms-key", nil, "KMS key to use")
addCommand(kmsLaunchCmd)
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ require (
github.com/siderolabs/discovery-api v0.1.3
github.com/siderolabs/discovery-client v0.1.5
github.com/siderolabs/gen v0.4.5
github.com/siderolabs/go-blockdevice v0.4.5
github.com/siderolabs/go-blockdevice v0.4.6
github.com/siderolabs/go-circular v0.1.0
github.com/siderolabs/go-cmd v0.1.1
github.com/siderolabs/go-debug v0.2.2
Expand All @@ -107,6 +107,7 @@ require (
github.com/siderolabs/go-smbios v0.3.2
github.com/siderolabs/go-tail v0.1.0
github.com/siderolabs/grpc-proxy v0.4.0
github.com/siderolabs/kms-client v0.1.0
github.com/siderolabs/net v0.4.0
github.com/siderolabs/siderolink v0.3.1
github.com/siderolabs/talos/pkg/machinery v1.5.0-alpha.1
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1153,8 +1153,8 @@ github.com/siderolabs/gen v0.4.5 h1:rwXUVJlL7hYza1LrSVXfT905ZC9Rgei37jMKKs/+eP0=
github.com/siderolabs/gen v0.4.5/go.mod h1:wS8tFq7sn5vqKAuyS30vJUig3tX5v6q79VG4KfUnILM=
github.com/siderolabs/go-api-signature v0.2.4 h1:s+K0GtaPHql4LdZzL72QvwPMzffY+KB0mszORDK+5/w=
github.com/siderolabs/go-api-signature v0.2.4/go.mod h1:rLIKzbDhKewI3MdztyntS1apH6EC8ccU2TF5S0/O2yg=
github.com/siderolabs/go-blockdevice v0.4.5 h1:NgpR9XTl/N7WeL59QHBsseDD0Nb8Y2nel+W3u7xHIvY=
github.com/siderolabs/go-blockdevice v0.4.5/go.mod h1:4PeOuk71pReJj1JQEXDE7kIIQJPVe8a+HZQa+qjxSEA=
github.com/siderolabs/go-blockdevice v0.4.6 h1:yfxFYzXezzszB0mSF2ZG8jPPampoNXa9r8W8nM0IoZI=
github.com/siderolabs/go-blockdevice v0.4.6/go.mod h1:4PeOuk71pReJj1JQEXDE7kIIQJPVe8a+HZQa+qjxSEA=
github.com/siderolabs/go-circular v0.1.0 h1:zpBJNUbCZSh0odZxA4Dcj0d3ShLLR2WxKW6hTdAtoiE=
github.com/siderolabs/go-circular v0.1.0/go.mod h1:14XnLf/I3J0VjzTgmwWNGjp58/bdIi4zXppAEx8plfw=
github.com/siderolabs/go-cmd v0.1.1 h1:nTouZUSxLeiiEe7hFexSVvaTsY/3O8k1s08BxPRrsps=
Expand Down Expand Up @@ -1183,6 +1183,8 @@ github.com/siderolabs/go-tail v0.1.0 h1:U+ZClt7BXLGsxDNU/XQ12sz7lQElfFZBYEPdkW78
github.com/siderolabs/go-tail v0.1.0/go.mod h1:vWxumnRUS3eTZczORCJW3QMjxiTETN31vyuFdaW8rPw=
github.com/siderolabs/grpc-proxy v0.4.0 h1:zYrhqLYs8JlYoLHYeel7/XwXDZ4OJ5XyP9wX7JlbPew=
github.com/siderolabs/grpc-proxy v0.4.0/go.mod h1:QDurYOwQD4H8BKyvCuUxMiuG/etYnb/++xaQB644NdU=
github.com/siderolabs/kms-client v0.1.0 h1:rCDWzcDDsNlp6zdyLngOuuhchVILn+vwUQy3tk6rQps=
github.com/siderolabs/kms-client v0.1.0/go.mod h1:4UQkRhuEh3kaK7VhJxez4YyJLv6lPEff7g3Pa6Y9okg=
github.com/siderolabs/net v0.4.0 h1:1bOgVay/ijPkJz4qct98nHsiB/ysLQU0KLoBC4qLm7I=
github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM=
github.com/siderolabs/protoenc v0.2.0 h1:QFxWIAo//12+/bm27GNYoK/TpQGTYsRrrZCu9jSghvU=
Expand Down
Loading

0 comments on commit ce63abb

Please sign in to comment.