constellation/keyservice/internal/test/aws_test.go
Otto Bittner 9a1f52e94e Refactor init/recovery to use kms URI
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.
2023-01-19 13:14:55 +01:00

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
}