add cryptsetup wrapper to core

Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
Malte Poll 2022-04-20 11:38:56 +02:00 committed by Malte Poll
parent bb56b46e21
commit 1b6ecf27ee
13 changed files with 215 additions and 18 deletions

View File

@ -17,6 +17,7 @@ import (
gcpcloud "github.com/edgelesssys/constellation/coordinator/cloudprovider/gcp"
"github.com/edgelesssys/constellation/coordinator/config"
"github.com/edgelesssys/constellation/coordinator/core"
"github.com/edgelesssys/constellation/coordinator/diskencryption"
"github.com/edgelesssys/constellation/coordinator/kubernetes"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/kubectl"
@ -41,6 +42,7 @@ func main() {
var cloudControllerManager core.CloudControllerManager
var cloudNodeManager core.CloudNodeManager
var autoscaler core.ClusterAutoscaler
var encryptedDisk core.EncryptedDisk
cfg := zap.NewDevelopmentConfig()
logLevelUser := flag.Bool("debug", false, "enables gRPC debug output")
@ -99,6 +101,7 @@ func main() {
cloudControllerManager = &gcpcloud.CloudControllerManager{}
cloudNodeManager = &gcpcloud.CloudNodeManager{}
autoscaler = &gcpcloud.Autoscaler{}
encryptedDisk = diskencryption.New()
bindIP = defaultIP
bindPort = defaultPort
etcdEndpoint = defaultEtcdEndpoint
@ -122,6 +125,7 @@ func main() {
cloudControllerManager = &azurecloud.CloudControllerManager{}
cloudNodeManager = &azurecloud.CloudNodeManager{}
autoscaler = &azurecloud.Autoscaler{}
encryptedDisk = diskencryption.New()
bindIP = defaultIP
bindPort = defaultPort
etcdEndpoint = defaultEtcdEndpoint
@ -136,6 +140,7 @@ func main() {
cloudControllerManager = &core.CloudControllerManagerFake{}
cloudNodeManager = &core.CloudNodeManagerFake{}
autoscaler = &core.ClusterAutoscalerFake{}
encryptedDisk = &core.EncryptedDiskFake{}
bindIP = defaultIP
bindPort = defaultPort
etcdEndpoint = "etcd-storage:2379"
@ -149,5 +154,5 @@ func main() {
fileHandler := file.NewHandler(fs)
dialer := &net.Dialer{}
run(validator, issuer, wg, openTPM, util.GetIPAddr, dialer, fileHandler, kube,
metadata, cloudControllerManager, cloudNodeManager, autoscaler, etcdEndpoint, enforceEtcdTls, bindIP, bindPort, zapLoggerCore)
metadata, cloudControllerManager, cloudNodeManager, autoscaler, encryptedDisk, etcdEndpoint, enforceEtcdTls, bindIP, bindPort, zapLoggerCore)
}

View File

@ -27,7 +27,7 @@ import (
var version = "0.0.0"
func run(validator core.QuoteValidator, issuer core.QuoteIssuer, vpn core.VPN, openTPM vtpm.TPMOpenFunc, getPublicIPAddr func() (string, error), dialer pubapi.Dialer, fileHandler file.Handler,
kube core.Cluster, metadata core.ProviderMetadata, cloudControllerManager core.CloudControllerManager, cloudNodeManager core.CloudNodeManager, clusterAutoscaler core.ClusterAutoscaler, etcdEndpoint string, etcdTLS bool, bindIP, bindPort string, zapLoggerCore *zap.Logger,
kube core.Cluster, metadata core.ProviderMetadata, cloudControllerManager core.CloudControllerManager, cloudNodeManager core.CloudNodeManager, clusterAutoscaler core.ClusterAutoscaler, encryptedDisk core.EncryptedDisk, etcdEndpoint string, etcdTLS bool, bindIP, bindPort string, zapLoggerCore *zap.Logger,
) {
defer zapLoggerCore.Sync()
zapLoggerCore.Info("starting coordinator", zap.String("version", version))
@ -42,7 +42,7 @@ func run(validator core.QuoteValidator, issuer core.QuoteIssuer, vpn core.VPN, o
ForceTLS: etcdTLS,
Logger: zapLoggerCore.WithOptions(zap.IncreaseLevel(zap.WarnLevel)).Named("etcd"),
}
core, err := core.NewCore(vpn, kube, metadata, cloudControllerManager, cloudNodeManager, clusterAutoscaler, zapLoggerCore, openTPM, etcdStoreFactory, fileHandler)
core, err := core.NewCore(vpn, kube, metadata, cloudControllerManager, cloudNodeManager, clusterAutoscaler, encryptedDisk, zapLoggerCore, openTPM, etcdStoreFactory, fileHandler)
if err != nil {
zapLoggerCore.Fatal("failed to create core", zap.Error(err))
}

View File

@ -194,7 +194,7 @@ func TestConcurrent(t *testing.T) {
func spawnPeer(require *require.Assertions, logger *zap.Logger, dialer *testdialer.BufconnDialer, netw *network, endpoint string) (*grpc.Server, *pubapi.API, *fakeVPN) {
vpn := newVPN(netw, endpoint)
cor, err := core.NewCore(vpn, &core.ClusterFake{}, &core.ProviderMetadataFake{}, &core.CloudControllerManagerFake{}, &core.CloudNodeManagerFake{}, &core.ClusterAutoscalerFake{}, logger, vtpm.OpenSimulatedTPM, fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs()))
cor, err := core.NewCore(vpn, &core.ClusterFake{}, &core.ProviderMetadataFake{}, &core.CloudControllerManagerFake{}, &core.CloudNodeManagerFake{}, &core.ClusterAutoscalerFake{}, &core.EncryptedDiskFake{}, logger, vtpm.OpenSimulatedTPM, fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
require.NoError(cor.AdvanceState(state.AcceptingInit, nil, nil))

View File

@ -169,7 +169,7 @@ func TestInitCluster(t *testing.T) {
zapLogger, err := zap.NewDevelopment()
require.NoError(err)
core, err := NewCore(&stubVPN{}, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, zapLogger, vtpm.OpenSimulatedTPM, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, vtpm.OpenSimulatedTPM, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
kubeconfig, err := core.InitCluster(tc.autoscalingNodeGroups, "cloud-service-account-uri")
@ -284,7 +284,7 @@ func TestJoinCluster(t *testing.T) {
zapLogger, err := zap.NewDevelopment()
require.NoError(err)
core, err := NewCore(&tc.vpn, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, zapLogger, vtpm.OpenSimulatedTPM, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&tc.vpn, &tc.cluster, &tc.metadata, &tc.cloudControllerManager, &tc.cloudNodeManager, &tc.clusterAutoscaler, nil, zapLogger, vtpm.OpenSimulatedTPM, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
joinReq := kubeadm.BootstrapTokenDiscovery{

View File

@ -36,6 +36,7 @@ type Core struct {
cloudControllerManager CloudControllerManager
cloudNodeManager CloudNodeManager
clusterAutoscaler ClusterAutoscaler
encryptedDisk EncryptedDisk
kms kms.CloudKMS
zaplogger *zap.Logger
persistentStoreFactory PersistentStoreFactory
@ -46,7 +47,7 @@ type Core struct {
// NewCore creates and initializes a new Core object.
func NewCore(vpn VPN, kube Cluster,
metadata ProviderMetadata, cloudControllerManager CloudControllerManager, cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler,
zapLogger *zap.Logger, openTPM vtpm.TPMOpenFunc, persistentStoreFactory PersistentStoreFactory, fileHandler file.Handler,
encryptedDisk EncryptedDisk, zapLogger *zap.Logger, openTPM vtpm.TPMOpenFunc, persistentStoreFactory PersistentStoreFactory, fileHandler file.Handler,
) (*Core, error) {
stor := store.NewStdStore()
c := &Core{
@ -58,6 +59,7 @@ func NewCore(vpn VPN, kube Cluster,
cloudNodeManager: cloudNodeManager,
cloudControllerManager: cloudControllerManager,
clusterAutoscaler: clusterAutoscaler,
encryptedDisk: encryptedDisk,
zaplogger: zapLogger,
kms: nil, // KMS is set up during init phase
persistentStoreFactory: persistentStoreFactory,

View File

@ -33,7 +33,7 @@ func TestGetNextNodeIP(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
require.NoError(core.InitializeStoreIPs())
@ -76,7 +76,7 @@ func TestSwitchToPersistentStore(t *testing.T) {
require := require.New(t)
storeFactory := &fakeStoreFactory{}
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, storeFactory, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, storeFactory, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
require.NoError(core.SwitchToPersistentStore())
@ -90,7 +90,7 @@ func TestGetIDs(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
_, _, err = core.GetIDs(nil)
@ -114,7 +114,7 @@ func TestNotifyNodeHeartbeat(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
const ip = "192.0.2.1"
@ -127,7 +127,7 @@ func TestDeriveKey(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
// error when no kms is set up
@ -208,7 +208,7 @@ func TestInitialize(t *testing.T) {
}).ToFile(fileHandler))
}
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, fileHandler)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, fileHandler)
require.NoError(err)
if tc.expectPanic {
@ -266,7 +266,7 @@ func TestPersistNodeState(t *testing.T) {
require.NoError(err)
require.NoError(file.Close())
}
core, err := NewCore(tc.vpn, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, fileHandler)
core, err := NewCore(tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, fileHandler)
require.NoError(err)
err = core.PersistNodeState(role.Coordinator, []byte("owner-id"), []byte("cluster-id"))
if tc.errExpected {

View File

@ -0,0 +1,57 @@
package core
import (
"fmt"
)
// GetDiskUUID gets the disk's UUID.
func (c *Core) GetDiskUUID() (string, error) {
if err := c.encryptedDisk.Open(); err != nil {
return "", fmt.Errorf("retrieving uuid of encrypted disk failed: cannot open disk: %w", err)
}
defer c.encryptedDisk.Close()
uuid, err := c.encryptedDisk.UUID()
if err != nil {
return "", fmt.Errorf("cannot retrieve uuid of disk: %w", err)
}
return uuid, nil
}
// UpdateDiskPassphrase switches the initial random passphrase of the encrypted disk to a permanent passphrase.
func (c *Core) UpdateDiskPassphrase(passphrase string) error {
if err := c.encryptedDisk.Open(); err != nil {
return fmt.Errorf("updating passphrase of encrypted disk failed: cannot open disk: %w", err)
}
defer c.encryptedDisk.Close()
return c.encryptedDisk.UpdatePassphrase(passphrase)
}
// EncryptedDisk manages the encrypted state disk.
type EncryptedDisk interface {
// Open prepares the underlying device for disk operations.
Open() error
// Close closes the underlying device.
Close() error
// UUID gets the device's UUID.
UUID() (string, error)
// UpdatePassphrase switches the initial random passphrase of the encrypted disk to a permanent passphrase.
UpdatePassphrase(passphrase string) error
}
type EncryptedDiskFake struct{}
func (f *EncryptedDiskFake) UUID() (string, error) {
return "fake-disk-uuid", nil
}
func (f *EncryptedDiskFake) UpdatePassphrase(passphrase string) error {
return nil
}
func (f *EncryptedDiskFake) Open() error {
return nil
}
func (f *EncryptedDiskFake) Close() error {
return nil
}

View File

@ -0,0 +1,122 @@
package core
import (
"errors"
"testing"
"github.com/edgelesssys/constellation/cli/file"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
func TestGetDiskUUID(t *testing.T) {
testCases := map[string]struct {
expectedUUID string
openErr error
uuidErr error
errExpected bool
}{
"getting uuid works": {
expectedUUID: "uuid",
},
"open can fail": {
openErr: errors.New("open-error"),
errExpected: true,
},
"getting disk uuid can fail": {
uuidErr: errors.New("uuid-err"),
errExpected: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
zapLogger, err := zap.NewDevelopment()
require.NoError(err)
diskStub := encryptedDiskStub{
openErr: tc.openErr,
uuidErr: tc.uuidErr,
uuid: tc.expectedUUID,
}
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
uuid, err := core.GetDiskUUID()
if tc.errExpected {
assert.Error(err)
return
}
require.NoError(err)
assert.Equal(tc.expectedUUID, uuid)
})
}
}
func TestUpdateDiskPassphrase(t *testing.T) {
testCases := map[string]struct {
openErr error
updatePassphraseErr error
errExpected bool
}{
"updating passphrase works": {},
"open can fail": {
openErr: errors.New("open-error"),
errExpected: true,
},
"updating disk passphrase can fail": {
updatePassphraseErr: errors.New("update-err"),
errExpected: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
zapLogger, err := zap.NewDevelopment()
require.NoError(err)
diskStub := encryptedDiskStub{
openErr: tc.openErr,
updatePassphraseErr: tc.updatePassphraseErr,
}
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, &diskStub, zapLogger, nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
err = core.UpdateDiskPassphrase("passphrase")
if tc.errExpected {
assert.Error(err)
return
}
require.NoError(err)
})
}
}
type encryptedDiskStub struct {
openErr error
closeErr error
uuid string
uuidErr error
updatePassphraseErr error
}
func (s *encryptedDiskStub) UUID() (string, error) {
return s.uuid, s.uuidErr
}
func (s *encryptedDiskStub) UpdatePassphrase(passphrase string) error {
return s.updatePassphraseErr
}
func (s *encryptedDiskStub) Open() error {
return s.openErr
}
func (s *encryptedDiskStub) Close() error {
return s.closeErr
}

View File

@ -145,11 +145,12 @@ func newMockCoreWithDialer(dialer *bufconnDialer) (*Core, *pubapi.API, error) {
ccmFake := &CloudControllerManagerFake{}
cnmFake := &CloudNodeManagerFake{}
autoscalerFake := &ClusterAutoscalerFake{}
encryptedDiskFake := &EncryptedDiskFake{}
getPublicAddr := func() (string, error) {
return "192.0.2.1", nil
}
core, err := NewCore(vpn, kubeFake, metadataFake, ccmFake, cnmFake, autoscalerFake, zapLogger, vtpm.OpenSimulatedTPM, &fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(vpn, kubeFake, metadataFake, ccmFake, cnmFake, autoscalerFake, encryptedDiskFake, zapLogger, vtpm.OpenSimulatedTPM, &fakeStoreFactory{}, file.NewHandler(afero.NewMemMapFs()))
if err != nil {
return nil, nil, err
}

View File

@ -53,7 +53,7 @@ func TestGetPeers(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
// prepare store
@ -113,7 +113,7 @@ func TestAddPeer(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
core, err := NewCore(&tc.vpn, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&tc.vpn, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), nil, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
err = core.AddPeer(tc.peer)

View File

@ -65,7 +65,7 @@ func TestAdvanceState(t *testing.T) {
return vtpm.OpenSimulatedTPM()
}
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, file.NewHandler(afero.NewMemMapFs()))
core, err := NewCore(&stubVPN{}, nil, nil, nil, nil, nil, nil, zaptest.NewLogger(t), openTPM, nil, file.NewHandler(afero.NewMemMapFs()))
require.NoError(err)
assert.Equal(state.Uninitialized, core.GetState())
core.state = tc.initialState

View File

@ -23,6 +23,8 @@ type Core interface {
SetUpKMS(ctx context.Context, storageURI, kmsURI, kekID string, useExisting bool) error
GetKMSInfo() (kms.KMSInformation, error)
GetDataKey(ctx context.Context, keyID string, length int) ([]byte, error)
GetDiskUUID() (string, error)
UpdateDiskPassphrase(passphrase string) error
GetState() state.State
RequireState(...state.State) error

View File

@ -145,3 +145,11 @@ func (c *fakeCore) GetKMSInfo() (kms.KMSInformation, error) {
func (c *fakeCore) GetDataKey(ctx context.Context, keyID string, length int) ([]byte, error) {
return c.dataKey, c.getDataKeyErr
}
func (c *fakeCore) GetDiskUUID() (string, error) {
return "fake-disk-uuid", nil
}
func (c *fakeCore) UpdateDiskPassphrase(passphrase string) error {
return nil
}