mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-03-17 12:36:14 -04:00
internal: refactor storage credentials (#1071)
* 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>
This commit is contained in:
parent
96b4b74a7a
commit
5eb73706f5
@ -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))
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
))
|
||||
|
||||
|
13
go.mod
13
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
|
||||
)
|
||||
|
30
hack/go.mod
30
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
|
||||
|
93
hack/go.sum
93
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=
|
||||
|
@ -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)
|
||||
```
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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)) {
|
@ -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,
|
||||
}
|
||||
|
104
internal/kms/storage/azureblob/azureblob.go
Normal file
104
internal/kms/storage/azureblob/azureblob.go
Normal file
@ -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)
|
||||
}
|
@ -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())
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
131
internal/kms/storage/gcs/gcs.go
Normal file
131
internal/kms/storage/gcs/gcs.go
Normal file
@ -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
|
||||
}
|
||||
}
|
@ -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 {
|
44
internal/kms/storage/memfs/memfs.go
Normal file
44
internal/kms/storage/memfs/memfs.go
Normal file
@ -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
|
||||
}
|
@ -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)
|
||||
}
|
@ -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
|
||||
}
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
113
internal/kms/uri/uri_test.go
Normal file
113
internal/kms/uri/uri_test.go
Normal file
@ -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")
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user