From 5eb73706f54cf00b8ef7bdb20387682f2cb7d8dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Wei=C3=9Fe?= <66256922+daniel-weisse@users.noreply.github.com> Date: Thu, 2 Mar 2023 15:08:31 +0100 Subject: [PATCH] internal: refactor storage credentials (#1071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move storage clients to separate packages * Allow setting of client credentials for AWS S3 * Use managed identity client secret or default credentials for Azure Blob Storage * Use credentials file to authorize GCS client --------- Signed-off-by: Daniel Weiße <dw@edgeless.systems> --- .../internal/initserver/initserver_test.go | 21 +- cli/internal/cmd/init.go | 22 +- cli/internal/cmd/init_test.go | 15 +- cli/internal/cmd/recover.go | 6 +- cli/internal/cmd/recover_test.go | 4 +- go.mod | 13 +- hack/go.mod | 30 --- hack/go.sum | 93 ------- internal/kms/README.md | 112 ++------ internal/kms/setup/setup.go | 119 +-------- internal/kms/setup/setup_test.go | 198 +------------- .../{awss3storage.go => awss3/awss3.go} | 50 ++-- .../awss3_test.go} | 13 +- internal/kms/storage/azureblob/azureblob.go | 104 ++++++++ .../azureblob_test.go} | 31 +-- internal/kms/storage/azurestorage.go | 105 -------- internal/kms/storage/gcloudstorage.go | 130 --------- .../storage/gcloudstorage_integration_test.go | 114 -------- internal/kms/storage/gcs/gcs.go | 131 +++++++++ .../gcs_test.go} | 35 ++- internal/kms/storage/memfs/memfs.go | 44 +++ .../memfs_test.go} | 21 +- internal/kms/storage/memfsstorage.go | 38 --- internal/kms/test/aws_test.go | 55 ++-- internal/kms/test/azure_test.go | 37 ++- internal/kms/test/gcp_test.go | 33 +-- internal/kms/test/integration_test.go | 45 +++- internal/kms/uri/uri.go | 250 ++++++++++++++++-- internal/kms/uri/uri_test.go | 113 ++++++++ keyservice/cmd/main.go | 5 +- 30 files changed, 857 insertions(+), 1130 deletions(-) rename internal/kms/storage/{awss3storage.go => awss3/awss3.go} (54%) rename internal/kms/storage/{awss3storage_test.go => awss3/awss3_test.go} (94%) create mode 100644 internal/kms/storage/azureblob/azureblob.go rename internal/kms/storage/{azurestorage_test.go => azureblob/azureblob_test.go} (86%) delete mode 100644 internal/kms/storage/azurestorage.go delete mode 100644 internal/kms/storage/gcloudstorage.go delete mode 100644 internal/kms/storage/gcloudstorage_integration_test.go create mode 100644 internal/kms/storage/gcs/gcs.go rename internal/kms/storage/{gcloudstorage_test.go => gcs/gcs_test.go} (85%) create mode 100644 internal/kms/storage/memfs/memfs.go rename internal/kms/storage/{memfsstroage_test.go => memfs/memfs_test.go} (62%) delete mode 100644 internal/kms/storage/memfsstorage.go create mode 100644 internal/kms/uri/uri_test.go diff --git a/bootstrapper/internal/initserver/initserver_test.go b/bootstrapper/internal/initserver/initserver_test.go index 2f5b3398c..4347ef9bc 100644 --- a/bootstrapper/internal/initserver/initserver_test.go +++ b/bootstrapper/internal/initserver/initserver_test.go @@ -20,6 +20,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/crypto/testvector" "github.com/edgelesssys/constellation/v2/internal/file" kmssetup "github.com/edgelesssys/constellation/v2/internal/kms/setup" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/oid" "github.com/edgelesssys/constellation/v2/internal/versions/components" @@ -90,7 +91,7 @@ func TestInit(t *testing.T) { initSecretHash, err := bcrypt.GenerateFromPassword(initSecret, bcrypt.DefaultCost) require.NoError(t, err) - masterSecret := kmssetup.MasterSecret{Key: []byte("secret"), Salt: []byte("salt")} + masterSecret := uri.MasterSecret{Key: []byte("secret"), Salt: []byte("salt")} testCases := map[string]struct { nodeLock *fakeLock @@ -108,14 +109,14 @@ func TestInit(t *testing.T) { disk: &stubDisk{}, fileHandler: file.NewHandler(afero.NewMemMapFs()), initSecretHash: initSecretHash, - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, }, "node locked": { nodeLock: lockedLock, initializer: &stubClusterInitializer{}, disk: &stubDisk{}, fileHandler: file.NewHandler(afero.NewMemMapFs()), - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, initSecretHash: initSecretHash, wantErr: true, wantShutdown: true, @@ -125,7 +126,7 @@ func TestInit(t *testing.T) { initializer: &stubClusterInitializer{}, disk: &stubDisk{openErr: someErr}, fileHandler: file.NewHandler(afero.NewMemMapFs()), - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, initSecretHash: initSecretHash, wantErr: true, }, @@ -134,7 +135,7 @@ func TestInit(t *testing.T) { initializer: &stubClusterInitializer{}, disk: &stubDisk{uuidErr: someErr}, fileHandler: file.NewHandler(afero.NewMemMapFs()), - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, initSecretHash: initSecretHash, wantErr: true, }, @@ -143,7 +144,7 @@ func TestInit(t *testing.T) { initializer: &stubClusterInitializer{}, disk: &stubDisk{updatePassphraseErr: someErr}, fileHandler: file.NewHandler(afero.NewMemMapFs()), - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, initSecretHash: initSecretHash, wantErr: true, }, @@ -152,7 +153,7 @@ func TestInit(t *testing.T) { initializer: &stubClusterInitializer{}, disk: &stubDisk{}, fileHandler: file.NewHandler(afero.NewReadOnlyFs(afero.NewMemMapFs())), - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, initSecretHash: initSecretHash, wantErr: true, }, @@ -161,7 +162,7 @@ func TestInit(t *testing.T) { initializer: &stubClusterInitializer{initClusterErr: someErr}, disk: &stubDisk{}, fileHandler: file.NewHandler(afero.NewMemMapFs()), - req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: kmssetup.NoStoreURI}, + req: &initproto.InitRequest{InitSecret: initSecret, KmsUri: masterSecret.EncodeToURI(), StorageUri: uri.NoStoreURI}, initSecretHash: initSecretHash, wantErr: true, }, @@ -249,9 +250,9 @@ func TestSetupDisk(t *testing.T) { disk: disk, } - masterSecret := kmssetup.MasterSecret{Key: tc.masterKey, Salt: tc.salt} + masterSecret := uri.MasterSecret{Key: tc.masterKey, Salt: tc.salt} - cloudKms, err := kmssetup.KMS(context.Background(), kmssetup.NoStoreURI, masterSecret.EncodeToURI()) + cloudKms, err := kmssetup.KMS(context.Background(), uri.NoStoreURI, masterSecret.EncodeToURI()) require.NoError(err) assert.NoError(server.setupDisk(context.Background(), cloudKms)) }) diff --git a/cli/internal/cmd/init.go b/cli/internal/cmd/init.go index a5b8792b9..c1694b2d0 100644 --- a/cli/internal/cmd/init.go +++ b/cli/internal/cmd/init.go @@ -33,7 +33,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/grpc/dialer" grpcRetry "github.com/edgelesssys/constellation/v2/internal/grpc/retry" - kmssetup "github.com/edgelesssys/constellation/v2/internal/kms/setup" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/license" "github.com/edgelesssys/constellation/v2/internal/retry" "github.com/edgelesssys/constellation/v2/internal/versions" @@ -164,7 +164,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud MasterSecret: masterSecret.Key, Salt: masterSecret.Salt, KmsUri: masterSecret.EncodeToURI(), - StorageUri: kmssetup.NoStoreURI, + StorageUri: uri.NoStoreURI, KeyEncryptionKeyId: "", UseExistingKek: false, CloudServiceAccountUri: serviceAccURI, @@ -351,19 +351,19 @@ type initFlags struct { } // readOrGenerateMasterSecret reads a base64 encoded master secret from file or generates a new 32 byte secret. -func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, filename string) (kmssetup.MasterSecret, error) { +func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler file.Handler, filename string) (uri.MasterSecret, error) { if filename != "" { i.log.Debugf("Reading master secret from file %q", filename) - var secret kmssetup.MasterSecret + var secret uri.MasterSecret if err := fileHandler.ReadJSON(filename, &secret); err != nil { - return kmssetup.MasterSecret{}, err + return uri.MasterSecret{}, err } if len(secret.Key) < crypto.MasterSecretLengthMin { - return kmssetup.MasterSecret{}, fmt.Errorf("provided master secret is smaller than the required minimum of %d Bytes", crypto.MasterSecretLengthMin) + return uri.MasterSecret{}, fmt.Errorf("provided master secret is smaller than the required minimum of %d Bytes", crypto.MasterSecretLengthMin) } if len(secret.Salt) < crypto.RNGLengthDefault { - return kmssetup.MasterSecret{}, fmt.Errorf("provided salt is smaller than the required minimum of %d Bytes", crypto.RNGLengthDefault) + return uri.MasterSecret{}, fmt.Errorf("provided salt is smaller than the required minimum of %d Bytes", crypto.RNGLengthDefault) } return secret, nil } @@ -372,19 +372,19 @@ func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler fi i.log.Debugf("Generating new master secret") key, err := crypto.GenerateRandomBytes(crypto.MasterSecretLengthDefault) if err != nil { - return kmssetup.MasterSecret{}, err + return uri.MasterSecret{}, err } salt, err := crypto.GenerateRandomBytes(crypto.RNGLengthDefault) if err != nil { - return kmssetup.MasterSecret{}, err + return uri.MasterSecret{}, err } - secret := kmssetup.MasterSecret{ + secret := uri.MasterSecret{ Key: key, Salt: salt, } i.log.Debugf("Generated master secret key and salt values") if err := fileHandler.WriteJSON(constants.MasterSecretFilename, secret, file.OptNone); err != nil { - return kmssetup.MasterSecret{}, err + return uri.MasterSecret{}, err } fmt.Fprintf(outWriter, "Your Constellation master secret was successfully written to ./%s\n", constants.MasterSecretFilename) return secret, nil diff --git a/cli/internal/cmd/init_test.go b/cli/internal/cmd/init_test.go index 52811658c..63635ecc4 100644 --- a/cli/internal/cmd/init_test.go +++ b/cli/internal/cmd/init_test.go @@ -18,9 +18,6 @@ import ( "testing" "time" - kmssetup "github.com/edgelesssys/constellation/v2/internal/kms/setup" - "github.com/edgelesssys/constellation/v2/internal/versions" - "github.com/edgelesssys/constellation/v2/bootstrapper/initproto" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/clusterid" @@ -33,9 +30,11 @@ import ( "github.com/edgelesssys/constellation/v2/internal/grpc/atlscredentials" "github.com/edgelesssys/constellation/v2/internal/grpc/dialer" "github.com/edgelesssys/constellation/v2/internal/grpc/testdialer" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/license" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/oid" + "github.com/edgelesssys/constellation/v2/internal/versions" "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -193,7 +192,7 @@ func TestInitialize(t *testing.T) { require.NoError(err) // assert.Contains(out.String(), base64.StdEncoding.EncodeToString([]byte("ownerID"))) assert.Contains(out.String(), hex.EncodeToString([]byte("clusterID"))) - var secret kmssetup.MasterSecret + var secret uri.MasterSecret assert.NoError(fileHandler.ReadJSON(constants.MasterSecretFilename, &secret)) assert.NotEmpty(secret.Key) assert.NotEmpty(secret.Salt) @@ -288,7 +287,7 @@ func TestReadOrGenerateMasterSecret(t *testing.T) { createFileFunc: func(handler file.Handler) error { return handler.WriteJSON( "someSecret", - kmssetup.MasterSecret{Key: []byte("constellation-master-secret"), Salt: []byte("constellation-32Byte-length-salt")}, + uri.MasterSecret{Key: []byte("constellation-master-secret"), Salt: []byte("constellation-32Byte-length-salt")}, file.OptNone, ) }, @@ -319,7 +318,7 @@ func TestReadOrGenerateMasterSecret(t *testing.T) { createFileFunc: func(handler file.Handler) error { return handler.WriteJSON( "shortSecret", - kmssetup.MasterSecret{Key: []byte("constellation-master-secret"), Salt: []byte("short")}, + uri.MasterSecret{Key: []byte("constellation-master-secret"), Salt: []byte("short")}, file.OptNone, ) }, @@ -331,7 +330,7 @@ func TestReadOrGenerateMasterSecret(t *testing.T) { createFileFunc: func(handler file.Handler) error { return handler.WriteJSON( "shortSecret", - kmssetup.MasterSecret{Key: []byte("short"), Salt: []byte("constellation-32Byte-length-salt")}, + uri.MasterSecret{Key: []byte("short"), Salt: []byte("constellation-32Byte-length-salt")}, file.OptNone, ) }, @@ -377,7 +376,7 @@ func TestReadOrGenerateMasterSecret(t *testing.T) { tc.filename = strings.Trim(filename[1], "\n") } - var masterSecret kmssetup.MasterSecret + var masterSecret uri.MasterSecret require.NoError(fileHandler.ReadJSON(tc.filename, &masterSecret)) assert.Equal(masterSecret.Key, secret.Key) assert.Equal(masterSecret.Salt, secret.Salt) diff --git a/cli/internal/cmd/recover.go b/cli/internal/cmd/recover.go index f67df6bc0..8df72cb76 100644 --- a/cli/internal/cmd/recover.go +++ b/cli/internal/cmd/recover.go @@ -24,7 +24,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/grpc/dialer" grpcRetry "github.com/edgelesssys/constellation/v2/internal/grpc/retry" - kmssetup "github.com/edgelesssys/constellation/v2/internal/kms/setup" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/retry" "github.com/spf13/afero" "github.com/spf13/cobra" @@ -73,7 +73,7 @@ func (r *recoverCmd) recover( } r.log.Debugf("Using flags: %+v", flags) - var masterSecret kmssetup.MasterSecret + var masterSecret uri.MasterSecret r.log.Debugf("Loading master secret file from %s", flags.secretPath) if err := fileHandler.ReadJSON(flags.secretPath, &masterSecret); err != nil { return err @@ -102,7 +102,7 @@ func (r *recoverCmd) recover( r.log.Debugf("Created a new validator") doer.setDialer(newDialer(validator), flags.endpoint) r.log.Debugf("Set dialer for endpoint %s", flags.endpoint) - doer.setURIs(masterSecret.EncodeToURI(), kmssetup.NoStoreURI) + doer.setURIs(masterSecret.EncodeToURI(), uri.NoStoreURI) r.log.Debugf("Set secrets") if err := r.recoverCall(cmd.Context(), cmd.OutOrStdout(), interval, doer); err != nil { if grpcRetry.ServiceIsUnavailable(err) { diff --git a/cli/internal/cmd/recover_test.go b/cli/internal/cmd/recover_test.go index 51fd4d400..668eb5826 100644 --- a/cli/internal/cmd/recover_test.go +++ b/cli/internal/cmd/recover_test.go @@ -26,7 +26,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/grpc/atlscredentials" "github.com/edgelesssys/constellation/v2/internal/grpc/dialer" "github.com/edgelesssys/constellation/v2/internal/grpc/testdialer" - kmssetup "github.com/edgelesssys/constellation/v2/internal/kms/setup" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/spf13/afero" "github.com/stretchr/testify/assert" @@ -158,7 +158,7 @@ func TestRecover(t *testing.T) { require.NoError(fileHandler.WriteJSON( "constellation-mastersecret.json", - kmssetup.MasterSecret{Key: tc.masterSecret.Secret, Salt: tc.masterSecret.Salt}, + uri.MasterSecret{Key: tc.masterSecret.Secret, Salt: tc.masterSecret.Salt}, file.OptNone, )) diff --git a/go.mod b/go.mod index e96e03472..2701ef7b5 100644 --- a/go.mod +++ b/go.mod @@ -53,11 +53,14 @@ require ( github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/aws/aws-sdk-go-v2 v1.17.5 github.com/aws/aws-sdk-go-v2/config v1.18.15 + github.com/aws/aws-sdk-go-v2/credentials v1.13.15 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.23 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.55 github.com/aws/aws-sdk-go-v2/service/cloudfront v1.26.0 github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.20.5 github.com/aws/aws-sdk-go-v2/service/ec2 v1.86.1 + github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.19.5 + github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.14.5 github.com/aws/aws-sdk-go-v2/service/s3 v1.30.5 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.6 github.com/aws/smithy-go v1.13.5 @@ -72,6 +75,7 @@ require ( github.com/google/go-tpm-tools v0.3.10 github.com/googleapis/gax-go/v2 v2.7.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 + github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7 github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7 github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 @@ -113,10 +117,9 @@ require ( k8s.io/kubernetes v1.26.2 k8s.io/mount-utils v0.26.2 k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 + sigs.k8s.io/yaml v1.3.0 ) -require github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect - require ( cloud.google.com/go v0.107.0 // indirect cloud.google.com/go/iam v0.8.0 // indirect @@ -144,20 +147,18 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 // indirect github.com/Masterminds/squirrel v1.5.3 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect + github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect github.com/aws/aws-sdk-go v1.44.209 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.15 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.29 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.23 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.3.30 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.21 // indirect - github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2 v1.19.5 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.24 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.23 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.23 // indirect - github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.14.5 github.com/aws/aws-sdk-go-v2/service/sso v1.12.4 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.4 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.18.5 // indirect @@ -224,7 +225,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.3.1 // indirect - github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect @@ -323,5 +323,4 @@ require ( sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 ) diff --git a/hack/go.mod b/hack/go.mod index f7ea7cca6..009786dd3 100644 --- a/hack/go.mod +++ b/hack/go.mod @@ -54,27 +54,9 @@ require ( ) require ( - cloud.google.com/go v0.107.0 // indirect cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.8.0 // indirect - cloud.google.com/go/kms v1.8.0 // indirect - cloud.google.com/go/storage v1.28.1 // indirect - github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.28 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/BurntSushi/toml v1.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect @@ -86,7 +68,6 @@ require ( github.com/acomagu/bufpipe v1.0.4 // indirect github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/aws/aws-sdk-go v1.44.209 // indirect github.com/aws/aws-sdk-go-v2 v1.17.5 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.15 // indirect @@ -119,7 +100,6 @@ require ( github.com/cyberphone/json-canonicalization v0.0.0-20210303052042-6bc126869bf4 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect github.com/docker/cli v20.10.21+incompatible // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/docker v20.10.22+incompatible // indirect @@ -153,7 +133,6 @@ require ( github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/btree v1.1.2 // indirect @@ -178,15 +157,8 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.3.1 // indirect - github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7 // indirect - github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect - github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 // indirect - github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hc-install v0.4.0 // indirect github.com/hashicorp/terraform-exec v0.17.3 // indirect @@ -215,7 +187,6 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -281,7 +252,6 @@ require ( golang.org/x/text v0.7.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.6.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/api v0.110.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc // indirect diff --git a/hack/go.sum b/hack/go.sum index 5f37afb3a..99999b224 100644 --- a/hack/go.sum +++ b/hack/go.sum @@ -32,7 +32,6 @@ cloud.google.com/go v0.92.2/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+Y cloud.google.com/go v0.92.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -46,10 +45,6 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/kms v1.8.0 h1:VrJLOsMRzW7IqTTYn+OYupqF3iKSE060Nrn+PECrYjg= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= cloud.google.com/go/monitoring v0.1.0/go.mod h1:Hpm3XfzJv+UTiXzCG5Ffp0wijzHTC7Cv4eR7o3x/fEE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= @@ -67,8 +62,6 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= @@ -84,47 +77,11 @@ github.com/Azure/azure-amqp-common-go/v2 v2.1.0/go.mod h1:R8rea+gJRuJR6QxTir/XuE github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-sdk-for-go v29.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v30.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 h1:gVXuXcWd1i4C2Ruxe321aU+IKGaStvGB/S90PUPB/W8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1/go.mod h1:DffdKW9RFqa5VgmsjUOsS7UE7eiA5iAvYUs63bhKQ0M= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 h1:T8quHYlUGyb/oqtSTwqlCr1ilJHrDv+ZtpSfo+hm1BU= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 h1:YvQv9Mz6T8oR5ypQOL6erY0Z5t71ak1uHV4QFokCOZk= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU= github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM= -github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk= -github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1 h1:oPdPEZFSbl7oSPEAIPMPBMUmiL+mqgzBJwM/9qYcwNg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -214,10 +171,7 @@ github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpi github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= -github.com/aws/aws-sdk-go v1.44.209 h1:wZuiaA4eaqYZmoZXqGgNHqVD7y7kUGFvACDGBgowTps= -github.com/aws/aws-sdk-go v1.44.209/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.17.5 h1:TzCUW1Nq4H8Xscph5M/skINUitxM5UBAyvm2s7XBzL4= github.com/aws/aws-sdk-go-v2 v1.17.5/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= @@ -354,10 +308,7 @@ github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mz github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= -github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/docker/cli v20.10.21+incompatible h1:qVkgyYUnOLQ98LtXBrwd/duVqPT2X4SHndOuGsfwyhU= github.com/docker/cli v20.10.21+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= @@ -409,7 +360,6 @@ github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVB github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= @@ -570,10 +520,6 @@ github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -681,12 +627,10 @@ github.com/google/licenseclassifier v0.0.0-20210325184830-bb04aff29e72/go.mod h1 github.com/google/logger v1.1.1 h1:+6Z2geNxc9G+4D4oDO9njjjn2d0wN5d7uOo0vOIW1NQ= github.com/google/logger v1.1.1/go.mod h1:BkeJZ+1FhQ+/d087r4dzojEg1u2ZX+ZqG1jTUrLM+zQ= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible h1:xmapqc1AyLoB+ddYT6r04bD9lIjlOqGaREovi0SzFaE= github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -773,18 +717,8 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo= -github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.8 h1:9Q2lu1YbbmiAgvYZ7Pr31RdlVonUpX+mmDL7Z7qTA2U= -github.com/hashicorp/go-kms-wrapping/v2 v2.0.8/go.mod h1:qTCjxGig/kjuj3hk1z8pOUrzbse/GxB1tGfbrq8tGJg= -github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7 h1:E3eEWpkofgPNrYyYznfS1+drq4/jFcqHQVNcL7WhUCo= -github.com/hashicorp/go-kms-wrapping/wrappers/awskms/v2 v2.0.7/go.mod h1:j5vefRoguQUG7iM4reS/hKIZssU1lZRqNPM5Wow6UnM= -github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7 h1:X27JWuPW6Gmi2l7NMm0pvnp7z7hhtns2TeIOQU93mqI= -github.com/hashicorp/go-kms-wrapping/wrappers/azurekeyvault/v2 v2.0.7/go.mod h1:i7Dt9mDsVUQG/I639jtdQerliaO2SvvPnpYPhZ8CGZ4= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8 h1:16I8OqBEuxZIowwn3jiLvhlx+z+ia4dJc9stvz0yUBU= -github.com/hashicorp/go-kms-wrapping/wrappers/gcpckms/v2 v2.0.8/go.mod h1:6QUMo5BrXAtbzSuZilqmx0A4px2u6PeFK7vfp2WIzeM= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= @@ -793,14 +727,10 @@ github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6 h1:W9WN8p6moV1fjKLkeqEgkAMu5rauy9QeYDAmIaPuuiA= -github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= -github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= @@ -856,7 +786,6 @@ github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8 github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -912,14 +841,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= @@ -963,9 +890,6 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= @@ -976,8 +900,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -1010,7 +932,6 @@ github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFW github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= @@ -1119,7 +1040,6 @@ github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pjbgf/sha1cd v0.2.3 h1:uKQP/7QOzNtKYH7UTohZLcjF5/55EnTw0jO/Ru4jZwI= github.com/pjbgf/sha1cd v0.2.3/go.mod h1:HOK9QrgzdHpbc2Kzip0Q1yi3M2MFGPADtR6HjG65m5M= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -1197,7 +1117,6 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= @@ -1303,7 +1222,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= @@ -1498,10 +1416,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= @@ -1610,7 +1526,6 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -1691,7 +1606,6 @@ golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1704,7 +1618,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1745,19 +1658,16 @@ golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1767,7 +1677,6 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= @@ -1879,8 +1788,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= diff --git a/internal/kms/README.md b/internal/kms/README.md index 5ab3a6b5d..f14be0751 100644 --- a/internal/kms/README.md +++ b/internal/kms/README.md @@ -51,7 +51,8 @@ Further, the vault type is chosen to configure whether or not the Key Vault is a ### Google KMS -Providing credentials to your application for Google's Cloud KMS h +Providing credentials to your application for Google's Cloud KMS happens through the usage of service accounts. +A credentials file for the service account is used to authorize the client. Note that the service account used for authentication requires the following permissions: @@ -74,109 +75,30 @@ Supported are: Each Plugin requires credentials to authenticate itself to a CSP. -### AWS S3 Bucket +#### AWS S3 Bucket -To use the AWS S3 Bucket plugin, you need to have an existing [AWS account](https://aws.amazon.com/de/premiumsupport/knowledge-center/create-and-activate-aws-account/). +For authentication an access key ID and an access key secret is used. +As a fallback, the client may try to automatically fetch the data from the local AWS directory. -For authentication, you have to pass a config file to the plugin. The AWS config package lets you automatically fetch the data from the local AWS directory. +#### Azure Blob Storage -#### Passing credentials automatically +Authorization for Azure Blob Storage happens through the use of manged identities. +The managed identity requires the following permissions: -You need to store your credentials in your local AWS directory at `$HOME/.aws/`. The AWS config package uses the values from the directory to build a config file, which is used to authenticate the client. The local AWS directory must contain two files: +* `Microsoft.Storage/storageAccounts/blobServices/containers/write` (only if a storage container is not set up in advance) +* `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write` +* `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read` +* `Microsoft.Storage/storageAccounts/blobServices/containers/blobs/add/action` -* `credentials` +#### Google Cloud Storage - ```bash - [default] - aws_access_key_id = MyAccessKeyId - aws_secret_access_key = MySecretAccessKey - ``` +Providing credentials to your application for Google's Cloud Storage happens through the usage of service accounts. +A credentials file for the service account is used to authorize the client. -* `config` +Note that the service account requires the following permissions: - ```bash - [default] - region = MyRegion - output = json - ``` - -If you have the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) installed, you can -initialise the files with the following command: - -```bash -aws configure -``` - -To create the client: - -```Go -cfg, err := config.LoadDefaultConfig(context.TODO()) -store, err := storage.NewAWSS3Storage(context.TODO(), "bucketName", cfg, func(*s3.Options) {}) -``` - -### Azure Blob Storage - -To use the Azure Blob storage plugin, you need to first [create a storage account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-create?tabs=azure-portal) or give your application access to an existing storage account. - -The plugin uses a connection string created for the storage account to authenticate itself to the Azure API. -The connection string can be found in your storage account in the Azure Portal under the "Access Keys" section or with the following Azure CLI command: - -```bash -az storage account show-connection-string -g MyResourceGroup -n MyStorageAccount -``` - -The client will use the specified Blob container if it already exists, or create it first otherwise. - -To create the client: - -```Go -connectionString := "DefaultEndpointsProtocol=https;AccountName=<myAccountName>;AccountKey=<myAccountKey>;EndpointSuffix=core.windows.net" -store, err := storage.NewAzureStorage(context.TODO(), connectionString, "myContainer", nil) -``` - -### Google Cloud Storage - -To use the Google Cloud Storage plugin, the Cloud Storage API needs to be enabled in your Google Cloud Account. You can use an existing bucket, create a new bucket yourself, or let the plugin create the bucket on initialization. - -When using the Google Cloud APIs, your application will typically [authenticate as a service account](https://cloud.google.com/docs/authentication/production). -You have two options for passing service account credentials to the Storage plugin: (1) Fetching them automatically from the environment or (2) passing them manually in your Go code. - -Note that the serivce account requires the following permissions: - -* `storage.buckets.create` +* `storage.buckets.create` (only if a bucket is not set up in advance) * `storage.buckets.get` * `storage.objects.create` * `storage.objects.get` * `storage.objects.update` - -#### Finding credentials automatically - -If your application is running inside a Google Cloud environment, and you have [attached a service account](https://cloud.google.com/iam/docs/impersonating-service-accounts#attaching-to-resources) to that environment, the Storage Plugin can retrieve credentials for the service account automatically. - -If your application is running in an environment with no service account attached, you can manually attach a [service account key](https://cloud.google.com/iam/docs/service-accounts#service_account_keys) to that environment. -After you [created a service account and stored its access key to file](https://cloud.google.com/docs/authentication/production#create_service_account) you need to set the environment variable `GOOGLE_APPLICATION_CREDENTIALS` to the location of that file. -The Storage Plugin will then be able to automatically load the credentials from there: - -```bash -export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-file.json" -``` - -To create the client: - -```Go -store, err := storage.NewGoogleCloudStorage(context.TODO(), "myProject", "myBucket", nil) -``` - -#### Passing credentials manually - -You may also explicitly use your service account file in code. -First, create a service account and key the same way as in [finding credentials automatically](#finding-credentials-automatically). -You can then specify the location of the file in your application code. - -To create the client: - -```Go -credentialFile := "/path/to/service-account-file.json" -opts := option.WithCredentialsFile(credentialFile) -store, err := storage.NewGoogleCloudStorage(context.TODO(), "myProject", "myBucket", nil, opts) -``` diff --git a/internal/kms/setup/setup.go b/internal/kms/setup/setup.go index 6f584245c..eee089e77 100644 --- a/internal/kms/setup/setup.go +++ b/internal/kms/setup/setup.go @@ -17,7 +17,6 @@ package setup import ( "context" - "encoding/base64" "fmt" "net/url" @@ -26,37 +25,12 @@ import ( "github.com/edgelesssys/constellation/v2/internal/kms/kms/azure" "github.com/edgelesssys/constellation/v2/internal/kms/kms/cluster" "github.com/edgelesssys/constellation/v2/internal/kms/kms/gcp" - "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/awss3" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/azureblob" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/gcs" "github.com/edgelesssys/constellation/v2/internal/kms/uri" ) -// Well known endpoints for KMS services. -const ( - AWSKMSURI = "kms://aws?keyPolicy=%s&kekID=%s" - AzureKMSURI = "kms://azure?tenantID=%s&clientID=%s&clientSecret=%s&name=%s&type=%s&kekID=%s" - GCPKMSURI = "kms://gcp?project=%s&location=%s&keyRing=%s&protectionLvl=%s&kekID=%s" - ClusterKMSURI = "kms://cluster-kms?key=%s&salt=%s" - AWSS3URI = "storage://aws?bucket=%s" - AzureBlobURI = "storage://azure?container=%s&connectionString=%s" - GCPStorageURI = "storage://gcp?projects=%s&bucket=%s" - NoStoreURI = "storage://no-store" -) - -// MasterSecret holds the master key and salt for deriving keys. -type MasterSecret struct { - Key []byte `json:"key"` - Salt []byte `json:"salt"` -} - -// EncodeToURI returns an URI encoding the master secret. -func (m *MasterSecret) EncodeToURI() string { - return fmt.Sprintf( - ClusterKMSURI, - base64.URLEncoding.EncodeToString(m.Key), - base64.URLEncoding.EncodeToString(m.Salt), - ) -} - // KMSInformation about an existing KMS. type KMSInformation struct { KMSURI string @@ -76,41 +50,41 @@ func KMS(ctx context.Context, storageURI, kmsURI string) (kms.CloudKMS, error) { // getStore creates a key store depending on the given parameters. func getStore(ctx context.Context, storageURI string) (kms.Storage, error) { - uri, err := url.Parse(storageURI) + url, err := url.Parse(storageURI) if err != nil { return nil, err } - if uri.Scheme != "storage" { - return nil, fmt.Errorf("invalid storage URI: invalid scheme: %s", uri.Scheme) + if url.Scheme != "storage" { + return nil, fmt.Errorf("invalid storage URI: invalid scheme: %s", url.Scheme) } - switch uri.Host { + switch url.Host { case "aws": - bucket, err := getAWSS3Config(uri) + cfg, err := uri.DecodeAWSS3ConfigFromURI(storageURI) if err != nil { return nil, err } - return storage.NewAWSS3Storage(ctx, bucket, nil) + return awss3.New(ctx, cfg) case "azure": - container, connString, err := getAzureBlobConfig(uri) + cfg, err := uri.DecodeAzureBlobConfigFromURI(storageURI) if err != nil { return nil, err } - return storage.NewAzureStorage(ctx, connString, container, nil) + return azureblob.New(ctx, cfg) case "gcp": - project, bucket, err := getGCPStorageConfig(uri) + cfg, err := uri.DecodeGoogleCloudStorageConfigFromURI(storageURI) if err != nil { return nil, err } - return storage.NewGoogleCloudStorage(ctx, project, bucket, nil) + return gcs.New(ctx, cfg) case "no-store": return nil, nil default: - return nil, fmt.Errorf("unknown storage type: %s", uri.Host) + return nil, fmt.Errorf("unknown storage type: %s", url.Host) } } @@ -157,68 +131,3 @@ func getKMS(ctx context.Context, kmsURI string, store kms.Storage) (kms.CloudKMS return nil, fmt.Errorf("unknown KMS type: %s", url.Host) } } - -type defaultPolicyProducer struct { - policy string -} - -func (p *defaultPolicyProducer) CreateKeyPolicy(keyID string) (string, error) { - return p.policy, nil -} - -func getAWSS3Config(uri *url.URL) (string, error) { - r, err := getConfig(uri.Query(), []string{"bucket"}) - return r[0], err -} - -func getAWSKMSConfig(uri *url.URL) (*defaultPolicyProducer, string, error) { - r, err := getConfig(uri.Query(), []string{"keyPolicy", "kekID"}) - if err != nil { - return nil, "", err - } - - if len(r) != 2 { - return nil, "", fmt.Errorf("expected 2 KmsURI args, got %d", len(r)) - } - - kekID, err := base64.URLEncoding.DecodeString(r[1]) - if err != nil { - return nil, "", fmt.Errorf("parsing kekID from kmsUri: %w", err) - } - - return &defaultPolicyProducer{policy: r[0]}, string(kekID), err -} - -func getAzureBlobConfig(uri *url.URL) (string, string, error) { - r, err := getConfig(uri.Query(), []string{"container", "connectionString"}) - if err != nil { - return "", "", err - } - return r[0], r[1], nil -} - -func getGCPStorageConfig(uri *url.URL) (string, string, error) { - r, err := getConfig(uri.Query(), []string{"project", "bucket"}) - return r[0], r[1], err -} - -// getConfig parses url query values, returning a map of the requested values. -// Returns an error if a key has no value. -// This function MUST always return a slice of the same length as len(keys). -func getConfig(values url.Values, keys []string) ([]string, error) { - res := make([]string, len(keys)) - - for idx, key := range keys { - val := values.Get(key) - if val == "" { - return res, fmt.Errorf("missing value for key: %q", key) - } - val, err := url.QueryUnescape(val) - if err != nil { - return res, fmt.Errorf("failed to unescape value for key: %q", key) - } - res[idx] = val - } - - return res, nil -} diff --git a/internal/kms/setup/setup_test.go b/internal/kms/setup/setup_test.go index 46d0e24b0..a92bbc0c1 100644 --- a/internal/kms/setup/setup_test.go +++ b/internal/kms/setup/setup_test.go @@ -8,18 +8,13 @@ package setup import ( "context" - "encoding/base64" - "fmt" - "net/url" "testing" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" "go.uber.org/goleak" ) -const constellationKekID = "Constellation" - func TestMain(m *testing.M) { goleak.VerifyTestMain(m, // https://github.com/census-instrumentation/opencensus-go/issues/1262 @@ -27,105 +22,6 @@ func TestMain(m *testing.M) { ) } -func TestGetStore(t *testing.T) { - testCases := map[string]struct { - uri string - wantErr bool - }{ - "no store": { - uri: NoStoreURI, - wantErr: false, - }, - "aws s3": { - uri: fmt.Sprintf(AWSS3URI, ""), - wantErr: true, - }, - "azure blob": { - uri: fmt.Sprintf(AzureBlobURI, "", ""), - wantErr: true, - }, - "gcp storage": { - uri: fmt.Sprintf(GCPStorageURI, "", ""), - wantErr: true, - }, - "unknown store": { - uri: "storage://unknown", - wantErr: true, - }, - "invalid scheme": { - uri: ClusterKMSURI, - wantErr: true, - }, - "not a url": { - uri: ":/123", - wantErr: true, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - - _, err := getStore(context.Background(), tc.uri) - if tc.wantErr { - assert.Error(err) - } else { - assert.NoError(err) - } - }) - } -} - -func TestGetKMS(t *testing.T) { - testCases := map[string]struct { - uri string - wantErr bool - }{ - "cluster kms": { - uri: fmt.Sprintf(ClusterKMSURI, base64.URLEncoding.EncodeToString([]byte("key")), base64.URLEncoding.EncodeToString([]byte("salt"))), - wantErr: false, - }, - "aws kms": { - uri: fmt.Sprintf(AWSKMSURI, "", ""), - wantErr: true, - }, - "azure kms": { - uri: fmt.Sprintf(AzureKMSURI, "", "", "", "", "", ""), - wantErr: true, - }, - "gcp kms": { - uri: fmt.Sprintf(GCPKMSURI, "", "", "", "", ""), - wantErr: true, - }, - "unknown kms": { - uri: "kms://unknown", - wantErr: true, - }, - "invalid scheme": { - uri: NoStoreURI, - wantErr: true, - }, - "not a url": { - uri: ":/123", - wantErr: true, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - - kms, err := getKMS(context.Background(), tc.uri, nil) - if tc.wantErr { - assert.Error(err) - } else { - assert.NoError(err) - assert.NotNil(kms) - } - }) - } -} - func TestSetUpKMS(t *testing.T) { assert := assert.New(t) @@ -133,98 +29,8 @@ func TestSetUpKMS(t *testing.T) { assert.Error(err) assert.Nil(kms) - masterSecret := MasterSecret{Key: []byte("key"), Salt: []byte("salt")} + masterSecret := uri.MasterSecret{Key: []byte("key"), Salt: []byte("salt")} kms, err = KMS(context.Background(), "storage://no-store", masterSecret.EncodeToURI()) assert.NoError(err) assert.NotNil(kms) } - -func TestGetAWSKMSConfig(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - policy := "{keyPolicy: keyPolicy}" - escapedPolicy := url.QueryEscape(policy) - kekID := base64.URLEncoding.EncodeToString([]byte(constellationKekID)) - uri, err := url.Parse(fmt.Sprintf(AWSKMSURI, escapedPolicy, kekID)) - require.NoError(err) - policyProducer, rKekID, err := getAWSKMSConfig(uri) - require.NoError(err) - keyPolicy, err := policyProducer.CreateKeyPolicy("") - require.NoError(err) - assert.Equal(policy, keyPolicy) - assert.Equal(constellationKekID, rKekID) -} - -func TestGetAzureBlobConfig(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - connStr := "DefaultEndpointsProtocol=https;AccountName=test;AccountKey=Q29uc3RlbGxhdGlvbg==;EndpointSuffix=core.windows.net" - escapedConnStr := url.QueryEscape(connStr) - container := "test" - uri, err := url.Parse(fmt.Sprintf(AzureBlobURI, container, escapedConnStr)) - require.NoError(err) - rContainer, rConnStr, err := getAzureBlobConfig(uri) - require.NoError(err) - assert.Equal(container, rContainer) - assert.Equal(connStr, rConnStr) -} - -func TestGetConfig(t *testing.T) { - const testURI = "test://config?name=test-name&data=test-data&value=test-value" - - testCases := map[string]struct { - uri string - keys []string - wantErr bool - }{ - "success": { - uri: testURI, - keys: []string{"name", "data", "value"}, - wantErr: false, - }, - "less keys than capture groups": { - uri: testURI, - keys: []string{"name", "data"}, - wantErr: false, - }, - "invalid regex": { - uri: testURI, - keys: []string{"name", "data", "test-value"}, - wantErr: true, - }, - "missing value": { - uri: "test://config?name=test-name&data=test-data&value", - keys: []string{"name", "data", "value"}, - wantErr: true, - }, - "more keys than expected": { - uri: testURI, - keys: []string{"name", "data", "value", "anotherValue"}, - wantErr: true, - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - uri, err := url.Parse(tc.uri) - require.NoError(err) - - res, err := getConfig(uri.Query(), tc.keys) - if tc.wantErr { - assert.Error(err) - assert.Len(res, len(tc.keys)) - } else { - assert.NoError(err) - require.Len(res, len(tc.keys)) - for i := range tc.keys { - assert.NotEmpty(res[i]) - } - } - }) - } -} diff --git a/internal/kms/storage/awss3storage.go b/internal/kms/storage/awss3/awss3.go similarity index 54% rename from internal/kms/storage/awss3storage.go rename to internal/kms/storage/awss3/awss3.go index d7ee53289..ee7d5d9a7 100644 --- a/internal/kms/storage/awss3storage.go +++ b/internal/kms/storage/awss3/awss3.go @@ -4,7 +4,8 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package storage +// Package awss3 implements a storage backend for the KMS using AWS S3: https://aws.amazon.com/s3/ +package awss3 import ( "bytes" @@ -14,9 +15,12 @@ import ( "io" awsconfig "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/edgelesssys/constellation/v2/internal/kms/config" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" ) type awsS3ClientAPI interface { @@ -25,44 +29,47 @@ type awsS3ClientAPI interface { 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 { +// Storage is an implementation of the Storage interface, storing keys in AWS S3 buckets. +type Storage struct { bucketID string client awsS3ClientAPI - optFns []func(*s3.Options) } -// NewAWSS3Storage creates a Storage client for AWS S3: https://aws.amazon.com/s3/ +// New creates a Storage client for AWS S3 using the provided config. // -// 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) +// See the AWS docs for more information: https://aws.amazon.com/s3/ +func New(ctx context.Context, cfg uri.AWSS3Config) (*Storage, error) { + clientCfg, err := awsconfig.LoadDefaultConfig( + ctx, + awsconfig.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(cfg.AccessKeyID, cfg.AccessKey, "")), + awsconfig.WithRegion(cfg.Region), + ) if err != nil { - return nil, err + return nil, fmt.Errorf("loading AWS S3 client config: %w", err) } - client := s3.NewFromConfig(cfg, optFns...) - store := &AWSS3Storage{client: client, bucketID: bucketID, optFns: optFns} + client := s3.NewFromConfig(clientCfg) + + store := &Storage{client: client, bucketID: cfg.Bucket} // Try to create new bucket, continue if bucket already exists - if err := store.createBucket(ctx, bucketID, cfg.Region, optFns...); err != nil { - return nil, err + if err := store.createBucket(ctx, cfg.Bucket, cfg.Region); err != nil { + return nil, fmt.Errorf("creating storage bucket: %w", 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) { +func (s *Storage) 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...) + output, err := s.client.GetObject(ctx, getObjectInput) if err != nil { var nsk *types.NoSuchKey if errors.As(err, &nsk) { - return nil, ErrDEKUnset + return nil, storage.ErrDEKUnset } return nil, fmt.Errorf("downloading DEK from storage: %w", err) } @@ -70,27 +77,28 @@ func (s *AWSS3Storage) Get(ctx context.Context, keyID string) ([]byte, error) { } // Put saves a DEK to AWS S3 Storage by key ID. -func (s *AWSS3Storage) Put(ctx context.Context, keyID string, data []byte) error { +func (s *Storage) 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 { + if _, err := s.client.PutObject(ctx, putObjectInput); 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 { +func (s *Storage) createBucket(ctx context.Context, bucketID, region string) error { createBucketInput := &s3.CreateBucketInput{ Bucket: &bucketID, CreateBucketConfiguration: &types.CreateBucketConfiguration{ LocationConstraint: types.BucketLocationConstraint(region), }, } - if _, err := s.client.CreateBucket(ctx, createBucketInput, optFns...); err != nil { + + if _, err := s.client.CreateBucket(ctx, createBucketInput); err != nil { var bne *types.BucketAlreadyExists var baowby *types.BucketAlreadyOwnedByYou if !(errors.As(err, &bne) || errors.As(err, &baowby)) { diff --git a/internal/kms/storage/awss3storage_test.go b/internal/kms/storage/awss3/awss3_test.go similarity index 94% rename from internal/kms/storage/awss3storage_test.go rename to internal/kms/storage/awss3/awss3_test.go index 0397fd1f7..5e3d3bfbb 100644 --- a/internal/kms/storage/awss3storage_test.go +++ b/internal/kms/storage/awss3/awss3_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package storage +package awss3 import ( "bytes" @@ -15,6 +15,7 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" "github.com/stretchr/testify/assert" ) @@ -71,7 +72,7 @@ func TestAWSS3Get(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - store := &AWSS3Storage{ + store := &Storage{ client: tc.client, } @@ -80,9 +81,9 @@ func TestAWSS3Get(t *testing.T) { assert.Error(err) if tc.unsetError { - assert.ErrorIs(err, ErrDEKUnset) + assert.ErrorIs(err, storage.ErrDEKUnset) } else { - assert.False(errors.Is(err, ErrDEKUnset)) + assert.False(errors.Is(err, storage.ErrDEKUnset)) } } else { @@ -111,7 +112,7 @@ func TestAWSS3Put(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - store := &AWSS3Storage{ + store := &Storage{ client: tc.client, } @@ -154,7 +155,7 @@ func TestAWSS3CreateBucket(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - store := &AWSS3Storage{ + store := &Storage{ client: tc.client, } diff --git a/internal/kms/storage/azureblob/azureblob.go b/internal/kms/storage/azureblob/azureblob.go new file mode 100644 index 000000000..e7e41424e --- /dev/null +++ b/internal/kms/storage/azureblob/azureblob.go @@ -0,0 +1,104 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package azureblob implements a storage backend for the KMS using Azure Blob Storage. +package azureblob + +import ( + "bytes" + "context" + "fmt" + "io" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "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/internal/kms/config" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" +) + +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) +} + +// Storage is an implementation of the Storage interface, storing keys in the Azure Blob Store. +type Storage struct { + client azureBlobAPI + container string +} + +// New initializes a storage client using Azure's Blob Storage using the provided config. +// +// See the Azure docs for more information: https://azure.microsoft.com/en-us/services/storage/blobs/ +func New(ctx context.Context, cfg uri.AzureBlobConfig) (*Storage, error) { + var creds azcore.TokenCredential + + creds, err := azidentity.NewClientSecretCredential(cfg.TenantID, cfg.ClientID, cfg.ClientSecret, nil) + if err != nil { + // Fallback: try to load default credentials + creds, err = azidentity.NewDefaultAzureCredential(nil) + if err != nil { + return nil, fmt.Errorf("invalid client-secret credentials. Trying to load default credentials: %w", err) + } + } + + client, err := azblob.NewClient(fmt.Sprintf("https://%s.blob.core.windows.net/", cfg.StorageAccount), creds, nil) + if err != nil { + return nil, fmt.Errorf("creating storage client: %w", err) + } + + s := &Storage{ + client: client, + container: cfg.Container, + } + + // 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 *Storage) Get(ctx context.Context, keyID string) ([]byte, error) { + res, err := s.client.DownloadStream(ctx, s.container, keyID, nil) + if err != nil { + if bloberror.HasCode(err, bloberror.BlobNotFound) { + return nil, storage.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 *Storage) Put(ctx context.Context, keyID string, encDEK []byte) error { + if _, err := s.client.UploadStream(ctx, s.container, keyID, bytes.NewReader(encDEK), nil); 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 *Storage) createContainerOrContinue(ctx context.Context) error { + _, err := s.client.CreateContainer(ctx, s.container, &azblob.CreateContainerOptions{ + Metadata: config.StorageTags, + }) + if (err == nil) || bloberror.HasCode(err, bloberror.ContainerAlreadyExists) { + return nil + } + + return fmt.Errorf("creating storage container: %w", err) +} diff --git a/internal/kms/storage/azurestorage_test.go b/internal/kms/storage/azureblob/azureblob_test.go similarity index 86% rename from internal/kms/storage/azurestorage_test.go rename to internal/kms/storage/azureblob/azureblob_test.go index 5660001a0..93a5f2987 100644 --- a/internal/kms/storage/azurestorage_test.go +++ b/internal/kms/storage/azureblob/azureblob_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package storage +package azureblob import ( "bytes" @@ -18,6 +18,7 @@ import ( "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/internal/kms/storage" "github.com/stretchr/testify/assert" ) @@ -45,11 +46,9 @@ func TestAzureGet(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - client := &AzureStorage{ - client: &tc.client, - connectionString: "test", - containerName: "test", - opts: &AzureOpts{}, + client := &Storage{ + client: &tc.client, + container: "test", } out, err := client.Get(context.Background(), "test-key") @@ -57,9 +56,9 @@ func TestAzureGet(t *testing.T) { assert.Error(err) if tc.unsetError { - assert.ErrorIs(err, ErrDEKUnset) + assert.ErrorIs(err, storage.ErrDEKUnset) } else { - assert.False(errors.Is(err, ErrDEKUnset)) + assert.False(errors.Is(err, storage.ErrDEKUnset)) } return } @@ -89,11 +88,9 @@ func TestAzurePut(t *testing.T) { testData := []byte{0x1, 0x2, 0x3} - client := &AzureStorage{ - client: &tc.client, - connectionString: "test", - containerName: "test", - opts: &AzureOpts{}, + client := &Storage{ + client: &tc.client, + container: "test", } err := client.Put(context.Background(), "test-key", testData) @@ -128,11 +125,9 @@ func TestCreateContainerOrContinue(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - client := &AzureStorage{ - client: &tc.client, - connectionString: "test", - containerName: "test", - opts: &AzureOpts{}, + client := &Storage{ + client: &tc.client, + container: "test", } err := client.createContainerOrContinue(context.Background()) diff --git a/internal/kms/storage/azurestorage.go b/internal/kms/storage/azurestorage.go deleted file mode 100644 index 4811b0027..000000000 --- a/internal/kms/storage/azurestorage.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -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/internal/kms/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) -} diff --git a/internal/kms/storage/gcloudstorage.go b/internal/kms/storage/gcloudstorage.go deleted file mode 100644 index 557921ad0..000000000 --- a/internal/kms/storage/gcloudstorage.go +++ /dev/null @@ -1,130 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package storage - -import ( - "context" - "errors" - "io" - - "cloud.google.com/go/storage" - "google.golang.org/api/option" -) - -type gcpStorageAPI interface { - Attrs(ctx context.Context, bucketName string) (*storage.BucketAttrs, error) - Close() error - CreateBucket(ctx context.Context, bucketName, projectID string, attrs *storage.BucketAttrs) error - NewWriter(ctx context.Context, bucketName, objectName string) io.WriteCloser - NewReader(ctx context.Context, bucketName, objectName string) (io.ReadCloser, error) -} - -type wrappedGCPClient struct { - *storage.Client -} - -func (c *wrappedGCPClient) Attrs(ctx context.Context, bucketName string) (*storage.BucketAttrs, error) { - return c.Client.Bucket(bucketName).Attrs(ctx) -} - -func (c *wrappedGCPClient) CreateBucket(ctx context.Context, bucketName, projectID string, attrs *storage.BucketAttrs) error { - return c.Client.Bucket(bucketName).Create(ctx, projectID, attrs) -} - -func (c *wrappedGCPClient) NewWriter(ctx context.Context, bucketName, objectName string) io.WriteCloser { - return c.Client.Bucket(bucketName).Object(objectName).NewWriter(ctx) -} - -func (c *wrappedGCPClient) NewReader(ctx context.Context, bucketName, objectName string) (io.ReadCloser, error) { - return c.Client.Bucket(bucketName).Object(objectName).NewReader(ctx) -} - -// GoogleCloudStorage is an implementation of the Storage interface, storing keys in Google Cloud Storage buckets. -type GoogleCloudStorage struct { - newClient func(ctx context.Context, opts ...option.ClientOption) (gcpStorageAPI, error) - projectID string - bucketName string - opts []option.ClientOption -} - -// NewGoogleCloudStorage creates a Storage client for Google Cloud Storage: https://cloud.google.com/storage/docs/ -// -// The parameter bucketOptions is optional, if not present default options will be created. -func NewGoogleCloudStorage(ctx context.Context, projectID, bucketName string, bucketOptions *storage.BucketAttrs, opts ...option.ClientOption) (*GoogleCloudStorage, error) { - s := &GoogleCloudStorage{ - newClient: gcpStorageClientFactory, - projectID: projectID, - bucketName: bucketName, - opts: opts, - } - - // Make sure the storage bucket exists, if not create it - if err := s.createContainerOrContinue(ctx, bucketOptions); err != nil { - return nil, err - } - - return s, nil -} - -// Get returns a DEK from Google Cloud Storage by key ID. -func (s *GoogleCloudStorage) Get(ctx context.Context, keyID string) ([]byte, error) { - client, err := s.newClient(ctx, s.opts...) - if err != nil { - return nil, err - } - defer client.Close() - - reader, err := client.NewReader(ctx, s.bucketName, keyID) - if err != nil { - if errors.Is(err, storage.ErrObjectNotExist) { - return nil, ErrDEKUnset - } - return nil, err - } - defer reader.Close() - - return io.ReadAll(reader) -} - -// Put saves a DEK to Google Cloud Storage by key ID. -func (s *GoogleCloudStorage) Put(ctx context.Context, keyID string, data []byte) error { - client, err := s.newClient(ctx, s.opts...) - if err != nil { - return err - } - defer client.Close() - - writer := client.NewWriter(ctx, s.bucketName, keyID) - defer writer.Close() - - _, err = writer.Write(data) - return err -} - -func (s *GoogleCloudStorage) createContainerOrContinue(ctx context.Context, bucketOptions *storage.BucketAttrs) error { - client, err := s.newClient(ctx, s.opts...) - if err != nil { - return err - } - defer client.Close() - - if _, err := client.Attrs(ctx, s.bucketName); errors.Is(err, storage.ErrBucketNotExist) { - return client.CreateBucket(ctx, s.bucketName, s.projectID, bucketOptions) - } else if err != nil { - return err - } - - return nil -} - -func gcpStorageClientFactory(ctx context.Context, opts ...option.ClientOption) (gcpStorageAPI, error) { - client, err := storage.NewClient(ctx, opts...) - if err != nil { - return nil, err - } - return &wrappedGCPClient{client}, nil -} diff --git a/internal/kms/storage/gcloudstorage_integration_test.go b/internal/kms/storage/gcloudstorage_integration_test.go deleted file mode 100644 index 99ce31fad..000000000 --- a/internal/kms/storage/gcloudstorage_integration_test.go +++ /dev/null @@ -1,114 +0,0 @@ -//go:build integration - -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package storage - -import ( - "context" - "io" - "os" - "testing" - "time" - - "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/api/option" -) - -const storageEmulator = "gcr.io/cloud-devrel-public-resources/storage-testbench" - -func TestGoogleCloudStorage(t *testing.T) { - assert := assert.New(t) - require := require.New(t) - - containerCtx := context.Background() - - // Set up the Storage Emulator - t.Log("Creating storage emulator...") - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - require.NoError(err) - emulator, err := setupEmulator(containerCtx, cli, storageEmulator) - require.NoError(err) - defer func() { _ = cli.ContainerStop(containerCtx, emulator.ID, nil) }() - - // Run the actual test - t.Setenv("STORAGE_EMULATOR_HOST", "localhost:9000") - - bucketName := "test-bucket" - projectName := "test-project" - - t.Log("Running test...") - ctx, cancel := context.WithTimeout(context.Background(), time.Second*50) - defer cancel() - store, err := NewGoogleCloudStorage(ctx, projectName, bucketName, nil, option.WithoutAuthentication()) - 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, ErrDEKUnset) -} - -func setupEmulator(ctx context.Context, cli *client.Client, imageName string) (container.ContainerCreateCreatedBody, error) { - reader, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions{}) - if err != nil { - return container.ContainerCreateCreatedBody{}, err - } - if _, err := io.Copy(os.Stdout, reader); err != nil { - return container.ContainerCreateCreatedBody{}, err - } - if err := reader.Close(); err != nil { - return container.ContainerCreateCreatedBody{}, err - } - - // the 3 true statements are necessary to attach later to the container log - containerConfig := &container.Config{ - Image: storageEmulator, - AttachStdout: true, - AttachStderr: true, - Tty: true, - } - emulator, err := cli.ContainerCreate(ctx, containerConfig, &container.HostConfig{NetworkMode: container.NetworkMode("host"), AutoRemove: true}, nil, nil, "google-cloud-storage-test") - if err != nil { - return emulator, err - } - if err := cli.ContainerStart(ctx, emulator.ID, types.ContainerStartOptions{}); err != nil { - return emulator, err - } - - logs, err := cli.ContainerLogs(ctx, emulator.ID, types.ContainerLogsOptions{ - ShowStdout: true, - Follow: true, - }) - if err != nil { - return emulator, err - } - go func() { _, _ = io.Copy(os.Stdout, logs) }() - return emulator, nil -} diff --git a/internal/kms/storage/gcs/gcs.go b/internal/kms/storage/gcs/gcs.go new file mode 100644 index 000000000..ca53bf55f --- /dev/null +++ b/internal/kms/storage/gcs/gcs.go @@ -0,0 +1,131 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package gcs implements a storage backend for the KMS using Google Cloud Storage (GCS). +package gcs + +import ( + "context" + "errors" + "io" + + gcstorage "cloud.google.com/go/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" + "google.golang.org/api/option" +) + +type gcpStorageAPI interface { + Attrs(ctx context.Context, bucketName string) (*gcstorage.BucketAttrs, error) + Close() error + CreateBucket(ctx context.Context, bucketName, projectID string, attrs *gcstorage.BucketAttrs) error + NewWriter(ctx context.Context, bucketName, objectName string) io.WriteCloser + NewReader(ctx context.Context, bucketName, objectName string) (io.ReadCloser, error) +} + +type wrappedGCPClient struct { + *gcstorage.Client +} + +func (c *wrappedGCPClient) Attrs(ctx context.Context, bucketName string) (*gcstorage.BucketAttrs, error) { + return c.Client.Bucket(bucketName).Attrs(ctx) +} + +func (c *wrappedGCPClient) CreateBucket(ctx context.Context, bucketName, projectID string, attrs *gcstorage.BucketAttrs) error { + return c.Client.Bucket(bucketName).Create(ctx, projectID, attrs) +} + +func (c *wrappedGCPClient) NewWriter(ctx context.Context, bucketName, objectName string) io.WriteCloser { + return c.Client.Bucket(bucketName).Object(objectName).NewWriter(ctx) +} + +func (c *wrappedGCPClient) NewReader(ctx context.Context, bucketName, objectName string) (io.ReadCloser, error) { + return c.Client.Bucket(bucketName).Object(objectName).NewReader(ctx) +} + +// Storage is an implementation of the Storage interface, storing keys in Google Cloud Storage buckets. +type Storage struct { + newClient func(ctx context.Context) (gcpStorageAPI, error) + bucketName string +} + +// New creates a Storage client for Google Cloud Storage using the provided config. +// +// See the Google docs for more information: https://cloud.google.com/storage/docs/ +func New(ctx context.Context, cfg uri.GoogleCloudStorageConfig) (*Storage, error) { + s := &Storage{ + newClient: newGCPStorageClientFactory(cfg.CredentialsPath), + bucketName: cfg.Bucket, + } + + // Make sure the storage bucket exists, if not create it + if err := s.createContainerOrContinue(ctx, cfg.ProjectID); err != nil { + return nil, err + } + + return s, nil +} + +// Get returns a DEK from Google Cloud Storage by key ID. +func (s *Storage) Get(ctx context.Context, keyID string) ([]byte, error) { + client, err := s.newClient(ctx) + if err != nil { + return nil, err + } + defer client.Close() + + reader, err := client.NewReader(ctx, s.bucketName, keyID) + if err != nil { + if errors.Is(err, gcstorage.ErrObjectNotExist) { + return nil, storage.ErrDEKUnset + } + return nil, err + } + defer reader.Close() + + return io.ReadAll(reader) +} + +// Put saves a DEK to Google Cloud Storage by key ID. +func (s *Storage) Put(ctx context.Context, keyID string, data []byte) error { + client, err := s.newClient(ctx) + if err != nil { + return err + } + defer client.Close() + + writer := client.NewWriter(ctx, s.bucketName, keyID) + defer writer.Close() + + _, err = writer.Write(data) + return err +} + +func (s *Storage) createContainerOrContinue(ctx context.Context, projectID string) error { + client, err := s.newClient(ctx) + if err != nil { + return err + } + defer client.Close() + + if _, err := client.Attrs(ctx, s.bucketName); errors.Is(err, gcstorage.ErrBucketNotExist) { + return client.CreateBucket(ctx, s.bucketName, projectID, nil) + } else if err != nil { + return err + } + + return nil +} + +func newGCPStorageClientFactory(credPath string) func(context.Context) (gcpStorageAPI, error) { + return func(ctx context.Context) (gcpStorageAPI, error) { + client, err := gcstorage.NewClient(ctx, option.WithCredentialsFile(credPath)) + if err != nil { + return nil, err + } + return &wrappedGCPClient{client}, nil + } +} diff --git a/internal/kms/storage/gcloudstorage_test.go b/internal/kms/storage/gcs/gcs_test.go similarity index 85% rename from internal/kms/storage/gcloudstorage_test.go rename to internal/kms/storage/gcs/gcs_test.go index e226a35b9..552cee99f 100644 --- a/internal/kms/storage/gcloudstorage_test.go +++ b/internal/kms/storage/gcs/gcs_test.go @@ -4,7 +4,7 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package storage +package gcs import ( "bytes" @@ -13,9 +13,9 @@ import ( "io" "testing" - "cloud.google.com/go/storage" + gcstorage "cloud.google.com/go/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" "github.com/stretchr/testify/assert" - "google.golang.org/api/option" ) type stubGCPStorageAPI struct { @@ -28,19 +28,19 @@ type stubGCPStorageAPI struct { writer *stubWriteCloser } -func (s *stubGCPStorageAPI) stubClientFactory(ctx context.Context, opts ...option.ClientOption) (gcpStorageAPI, error) { +func (s *stubGCPStorageAPI) stubClientFactory(ctx context.Context) (gcpStorageAPI, error) { return s, s.newClientErr } -func (s *stubGCPStorageAPI) Attrs(ctx context.Context, bucketName string) (*storage.BucketAttrs, error) { - return &storage.BucketAttrs{}, s.attrsErr +func (s *stubGCPStorageAPI) Attrs(ctx context.Context, bucketName string) (*gcstorage.BucketAttrs, error) { + return &gcstorage.BucketAttrs{}, s.attrsErr } func (s *stubGCPStorageAPI) Close() error { return nil } -func (s *stubGCPStorageAPI) CreateBucket(ctx context.Context, bucketName, projectID string, attrs *storage.BucketAttrs) error { +func (s *stubGCPStorageAPI) CreateBucket(ctx context.Context, bucketName, projectID string, attrs *gcstorage.BucketAttrs) error { s.createBucketCalled = true return s.createBucketErr } @@ -88,7 +88,7 @@ func TestGCPGet(t *testing.T) { wantErr: true, }, "ErrObjectNotExist error": { - client: &stubGCPStorageAPI{newReaderErr: storage.ErrObjectNotExist}, + client: &stubGCPStorageAPI{newReaderErr: gcstorage.ErrObjectNotExist}, unsetError: true, wantErr: true, }, @@ -98,9 +98,8 @@ func TestGCPGet(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - client := &GoogleCloudStorage{ + client := &Storage{ newClient: tc.client.stubClientFactory, - projectID: "test", bucketName: "test", } @@ -109,9 +108,9 @@ func TestGCPGet(t *testing.T) { assert.Error(err) if tc.unsetError { - assert.ErrorIs(err, ErrDEKUnset) + assert.ErrorIs(err, storage.ErrDEKUnset) } else { - assert.False(errors.Is(err, ErrDEKUnset)) + assert.False(errors.Is(err, storage.ErrDEKUnset)) } } else { @@ -155,9 +154,8 @@ func TestGCPPut(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - client := &GoogleCloudStorage{ + client := &Storage{ newClient: tc.client.stubClientFactory, - projectID: "test", bucketName: "test", } testData := []byte{0x1, 0x2, 0x3} @@ -184,7 +182,7 @@ func TestGCPCreateContainerOrContinue(t *testing.T) { client: &stubGCPStorageAPI{}, }, "container does not exist": { - client: &stubGCPStorageAPI{attrsErr: storage.ErrBucketNotExist}, + client: &stubGCPStorageAPI{attrsErr: gcstorage.ErrBucketNotExist}, createNewBucket: true, }, "creating client fails": { @@ -197,7 +195,7 @@ func TestGCPCreateContainerOrContinue(t *testing.T) { }, "CreateBucket fails": { client: &stubGCPStorageAPI{ - attrsErr: storage.ErrBucketNotExist, + attrsErr: gcstorage.ErrBucketNotExist, createBucketErr: someErr, }, wantErr: true, @@ -208,13 +206,12 @@ func TestGCPCreateContainerOrContinue(t *testing.T) { t.Run(name, func(t *testing.T) { assert := assert.New(t) - client := &GoogleCloudStorage{ + client := &Storage{ newClient: tc.client.stubClientFactory, - projectID: "test", bucketName: "test", } - err := client.createContainerOrContinue(context.Background(), nil) + err := client.createContainerOrContinue(context.Background(), "project") if tc.wantErr { assert.Error(err) } else { diff --git a/internal/kms/storage/memfs/memfs.go b/internal/kms/storage/memfs/memfs.go new file mode 100644 index 000000000..7484b679e --- /dev/null +++ b/internal/kms/storage/memfs/memfs.go @@ -0,0 +1,44 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +// Package memfs implements a storage backend for the KMS that stores keys in memory only. +// This package should be used for testing only. +package memfs + +import ( + "context" + + "github.com/edgelesssys/constellation/v2/internal/kms/storage" +) + +// Storage is the standard implementation of the Storage interface, storing keys in memory only. +type Storage struct { + dekPool map[string][]byte +} + +// New creates and initializes a new Storage object. +func New() *Storage { + s := &Storage{ + dekPool: make(map[string][]byte), + } + + return s +} + +// Get returns a DEK from Storage by key ID. +func (s *Storage) Get(ctx context.Context, keyID string) ([]byte, error) { + encDEK, ok := s.dekPool[keyID] + if ok { + return encDEK, nil + } + return nil, storage.ErrDEKUnset +} + +// Put saves a DEK to Storage by key ID. +func (s *Storage) Put(ctx context.Context, keyID string, encDEK []byte) error { + s.dekPool[keyID] = encDEK + return nil +} diff --git a/internal/kms/storage/memfsstroage_test.go b/internal/kms/storage/memfs/memfs_test.go similarity index 62% rename from internal/kms/storage/memfsstroage_test.go rename to internal/kms/storage/memfs/memfs_test.go index e01571856..9fe33362b 100644 --- a/internal/kms/storage/memfsstroage_test.go +++ b/internal/kms/storage/memfs/memfs_test.go @@ -4,12 +4,13 @@ Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ -package storage +package memfs import ( "context" "testing" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" "github.com/stretchr/testify/assert" "go.uber.org/goleak" ) @@ -24,28 +25,28 @@ func TestMain(m *testing.M) { func TestMemMapStorage(t *testing.T) { assert := assert.New(t) - storage := NewMemMapStorage() + store := New() testDEK1 := []byte("test DEK") testDEK2 := []byte("more test DEK") ctx := context.Background() // request unset value - _, err := storage.Get(ctx, "test:input") - assert.ErrorIs(err, ErrDEKUnset) + _, err := store.Get(ctx, "test:input") + assert.ErrorIs(err, storage.ErrDEKUnset) // test Put method - assert.NoError(storage.Put(ctx, "volume01", testDEK1)) - assert.NoError(storage.Put(ctx, "volume02", testDEK2)) + assert.NoError(store.Put(ctx, "volume01", testDEK1)) + assert.NoError(store.Put(ctx, "volume02", testDEK2)) // make sure values have been set - val, err := storage.Get(ctx, "volume01") + val, err := store.Get(ctx, "volume01") assert.NoError(err) assert.Equal(testDEK1, val) - val, err = storage.Get(ctx, "volume02") + val, err = store.Get(ctx, "volume02") assert.NoError(err) assert.Equal(testDEK2, val) - _, err = storage.Get(ctx, "invalid:key") - assert.ErrorIs(err, ErrDEKUnset) + _, err = store.Get(ctx, "invalid:key") + assert.ErrorIs(err, storage.ErrDEKUnset) } diff --git a/internal/kms/storage/memfsstorage.go b/internal/kms/storage/memfsstorage.go deleted file mode 100644 index eb8e2b992..000000000 --- a/internal/kms/storage/memfsstorage.go +++ /dev/null @@ -1,38 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package storage - -import "context" - -// MemMapStorage is the standard implementation of the Storage interface, storing keys in memory only. -type MemMapStorage struct { - dekPool map[string][]byte -} - -// NewMemMapStorage creates and initialises a new MemMapStorage object. -func NewMemMapStorage() *MemMapStorage { - s := &MemMapStorage{ - dekPool: make(map[string][]byte), - } - - return s -} - -// Get returns a DEK from MemMapStorage by key ID. -func (s *MemMapStorage) Get(ctx context.Context, keyID string) ([]byte, error) { - encDEK, ok := s.dekPool[keyID] - if ok { - return encDEK, nil - } - return nil, ErrDEKUnset -} - -// Put saves a DEK to MemMapStorage by key ID. -func (s *MemMapStorage) Put(ctx context.Context, keyID string, encDEK []byte) error { - s.dekPool[keyID] = encDEK - return nil -} diff --git a/internal/kms/test/aws_test.go b/internal/kms/test/aws_test.go index 1a7ca929e..184d045a0 100644 --- a/internal/kms/test/aws_test.go +++ b/internal/kms/test/aws_test.go @@ -11,7 +11,6 @@ package test import ( "context" "flag" - "strings" "testing" "time" @@ -19,9 +18,9 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/edgelesssys/constellation/v2/internal/kms/kms/aws" - "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/awss3" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/memfs" "github.com/edgelesssys/constellation/v2/internal/kms/uri" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -29,53 +28,41 @@ func TestAwsStorage(t *testing.T) { if !*runAwsStorage { t.Skip("Skipping AWS storage test") } - assert := assert.New(t) + if *awsAccessKey == "" || *awsAccessKeyID == "" || *awsBucket == "" || *awsRegion == "" { + flag.Usage() + t.Fatal("Required flags not set: --aws-access-key, --aws-access-key-id, --aws-bucket, --aws-region") + } 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) {}) + cfg := uri.AWSS3Config{ + Bucket: *awsBucket, + AccessKeyID: *awsAccessKeyID, + AccessKey: *awsAccessKey, + Region: *awsRegion, + } + store, err := awss3.New(ctx, cfg) require.NoError(err) - testDEK1 := []byte("test DEK") - testDEK2 := []byte("more test DEK") + runStorageTest(t, store) - // 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) {}) + cleanUpBucket(ctx, require, *awsBucket, *awsRegion) } -func cleanUpBucket(ctx context.Context, require *require.Assertions, bucketID string, optFns ...func(*s3.Options)) { - cfg, err := config.LoadDefaultConfig(ctx) +func cleanUpBucket(ctx context.Context, require *require.Assertions, bucketID, awsRegion string) { + cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(awsRegion)) require.NoError(err) - client := s3.NewFromConfig(cfg, optFns...) + client := s3.NewFromConfig(cfg) // List all objects of the bucket listObjectsInput := &s3.ListObjectsV2Input{ Bucket: &bucketID, } - output, _ := client.ListObjectsV2(ctx, listObjectsInput) + output, err := client.ListObjectsV2(ctx, listObjectsInput) + require.NoError(err) var objects []string var i int32 for i = 0; i < output.KeyCount; i++ { @@ -117,7 +104,7 @@ func TestAwsKms(t *testing.T) { require := require.New(t) - store := storage.NewMemMapStorage() + store := memfs.New() ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() diff --git a/internal/kms/test/azure_test.go b/internal/kms/test/azure_test.go index 6a9c0e945..855b4dd54 100644 --- a/internal/kms/test/azure_test.go +++ b/internal/kms/test/azure_test.go @@ -15,9 +15,9 @@ import ( "time" "github.com/edgelesssys/constellation/v2/internal/kms/kms/azure" - "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/azureblob" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/memfs" "github.com/edgelesssys/constellation/v2/internal/kms/uri" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -25,31 +25,26 @@ func TestAzureStorage(t *testing.T) { if !*runAzStorage { t.Skip("Skipping Azure storage test") } - if *azConnectionString == "" || *azContainer == "" { + if *azStorageAccount == "" || *azContainer == "" || *azClientID == "" || *azClientSecret == "" || *azTenantID == "" { flag.Usage() - t.Fatal("Required flags not set: --az-connection-string, --az-container") + t.Fatal("Required flags not set: --az-storage-account, --az-container, --az-tenant-id, --az-client-id, --az-client-secret") } - - assert := assert.New(t) require := require.New(t) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - store, err := storage.NewAzureStorage(ctx, *azConnectionString, *azContainer, nil) + + cfg := uri.AzureBlobConfig{ + StorageAccount: *azStorageAccount, + Container: *azContainer, + TenantID: *azTenantID, + ClientID: *azClientID, + ClientSecret: *azClientSecret, + } + store, err := azureblob.New(ctx, cfg) require.NoError(err) - testData := []byte("Constellation test data") - testName := "constellation-test" - - err = store.Put(ctx, testName, testData) - assert.NoError(err) - - got, err := store.Get(ctx, testName) - assert.NoError(err) - assert.Equal(testData, got) - - _, err = store.Get(ctx, addSuffix("does-not-exist")) - assert.ErrorIs(err, storage.ErrDEKUnset) + runStorageTest(t, store) } func TestAzureKeyKMS(t *testing.T) { @@ -63,7 +58,7 @@ func TestAzureKeyKMS(t *testing.T) { } require := require.New(t) - store := storage.NewMemMapStorage() + store := memfs.New() ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -92,7 +87,7 @@ func TestAzureKeyHSM(t *testing.T) { } require := require.New(t) - store := storage.NewMemMapStorage() + store := memfs.New() ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() diff --git a/internal/kms/test/gcp_test.go b/internal/kms/test/gcp_test.go index 982a97e17..35162e0f1 100644 --- a/internal/kms/test/gcp_test.go +++ b/internal/kms/test/gcp_test.go @@ -15,9 +15,9 @@ import ( "time" "github.com/edgelesssys/constellation/v2/internal/kms/kms/gcp" - "github.com/edgelesssys/constellation/v2/internal/kms/storage" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/gcs" + "github.com/edgelesssys/constellation/v2/internal/kms/storage/memfs" "github.com/edgelesssys/constellation/v2/internal/kms/uri" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -31,7 +31,7 @@ func TestGCPKMS(t *testing.T) { } require := require.New(t) - store := storage.NewMemMapStorage() + store := memfs.New() ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() @@ -53,29 +53,22 @@ func TestGcpStorage(t *testing.T) { if !*runGcpStorage { t.Skip("Skipping Google Storage test") } - - if *gcpProjectID == "" || *gcpBucket == "" { + if *gcpProjectID == "" || *gcpBucket == "" || *gcpCredentialsPath == "" { flag.Usage() t.Fatal("Required flags not set: --gcp-project, --gcp-bucket ") } - - assert := assert.New(t) + require := require.New(t) ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) defer cancel() - store, err := storage.NewGoogleCloudStorage(ctx, *gcpProjectID, *gcpBucket, nil) - assert.NoError(err) - testData := []byte("Constellation test data") - testName := "constellation-test" + cfg := uri.GoogleCloudStorageConfig{ + CredentialsPath: *gcpCredentialsPath, + ProjectID: *gcpProjectID, + Bucket: *gcpBucket, + } + store, err := gcs.New(ctx, cfg) + require.NoError(err) - err = store.Put(ctx, testName, testData) - assert.NoError(err) - - got, err := store.Get(ctx, testName) - assert.NoError(err) - assert.Equal(testData, got) - - _, err = store.Get(ctx, addSuffix("does-not-exist")) - assert.ErrorIs(err, storage.ErrDEKUnset) + runStorageTest(t, store) } diff --git a/internal/kms/test/integration_test.go b/internal/kms/test/integration_test.go index 529cb8372..bd6dccd80 100644 --- a/internal/kms/test/integration_test.go +++ b/internal/kms/test/integration_test.go @@ -19,6 +19,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/kms/config" "github.com/edgelesssys/constellation/v2/internal/kms/kms" + "github.com/edgelesssys/constellation/v2/internal/kms/storage" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -29,18 +30,19 @@ var ( runAwsStorage = flag.Bool("aws-storage", false, "set to run AWS S3 Bucket Storage test") runAwsKms = flag.Bool("aws-kms", false, "set to run AWS KMS test") awsRegion = flag.String("aws-region", "us-east-1", "Region to use for AWS tests. Required for AWS KMS test.") - awsAccessKeyID = flag.String("aws-access-key-id", "", "ID of the Access key to use for AWS tests. Required for AWS KMS test.") - awsAccessKey = flag.String("aws-access-key", "", "Access key to use for AWS tests. Required for AWS KMS test.") + awsAccessKeyID = flag.String("aws-access-key-id", "", "ID of the Access key to use for AWS tests. Required for AWS KMS and storage test.") + awsAccessKey = flag.String("aws-access-key", "", "Access key to use for AWS tests. Required for AWS KMS and storage test.") + awsBucket = flag.String("aws-bucket", "", "Name of the S3 bucket to use for AWS storage test. Required for AWS storage test.") - azConnectionString = flag.String("az-storage-conn", "", "Connection string for Azure storage account. Required for Azure storage test.") - azContainer = flag.String("az-container", "constellation-test-storage", "Container to save test data to. Required for Azure storage test.") - runAzStorage = flag.Bool("az-storage", false, "set to run Azure Storage test") - runAzKms = flag.Bool("az-kms", false, "set to run Azure KMS test") - runAzHsm = flag.Bool("az-hsm", false, "set to run Azure HSM test") - azVaultName = flag.String("az-vault-name", "", "Name of the Azure Key Vault to use. Required for Azure KMS/HSM test.") - azTenantID = flag.String("az-tenant-id", "", "Tenant ID to use for Azure tests. Required for Azure KMS/HSM test.") - azClientID = flag.String("az-client-id", "", "Client ID to use for Azure tests. Required for Azure KMS/HSM test.") - azClientSecret = flag.String("az-client-secret", "", "Client secret to use for Azure tests. Required for Azure KMS/HSM test.") + runAzStorage = flag.Bool("az-storage", false, "set to run Azure Storage test") + runAzKms = flag.Bool("az-kms", false, "set to run Azure KMS test") + runAzHsm = flag.Bool("az-hsm", false, "set to run Azure HSM test") + azVaultName = flag.String("az-vault-name", "", "Name of the Azure Key Vault to use. Required for Azure KMS/HSM and storage test.") + azTenantID = flag.String("az-tenant-id", "", "Tenant ID to use for Azure tests. Required for Azure KMS/HSM and storage test.") + azClientID = flag.String("az-client-id", "", "Client ID to use for Azure tests. Required for Azure KMS/HSM and storage test.") + azClientSecret = flag.String("az-client-secret", "", "Client secret to use for Azure tests. Required for Azure KMS/HSM and storage test.") + azStorageAccount = flag.String("az-storage-account", "", "Service URL for Azure storage account. Required for Azure storage test.") + azContainer = flag.String("az-container", "constellation-test-storage", "Container to save test data to. Required for Azure storage test.") runGcpKms = flag.Bool("gcp-kms", false, "set to run Google KMS test") runGcpStorage = flag.Bool("gcp-storage", false, "set to run Google Storage test") @@ -81,6 +83,27 @@ func runKMSTest(t *testing.T, kms kms.CloudKMS) { t.Logf("DEK 3: %x\n", res3) } +func runStorageTest(t *testing.T, store kms.Storage) { + assert := assert.New(t) + require := require.New(t) + + testData := []byte("Constellation test data") + testName := "constellation-test" + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + err := store.Put(ctx, testName, testData) + require.NoError(err) + + got, err := store.Get(ctx, testName) + require.NoError(err) + assert.Equal(testData, got) + + _, err = store.Get(ctx, addSuffix("does-not-exist")) + assert.ErrorIs(err, storage.ErrDEKUnset) +} + func addSuffix(s string) string { rand := rand.New(rand.NewSource(time.Now().UnixNano())) letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") diff --git a/internal/kms/uri/uri.go b/internal/kms/uri/uri.go index ace889fb1..6a3de8887 100644 --- a/internal/kms/uri/uri.go +++ b/internal/kms/uri/uri.go @@ -44,19 +44,23 @@ type VaultBaseURL string // Well known endpoints for KMS services. const ( - awsKMSURI = "kms://aws?region=%s&accessKeyID=%s&acccessKey=%skeyName=%s" + awsKMSURI = "kms://aws?region=%s&accessKeyID=%s&accessKey=%s&keyName=%s" azureKMSURI = "kms://azure?tenantID=%s&clientID=%s&clientSecret=%s&vaultName=%s&vaultType=%s&keyName=%s" - gcpKMSURI = "kms://gcp?project=%s&location=%s&keyRing=%s&credentialsPath=%s&keyName=%s" + gcpKMSURI = "kms://gcp?projectID=%s&location=%s&keyRing=%s&credentialsPath=%s&keyName=%s" clusterKMSURI = "kms://cluster-kms?key=%s&salt=%s" - awsS3URI = "storage://aws?bucket=%s" - azureBlobURI = "storage://azure?container=%s&connectionString=%s" - gcpStorageURI = "storage://gcp?projects=%s&bucket=%s" - NoStoreURI = "storage://no-store" + awsS3URI = "storage://aws?bucket=%s®ion=%s&accessKeyID=%s&accessKey=%s" + azureBlobURI = "storage://azure?account=%s&container=%s&tenantID=%s&clientID=%s&clientSecret=%s" + gcpStorageURI = "storage://gcp?projectID=%s&bucket=%s&credentialsPath=%s" + // NoStoreURI is a URI that indicates that no storage is used. + // Should only be used with cluster KMS. + NoStoreURI = "storage://no-store" ) // MasterSecret holds the master key and salt for deriving keys. type MasterSecret struct { - Key []byte `json:"key"` + // Key is the secret value used in HKDF to derive keys. + Key []byte `json:"key"` + // Salt is the salt used in HKDF to derive keys. Salt []byte `json:"salt"` } @@ -100,10 +104,14 @@ func DecodeMasterSecretFromURI(uri string) (MasterSecret, error) { // AWSConfig is the configuration to authenticate with AWS KMS. type AWSConfig struct { - KeyName string - Region string + // KeyName is the name of the key in AWS KMS. + KeyName string + // Region is the region of the key in AWS KMS. + Region string + // AccessKeyID is the ID of the access key used for authentication with the AWS API. AccessKeyID string - AccessKey string + // AccessKey is the secret value used for authentication with the AWS API. + AccessKey string } // DecodeAWSConfigFromURI decodes an AWS configuration from a URI. @@ -157,14 +165,84 @@ func (c AWSConfig) EncodeToURI() string { ) } +// AWSS3Config is the configuration to authenticate with AWS S3 storage bucket. +type AWSS3Config struct { + // Bucket is the name of the S3 storage bucket to use. + Bucket string + // Region is the region storage bucket is located in. + Region string + // AccessKeyID is the ID of the access key used for authentication with the AWS API. + AccessKeyID string + // AccessKey is the secret value used for authentication with the AWS API. + AccessKey string +} + +// DecodeAWSS3ConfigFromURI decodes an S3 configuration from a URI. +func DecodeAWSS3ConfigFromURI(uri string) (AWSS3Config, error) { + u, err := url.Parse(uri) + if err != nil { + return AWSS3Config{}, err + } + + if u.Scheme != "storage" { + return AWSS3Config{}, fmt.Errorf("invalid scheme: %q", u.Scheme) + } + if u.Host != "aws" { + return AWSS3Config{}, fmt.Errorf("invalid host: %q", u.Host) + } + + q := u.Query() + bucket, err := getQueryParameter(q, "bucket") + if err != nil { + return AWSS3Config{}, err + } + region, err := getQueryParameter(q, "region") + if err != nil { + return AWSS3Config{}, err + } + accessKeyID, err := getQueryParameter(q, "accessKeyID") + if err != nil { + return AWSS3Config{}, err + } + accessKey, err := getQueryParameter(q, "accessKey") + if err != nil { + return AWSS3Config{}, err + } + + return AWSS3Config{ + Bucket: bucket, + Region: region, + AccessKeyID: accessKeyID, + AccessKey: accessKey, + }, nil +} + +// EncodeToURI returns a URI encoding the S3 configuration. +func (s AWSS3Config) EncodeToURI() string { + return fmt.Sprintf( + awsS3URI, + url.QueryEscape(s.Bucket), + url.QueryEscape(s.Region), + url.QueryEscape(s.AccessKeyID), + url.QueryEscape(s.AccessKey), + ) +} + // AzureConfig is the configuration to authenticate with Azure Key Vault. type AzureConfig struct { - TenantID string - ClientID string + // TenantID of the Azure Active Directory the Key Vault is located in. + TenantID string + // ClientID is the ID of the managed identity used to authenticate with the Azure API. + ClientID string + // ClientSecret is the secret-value/password of the managed identity used to authenticate with the Azure API. ClientSecret string - KeyName string - VaultName string - VaultType VaultBaseURL + // KeyName is the name of the key in Azure Key Vault. + KeyName string + // VaultName is the name of the vault. + VaultName string + // VaultType is the type of the vault. + // This defines whether or not the Key Vault is a managed HSM. + VaultType VaultBaseURL } // DecodeAzureConfigFromURI decodes an Azure configuration from a URI. @@ -230,14 +308,89 @@ func (a AzureConfig) EncodeToURI() string { ) } +// AzureBlobConfig is the configuration to authenticate with Azure Blob storage. +type AzureBlobConfig struct { + // StorageAccount is the name of the storage account to use. + StorageAccount string + // Container is the name of the container to use. + Container string + // TenantID of the Azure Active Directory the Key Vault is located in. + TenantID string + // ClientID is the ID of the managed identity used to authenticate with the Azure API. + ClientID string + // ClientSecret is the secret-value/password of the managed identity used to authenticate with the Azure API. + ClientSecret string +} + +// DecodeAzureBlobConfigFromURI decodes an Azure Blob configuration from a URI. +func DecodeAzureBlobConfigFromURI(uri string) (AzureBlobConfig, error) { + u, err := url.Parse(uri) + if err != nil { + return AzureBlobConfig{}, err + } + + if u.Scheme != "storage" { + return AzureBlobConfig{}, fmt.Errorf("invalid scheme: %q", u.Scheme) + } + if u.Host != "azure" { + return AzureBlobConfig{}, fmt.Errorf("invalid host: %q", u.Host) + } + + q := u.Query() + storageAccount, err := getQueryParameter(q, "account") + if err != nil { + return AzureBlobConfig{}, err + } + container, err := getQueryParameter(q, "container") + if err != nil { + return AzureBlobConfig{}, err + } + tenantID, err := getQueryParameter(q, "tenantID") + if err != nil { + return AzureBlobConfig{}, err + } + clientID, err := getQueryParameter(q, "clientID") + if err != nil { + return AzureBlobConfig{}, err + } + clientSecret, err := getQueryParameter(q, "clientSecret") + if err != nil { + return AzureBlobConfig{}, err + } + + return AzureBlobConfig{ + StorageAccount: storageAccount, + Container: container, + TenantID: tenantID, + ClientID: clientID, + ClientSecret: clientSecret, + }, nil +} + +// EncodeToURI returns a URI encoding the Azure Blob configuration. +func (a AzureBlobConfig) EncodeToURI() string { + return fmt.Sprintf( + azureBlobURI, + url.QueryEscape(a.StorageAccount), + url.QueryEscape(a.Container), + url.QueryEscape(a.TenantID), + url.QueryEscape(a.ClientID), + url.QueryEscape(a.ClientSecret), + ) +} + // GCPConfig is the configuration to authenticate with GCP KMS. type GCPConfig struct { - // CredentialsPath is the path to a credentials file of a service account used to authorize against the KMS. + // CredentialsPath is the path to a credentials file of a service account used to authorize against the GCP API. CredentialsPath string - ProjectID string - Location string - KeyRing string - KeyName string + // ProjectID is the name of the GCP project the KMS is located in. + ProjectID string + // Location is the location of the KMS. + Location string + // KeyRing is the name of the keyring. + KeyRing string + // KeyName is the name of the key in the GCP KMS. + KeyName string } // DecodeGCPConfigFromURI decodes a GCP configuration from a URI. @@ -255,7 +408,7 @@ func DecodeGCPConfigFromURI(uri string) (GCPConfig, error) { } q := u.Query() - credentials, err := getQueryParameter(q, "credentials") + credentials, err := getQueryParameter(q, "credentialsPath") if err != nil { return GCPConfig{}, err } @@ -297,6 +450,61 @@ func (g GCPConfig) EncodeToURI() string { ) } +// GoogleCloudStorageConfig is the configuration to authenticate with Google Cloud Storage. +type GoogleCloudStorageConfig struct { + // CredentialsPath is the path to a credentials file of a service account used to authorize against the GCP API. + CredentialsPath string + // ProjectID is the name of the GCP project the storage bucket is located in. + ProjectID string + // Bucket is the name of the bucket to use. + Bucket string +} + +// DecodeGoogleCloudStorageConfigFromURI decodes a Google Cloud Storage configuration from a URI. +func DecodeGoogleCloudStorageConfigFromURI(uri string) (GoogleCloudStorageConfig, error) { + u, err := url.Parse(uri) + if err != nil { + return GoogleCloudStorageConfig{}, err + } + + if u.Scheme != "storage" { + return GoogleCloudStorageConfig{}, fmt.Errorf("invalid scheme: %q", u.Scheme) + } + if u.Host != "gcp" { + return GoogleCloudStorageConfig{}, fmt.Errorf("invalid host: %q", u.Host) + } + + q := u.Query() + credentials, err := getQueryParameter(q, "credentialsPath") + if err != nil { + return GoogleCloudStorageConfig{}, err + } + projectID, err := getQueryParameter(q, "projectID") + if err != nil { + return GoogleCloudStorageConfig{}, err + } + bucket, err := getQueryParameter(q, "bucket") + if err != nil { + return GoogleCloudStorageConfig{}, err + } + + return GoogleCloudStorageConfig{ + CredentialsPath: credentials, + ProjectID: projectID, + Bucket: bucket, + }, nil +} + +// EncodeToURI returns a URI encoding the Google Cloud Storage configuration. +func (g GoogleCloudStorageConfig) EncodeToURI() string { + return fmt.Sprintf( + gcpStorageURI, + url.QueryEscape(g.ProjectID), + url.QueryEscape(g.Bucket), + url.QueryEscape(g.CredentialsPath), + ) +} + // getBase64QueryParameter returns the url-base64-decoded value for the given key from the query parameters. func getBase64QueryParameter(q url.Values, key string) ([]byte, error) { value, err := getQueryParameter(q, key) diff --git a/internal/kms/uri/uri_test.go b/internal/kms/uri/uri_test.go new file mode 100644 index 000000000..3517d2a8c --- /dev/null +++ b/internal/kms/uri/uri_test.go @@ -0,0 +1,113 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package uri + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + +func TestMasterSecretURI(t *testing.T) { + cfg := MasterSecret{ + Key: []byte("key"), + Salt: []byte("salt"), + } + + checkURI(t, cfg, DecodeMasterSecretFromURI) +} + +func TestAWSURI(t *testing.T) { + cfg := AWSConfig{ + KeyName: "key", + Region: "region", + AccessKeyID: "accessKeyID", + AccessKey: "accessKey", + } + + checkURI(t, cfg, DecodeAWSConfigFromURI) +} + +func TestAWSS3URI(t *testing.T) { + cfg := AWSS3Config{ + Bucket: "bucket", + Region: "region", + AccessKeyID: "accessKeyID", + AccessKey: "accessKey", + } + + checkURI(t, cfg, DecodeAWSS3ConfigFromURI) +} + +func TestAzureURI(t *testing.T) { + cfg := AzureConfig{ + KeyName: "key", + TenantID: "tenantID", + ClientID: "clientID", + ClientSecret: "clientSecret", + VaultName: "vaultName", + VaultType: DefaultCloud, + } + + checkURI(t, cfg, DecodeAzureConfigFromURI) +} + +func TestAzureBlobURI(t *testing.T) { + cfg := AzureBlobConfig{ + StorageAccount: "accountName", + Container: "containerName", + TenantID: "tenantID", + ClientID: "clientID", + ClientSecret: "clientSecret", + } + + checkURI(t, cfg, DecodeAzureBlobConfigFromURI) +} + +func TestGCPURI(t *testing.T) { + cfg := GCPConfig{ + KeyName: "key", + ProjectID: "project", + Location: "location", + KeyRing: "keyRing", + CredentialsPath: "/path/to/credentials", + } + + checkURI(t, cfg, DecodeGCPConfigFromURI) +} + +func TestGoogleCloudStorageURI(t *testing.T) { + cfg := GoogleCloudStorageConfig{ + ProjectID: "project", + Bucket: "bucket", + CredentialsPath: "/path/to/credentials", + } + + checkURI(t, cfg, DecodeGoogleCloudStorageConfigFromURI) +} + +type cfgStruct interface { + EncodeToURI() string +} + +func checkURI[T any](t *testing.T, cfg cfgStruct, decodeFunc func(string) (T, error)) { + t.Helper() + require := require.New(t) + assert := assert.New(t) + + uri := cfg.EncodeToURI() + decoded, err := decodeFunc(uri) + require.NoError(err, "failed to decode URI to config: %s", uri) + + assert.Equal(cfg, decoded, "decoded config does not match original config") +} diff --git a/keyservice/cmd/main.go b/keyservice/cmd/main.go index da6354866..24c378cb0 100644 --- a/keyservice/cmd/main.go +++ b/keyservice/cmd/main.go @@ -18,6 +18,7 @@ import ( "github.com/edgelesssys/constellation/v2/internal/crypto" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/kms/setup" + "github.com/edgelesssys/constellation/v2/internal/kms/uri" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/keyservice/internal/server" "github.com/spf13/afero" @@ -52,12 +53,12 @@ func main() { if len(salt) < crypto.RNGLengthDefault { log.With(zap.Error(errors.New("invalid salt length"))).Fatalf("Expected salt to be %d bytes, but got %d", crypto.RNGLengthDefault, len(salt)) } - masterSecret := setup.MasterSecret{Key: masterKey, Salt: salt} + masterSecret := uri.MasterSecret{Key: masterKey, Salt: salt} // set up Key Management Service ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() - conKMS, err := setup.KMS(ctx, setup.NoStoreURI, masterSecret.EncodeToURI()) + conKMS, err := setup.KMS(ctx, uri.NoStoreURI, masterSecret.EncodeToURI()) if err != nil { log.With(zap.Error(err)).Fatalf("Failed to setup KMS") }