mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-29 01:16:12 -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.
106 lines
3.6 KiB
Go
106 lines
3.6 KiB
Go
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package storage
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror"
|
|
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
|
|
"github.com/edgelesssys/constellation/v2/keyservice/internal/config"
|
|
)
|
|
|
|
type azureBlobAPI interface {
|
|
CreateContainer(context.Context, string, *container.CreateOptions) (azblob.CreateContainerResponse, error)
|
|
DownloadStream(context.Context, string, string, *blob.DownloadStreamOptions) (azblob.DownloadStreamResponse, error)
|
|
UploadStream(context.Context, string, string, io.Reader, *azblob.UploadStreamOptions) (azblob.UploadStreamResponse, error)
|
|
}
|
|
|
|
// AzureStorage is an implementation of the Storage interface, storing keys in the Azure Blob Store.
|
|
type AzureStorage struct {
|
|
client azureBlobAPI
|
|
connectionString string
|
|
containerName string
|
|
opts *AzureOpts
|
|
}
|
|
|
|
// AzureOpts are additional options to be used when interacting with the Azure API.
|
|
type AzureOpts struct {
|
|
service *azblob.ClientOptions
|
|
download *azblob.DownloadStreamOptions
|
|
upload *azblob.UploadStreamOptions
|
|
}
|
|
|
|
// NewAzureStorage initializes a storage client using Azure's Blob Storage: https://azure.microsoft.com/en-us/services/storage/blobs/
|
|
//
|
|
// A connections string is required to connect to the Storage Account, see https://docs.microsoft.com/en-us/azure/storage/common/storage-configure-connection-string
|
|
// If the container does not exists, a new one is created automatically.
|
|
// Connect options for the Client, Downloader and Uploader can be configured using opts.
|
|
func NewAzureStorage(ctx context.Context, connectionString, containerName string, opts *AzureOpts) (*AzureStorage, error) {
|
|
if opts == nil {
|
|
opts = &AzureOpts{}
|
|
}
|
|
|
|
client, err := azblob.NewClientFromConnectionString(connectionString, opts.service)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating storage client from connection string: %w", err)
|
|
}
|
|
|
|
s := &AzureStorage{
|
|
client: client,
|
|
connectionString: connectionString,
|
|
containerName: containerName,
|
|
opts: opts,
|
|
}
|
|
|
|
// Try to create a new storage container, continue if it already exists
|
|
if err := s.createContainerOrContinue(ctx); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return s, nil
|
|
}
|
|
|
|
// Get returns a DEK from from Azure Blob Storage by key ID.
|
|
func (s *AzureStorage) Get(ctx context.Context, keyID string) ([]byte, error) {
|
|
res, err := s.client.DownloadStream(ctx, s.containerName, keyID, s.opts.download)
|
|
if err != nil {
|
|
if bloberror.HasCode(err, bloberror.BlobNotFound) {
|
|
return nil, ErrDEKUnset
|
|
}
|
|
return nil, fmt.Errorf("downloading DEK from storage: %w", err)
|
|
}
|
|
defer res.Body.Close()
|
|
return io.ReadAll(res.Body)
|
|
}
|
|
|
|
// Put saves a DEK to Azure Blob Storage by key ID.
|
|
func (s *AzureStorage) Put(ctx context.Context, keyID string, encDEK []byte) error {
|
|
if _, err := s.client.UploadStream(ctx, s.containerName, keyID, bytes.NewReader(encDEK), s.opts.upload); err != nil {
|
|
return fmt.Errorf("uploading DEK to storage: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// createContainerOrContinue creates a new storage container if necessary, or continues if it already exists.
|
|
func (s *AzureStorage) createContainerOrContinue(ctx context.Context) error {
|
|
_, err := s.client.CreateContainer(ctx, s.containerName, &azblob.CreateContainerOptions{
|
|
Metadata: config.StorageTags,
|
|
})
|
|
if (err == nil) || bloberror.HasCode(err, bloberror.ContainerAlreadyExists) {
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("creating storage container: %w", err)
|
|
}
|