constellation/keyservice/internal/test/aws_test.go
Otto Bittner 90b88e1cf9 kms: rename kms to keyservice
In the light of extending our eKMS support it will be helpful
to have a tighter use of the word "KMS".
KMS should refer to the actual component that manages keys.
The keyservice, also called KMS in the constellation code,
does not manage keys itself. It talks to a KMS backend,
which in turn does the actual key management.
2023-01-16 11:56:34 +01:00

226 lines
6.6 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/keyservice/internal/config"
"github.com/edgelesssys/constellation/v2/keyservice/internal/storage"
awsInterface "github.com/edgelesssys/constellation/v2/keyservice/kms/aws"
"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)
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, newKEKId1, "volume01", kmsconfig.SymmetricKeyLength)
require.NoError(err)
volumeKey1Copy, err := client.GetDEK(ctx, newKEKId1, "volume01", kmsconfig.SymmetricKeyLength)
require.NoError(err)
assert.Equal(volumeKey1, volumeKey1Copy)
// test setting a second DEK
volumeKey2, err := client.GetDEK(ctx, newKEKId1, "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, newKEKId2, "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
}