mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
9a1f52e94e
So far the masterSecret was sent to the initial bootstrapper on init/recovery. With this commit this information is encoded in the kmsURI that is sent during init. For recover, the communication with the recoveryserver is changed. Before a streaming gRPC call was used to exchanges UUID for measurementSecret and state disk key. Now a standard gRPC is made that includes the same kmsURI & storageURI that are sent during init.
226 lines
6.5 KiB
Go
226 lines
6.5 KiB
Go
//go:build integration
|
|
|
|
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package test
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/service/kms"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
kmsconfig "github.com/edgelesssys/constellation/v2/internal/kms/config"
|
|
awsInterface "github.com/edgelesssys/constellation/v2/internal/kms/kms/aws"
|
|
"github.com/edgelesssys/constellation/v2/internal/kms/storage"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var defaultPolicyWithDecryption = `{
|
|
"Version" : "2012-10-17",
|
|
"Id" : "updated-policy",
|
|
"Statement" : [ {
|
|
"Sid" : "Enable IAM User Permissions",
|
|
"Effect" : "Allow",
|
|
"Principal" : {
|
|
"AWS" : "*"
|
|
},
|
|
"Action" : [
|
|
"kms:CreateAlias",
|
|
"kms:CreateKey",
|
|
"kms:Decrypt",
|
|
"kms:DeleteAlias",
|
|
"kms:DescribeKey",
|
|
"kms:Encrypt",
|
|
"kms:GenerateDataKey",
|
|
"kms:GenerateDataKeyWithoutPlaintext",
|
|
"kms:GetKeyPolicy",
|
|
"kms:GetParametersForImport",
|
|
"kms:ImportKeyMaterial",
|
|
"kms:PutKeyPolicy",
|
|
"kms:ScheduleKeyDeletion"
|
|
],
|
|
"Resource" : "*"
|
|
} ]
|
|
}`
|
|
|
|
func TestAwsStorage(t *testing.T) {
|
|
if !*runAwsStorage {
|
|
t.Skip("Skipping AWS storage test")
|
|
}
|
|
assert := assert.New(t)
|
|
require := require.New(t)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
|
defer cancel()
|
|
bucketID := strings.ToLower(addSuffix("test-bucket"))
|
|
|
|
// create bucket
|
|
store, err := storage.NewAWSS3Storage(ctx, bucketID, func(*s3.Options) {})
|
|
require.NoError(err)
|
|
|
|
testDEK1 := []byte("test DEK")
|
|
testDEK2 := []byte("more test DEK")
|
|
|
|
// request unset value
|
|
_, err = store.Get(ctx, "test:input")
|
|
assert.Error(err)
|
|
|
|
// test Put method
|
|
assert.NoError(store.Put(ctx, "volume01", testDEK1))
|
|
assert.NoError(store.Put(ctx, "volume02", testDEK2))
|
|
|
|
// make sure values have been set
|
|
val, err := store.Get(ctx, "volume01")
|
|
assert.NoError(err)
|
|
assert.Equal(testDEK1, val)
|
|
val, err = store.Get(ctx, "volume02")
|
|
assert.NoError(err)
|
|
assert.Equal(testDEK2, val)
|
|
|
|
_, err = store.Get(ctx, "invalid:key")
|
|
assert.Error(err)
|
|
assert.ErrorIs(err, storage.ErrDEKUnset)
|
|
|
|
cleanUpBucket(ctx, require, bucketID, func(*s3.Options) {})
|
|
}
|
|
|
|
func cleanUpBucket(ctx context.Context, require *require.Assertions, bucketID string, optFns ...func(*s3.Options)) {
|
|
cfg, err := config.LoadDefaultConfig(ctx)
|
|
require.NoError(err)
|
|
client := s3.NewFromConfig(cfg, optFns...)
|
|
|
|
// List all objects of the bucket
|
|
listObjectsInput := &s3.ListObjectsV2Input{
|
|
Bucket: &bucketID,
|
|
}
|
|
output, _ := client.ListObjectsV2(ctx, listObjectsInput)
|
|
var objects []string
|
|
var i int32
|
|
for i = 0; i < output.KeyCount; i++ {
|
|
objects = append(objects, *output.Contents[i].Key)
|
|
}
|
|
// Delete all objects of the bucket
|
|
require.NoError(cleanUpObjects(ctx, client, bucketID, objects))
|
|
|
|
// Delete the bucket
|
|
deleteBucketInput := &s3.DeleteBucketInput{
|
|
Bucket: &bucketID,
|
|
}
|
|
_, err = client.DeleteBucket(ctx, deleteBucketInput)
|
|
require.NoError(err)
|
|
}
|
|
|
|
func cleanUpObjects(ctx context.Context, client *s3.Client, bucketID string, objectsToDelete []string) error {
|
|
var objectsIdentifier []types.ObjectIdentifier
|
|
for _, object := range objectsToDelete {
|
|
objectsIdentifier = append(objectsIdentifier, types.ObjectIdentifier{Key: aws.String(object)})
|
|
}
|
|
deleteObjectsInput := &s3.DeleteObjectsInput{
|
|
Bucket: &bucketID,
|
|
Delete: &types.Delete{Objects: objectsIdentifier},
|
|
}
|
|
_, err := client.DeleteObjects(ctx, deleteObjectsInput)
|
|
return err
|
|
}
|
|
|
|
func TestAwsKms(t *testing.T) {
|
|
if !*runAwsKms {
|
|
t.Skip("Skipping AWS KMS test")
|
|
}
|
|
require := require.New(t)
|
|
assert := assert.New(t)
|
|
|
|
newKEKId1 := addSuffix("test-key-")
|
|
newKEKId2 := addSuffix("test-key-")
|
|
require.NotEqual(newKEKId1, newKEKId2)
|
|
var keyPolicyProducer createKeyPolicyFunc
|
|
|
|
client, err := awsInterface.New(context.Background(), &keyPolicyProducer, nil, newKEKId1)
|
|
require.NoError(err)
|
|
|
|
privateKEK1 := []byte(strings.Repeat("1234", 8))
|
|
privateKEK2 := []byte(strings.Repeat("5678", 8))
|
|
privateKEK3 := make([]byte, 0)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
|
defer cancel()
|
|
|
|
// test setting first KEK
|
|
assert.NoError(client.CreateKEK(ctx, newKEKId1, privateKEK1))
|
|
|
|
// make sure that CreateKEK is idempotent
|
|
assert.NoError(client.CreateKEK(ctx, newKEKId1, privateKEK1))
|
|
|
|
// make sure you can not overwrite KEK with different key material
|
|
assert.NoError(client.CreateKEK(ctx, newKEKId1, privateKEK2))
|
|
|
|
// make sure that GetDEK is idempotent
|
|
volumeKey1, err := client.GetDEK(ctx, "volume01", kmsconfig.SymmetricKeyLength)
|
|
require.NoError(err)
|
|
volumeKey1Copy, err := client.GetDEK(ctx, "volume01", kmsconfig.SymmetricKeyLength)
|
|
require.NoError(err)
|
|
assert.Equal(volumeKey1, volumeKey1Copy)
|
|
|
|
// test setting a second DEK
|
|
volumeKey2, err := client.GetDEK(ctx, "volume02", kmsconfig.SymmetricKeyLength)
|
|
require.NoError(err)
|
|
assert.NotEqual(volumeKey1, volumeKey2)
|
|
|
|
// make sure AWS KMS generates KEK when calling CreateKEK with empty key
|
|
assert.NoError(client.CreateKEK(ctx, newKEKId2, privateKEK3))
|
|
|
|
// make sure that CreateKEK is idempotent
|
|
assert.NoError(client.CreateKEK(ctx, newKEKId2, privateKEK3))
|
|
|
|
// test setting a DEK with AWS KMS generated KEK
|
|
volumeKey3, err := client.GetDEK(ctx, "volume03", kmsconfig.SymmetricKeyLength)
|
|
require.NoError(err)
|
|
assert.NotEqual(volumeKey1, volumeKey3)
|
|
|
|
cleanUp(ctx, assert, require, newKEKId1)
|
|
cleanUp(ctx, assert, require, newKEKId2)
|
|
}
|
|
|
|
func cleanUp(ctx context.Context, assert *assert.Assertions, require *require.Assertions, alias string) {
|
|
cfg, err := config.LoadDefaultConfig(ctx)
|
|
require.NoError(err)
|
|
awsClient := kms.NewFromConfig(cfg)
|
|
require.NotNil(awsClient)
|
|
|
|
describeKeyInput := &kms.DescribeKeyInput{
|
|
KeyId: aws.String("alias/" + alias),
|
|
}
|
|
describeKeyOutput, err := awsClient.DescribeKey(ctx, describeKeyInput)
|
|
assert.NoError(err)
|
|
deleteAliasInput := &kms.DeleteAliasInput{
|
|
AliasName: aws.String("alias/" + alias),
|
|
}
|
|
_, err = awsClient.DeleteAlias(ctx, deleteAliasInput)
|
|
assert.NoError(err)
|
|
scheduleKeyDeletionInput := &kms.ScheduleKeyDeletionInput{
|
|
KeyId: describeKeyOutput.KeyMetadata.KeyId,
|
|
PendingWindowInDays: aws.Int32(7),
|
|
}
|
|
_, err = awsClient.ScheduleKeyDeletion(ctx, scheduleKeyDeletionInput)
|
|
assert.NoError(err)
|
|
}
|
|
|
|
type createKeyPolicyFunc struct{}
|
|
|
|
func (fn *createKeyPolicyFunc) CreateKeyPolicy(keyID string) (string, error) {
|
|
policy := defaultPolicyWithDecryption
|
|
policy = strings.Replace(policy, "<AWSAccountId>", "795746500882", 2)
|
|
return policy, nil
|
|
}
|