mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-12 01:14:28 -05:00
90b88e1cf9
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.
102 lines
3.1 KiB
Go
102 lines
3.1 KiB
Go
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3/types"
|
|
"github.com/edgelesssys/constellation/v2/keyservice/internal/config"
|
|
)
|
|
|
|
type awsS3ClientAPI interface {
|
|
GetObject(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error)
|
|
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
|
|
CreateBucket(ctx context.Context, params *s3.CreateBucketInput, optFns ...func(*s3.Options)) (*s3.CreateBucketOutput, error)
|
|
}
|
|
|
|
// AWSS3Storage is an implementation of the Storage interface, storing keys in AWS S3 buckets.
|
|
type AWSS3Storage struct {
|
|
bucketID string
|
|
client awsS3ClientAPI
|
|
optFns []func(*s3.Options)
|
|
}
|
|
|
|
// NewAWSS3Storage creates a Storage client for AWS S3: https://aws.amazon.com/s3/
|
|
//
|
|
// You need to provide credentials to authenticate to AWS using the cfg parameter.
|
|
func NewAWSS3Storage(ctx context.Context, bucketID string, optFns ...func(*s3.Options)) (*AWSS3Storage, error) {
|
|
// Create S3 client
|
|
cfg, err := awsconfig.LoadDefaultConfig(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
client := s3.NewFromConfig(cfg, optFns...)
|
|
|
|
store := &AWSS3Storage{client: client, bucketID: bucketID, optFns: optFns}
|
|
|
|
// Try to create new bucket, continue if bucket already exists
|
|
if err := store.createBucket(ctx, bucketID, cfg.Region, optFns...); err != nil {
|
|
return nil, err
|
|
}
|
|
return store, nil
|
|
}
|
|
|
|
// Get returns a DEK from from AWS S3 Storage by key ID.
|
|
func (s *AWSS3Storage) Get(ctx context.Context, keyID string) ([]byte, error) {
|
|
getObjectInput := &s3.GetObjectInput{
|
|
Bucket: &s.bucketID,
|
|
Key: &keyID,
|
|
}
|
|
output, err := s.client.GetObject(ctx, getObjectInput, s.optFns...)
|
|
if err != nil {
|
|
var nsk *types.NoSuchKey
|
|
if errors.As(err, &nsk) {
|
|
return nil, ErrDEKUnset
|
|
}
|
|
return nil, fmt.Errorf("downloading DEK from storage: %w", err)
|
|
}
|
|
return io.ReadAll(output.Body)
|
|
}
|
|
|
|
// Put saves a DEK to AWS S3 Storage by key ID.
|
|
func (s *AWSS3Storage) Put(ctx context.Context, keyID string, data []byte) error {
|
|
putObjectInput := &s3.PutObjectInput{
|
|
Bucket: &s.bucketID,
|
|
Key: &keyID,
|
|
Body: bytes.NewReader(data),
|
|
Tagging: &config.AWSS3Tag,
|
|
}
|
|
if _, err := s.client.PutObject(ctx, putObjectInput, s.optFns...); err != nil {
|
|
return fmt.Errorf("uploading DEK to storage: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *AWSS3Storage) createBucket(ctx context.Context, bucketID, region string, optFns ...func(*s3.Options)) error {
|
|
createBucketInput := &s3.CreateBucketInput{
|
|
Bucket: &bucketID,
|
|
CreateBucketConfiguration: &types.CreateBucketConfiguration{
|
|
LocationConstraint: types.BucketLocationConstraint(region),
|
|
},
|
|
}
|
|
if _, err := s.client.CreateBucket(ctx, createBucketInput, optFns...); err != nil {
|
|
var bne *types.BucketAlreadyExists
|
|
var baowby *types.BucketAlreadyOwnedByYou
|
|
if !(errors.As(err, &bne) || errors.As(err, &baowby)) {
|
|
return fmt.Errorf("creating storage container: %w", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|