* fix(storage): Storage Assume Role Support This commit introduces fixes to enable operators to use AWS dynamic credentials on the storage side of this plugin, namely validation, improved credential lifecycle management to allow for storage buckets to be updated from static to dynamic credentials and vice versa, and a new way to determine credential types. It also introduces new tests for dynamic credential usage for the various functions. * fix(storage): Static rotated creds being deleted on plugin err On Storage Bucket update, when we determine if the credentials are to be updated to new ones, we call `credState.ReplaceCreds`. This call, if the old credentials were managed by us (rotated at create time), will delete the old credentials and set the new ones onto credState. We called this function without validating if the incoming credentials are valid. This could get the bucket in a state where where, while we'd return an update error if the incoming credentials weren't working with S3 (err in the dry run process that happens at the end of the update), we'd also silently delete the old credentials the bucket used, rendering the bucket completely broken. --------- Co-authored-by: Hugo <hugoamvieira@users.noreply.github.com>
89 lines
2.7 KiB
Go
89 lines
2.7 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package credential
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
"github.com/aws/aws-sdk-go-v2/service/iam"
|
|
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
)
|
|
|
|
const (
|
|
testOptionErr = "test option error"
|
|
testGetCallerIdentityErr = "test error for GetCallerIdentity"
|
|
testGetUserErr = "test error for GetUser"
|
|
testDeleteAccessKeyErr = "test error for DeleteAccessKey"
|
|
)
|
|
|
|
type testMockIAMState struct {
|
|
DeleteAccessKeyCalled bool
|
|
}
|
|
|
|
func (s *testMockIAMState) Reset() {
|
|
s.DeleteAccessKeyCalled = false
|
|
}
|
|
|
|
type testMockIAM struct {
|
|
awsutil.IAMClient
|
|
|
|
State *testMockIAMState
|
|
}
|
|
|
|
func (m *testMockIAM) DeleteAccessKey(ctx context.Context, input *iam.DeleteAccessKeyInput, opts ...func(*iam.Options)) (*iam.DeleteAccessKeyOutput, error) {
|
|
m.State.DeleteAccessKeyCalled = true
|
|
return m.IAMClient.DeleteAccessKey(ctx, input, opts...)
|
|
}
|
|
|
|
func newTestMockIAM(state *testMockIAMState, opts ...awsutil.MockIAMOption) awsutil.IAMAPIFunc {
|
|
return func(awsConfig *aws.Config) (awsutil.IAMClient, error) {
|
|
m := &testMockIAM{
|
|
State: state,
|
|
}
|
|
f := awsutil.NewMockIAM(opts...)
|
|
var err error
|
|
|
|
m.IAMClient, err = f(awsConfig)
|
|
return m, err
|
|
}
|
|
}
|
|
|
|
// MockStaticCredentialSecrets returns a *structpb.Struct that contains two
|
|
// key pair values: (access_key_id, AKIA_foobar) & (secret_access_key, bazqux)
|
|
func MockStaticCredentialSecrets() *structpb.Struct {
|
|
return &structpb.Struct{
|
|
Fields: map[string]*structpb.Value{
|
|
ConstAccessKeyId: structpb.NewStringValue("AKIA_foobar"),
|
|
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
|
|
},
|
|
}
|
|
}
|
|
|
|
// MockAssumeRoleAttributes returns a *structpb.Struct that contains six key pair values:
|
|
//
|
|
// (region, region)
|
|
// (disable_credential_rotation, disableRotate)
|
|
// (role_arn, arn:aws:iam::123456789012:role/S3Access)
|
|
// (role_external_id, 1234567890)
|
|
// (role_session_name, ec2-assume-role-provider)
|
|
// (role_tags, struct{foo:bar})
|
|
func MockAssumeRoleAttributes(region string, disableRotate bool) *structpb.Struct {
|
|
return &structpb.Struct{
|
|
Fields: map[string]*structpb.Value{
|
|
ConstRegion: structpb.NewStringValue(region),
|
|
ConstDisableCredentialRotation: structpb.NewBoolValue(disableRotate),
|
|
ConstRoleArn: structpb.NewStringValue("arn:aws:iam::123456789012:role/S3Access"),
|
|
ConstRoleExternalId: structpb.NewStringValue("1234567890"),
|
|
ConstRoleSessionName: structpb.NewStringValue("ec2-assume-role-provider"),
|
|
ConstRoleTags: structpb.NewStructValue(&structpb.Struct{
|
|
Fields: map[string]*structpb.Value{
|
|
"foo": structpb.NewStringValue("bar"),
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
}
|