feat: update sts & iam client to utilize dualstack

This commit is contained in:
Damian Debkowski committed 2024-10-24 18:37:08 -07:00
1 parent a7ab2877fe
commit 23259fda6b
6 files changed
+119 -13

No files matched your search

+12 -1
View File
@@ -37,7 +37,7 @@ type CredentialAttributes struct {
// CredentialsConfig used for configuring an AWS session. An status error is returned
// with an InvalidArgument code if any unrecognized fields are found in the protobuf
// struct input.
func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes) (*awsutil.CredentialsConfig, error) {
func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes, dualStack bool) (*awsutil.CredentialsConfig, error) {
// initialize secrets if it is nil
// secrets can be nil because static credentials are optional
if secrets == nil {
@@ -117,6 +117,17 @@ func GetCredentialsConfig(secrets *structpb.Struct, attrs *CredentialAttributes)
opts = append(opts, awsutil.WithRoleTags(attrs.RoleTags))
}
if dualStack {
opts = append(opts, []awsutil.Option{
awsutil.WithIamEndpointResolver(&iamEndpointResolver{
dualStack: dualStack,
}),
awsutil.WithStsEndpointResolver(&stsEndpointResolver{
dualStack: dualStack,
}),
}...)
}
cfg, err := awsutil.NewCredentialsConfig(opts...)
if err != nil {
return nil, errors.BadRequestStatus(err.Error())
+27 -1
View File
@@ -106,6 +106,7 @@ func TestGetCredentialsConfig(t *testing.T) {
name string
secrets *structpb.Struct
attrs *CredentialAttributes
dualStack bool
expected *awsutil.CredentialsConfig
expectedErrContains string
}{
@@ -135,6 +136,24 @@ func TestGetCredentialsConfig(t *testing.T) {
Region: "us-west-2",
},
},
{
name: "with dualstack",
secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("AKIAfoobar"),
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
},
},
dualStack: true,
attrs: &CredentialAttributes{
Region: "us-west-2",
},
expected: &awsutil.CredentialsConfig{
AccessKey: "AKIAfoobar",
SecretKey: "bazqux",
Region: "us-west-2",
},
},
{
name: "unknown fields",
secrets: &structpb.Struct{
@@ -233,7 +252,7 @@ func TestGetCredentialsConfig(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := GetCredentialsConfig(tc.secrets, tc.attrs)
actual, err := GetCredentialsConfig(tc.secrets, tc.attrs, tc.dualStack)
if tc.expectedErrContains != "" {
require.Error(err)
require.Contains(err.Error(), tc.expectedErrContains)
@@ -249,6 +268,13 @@ func TestGetCredentialsConfig(t *testing.T) {
require.Equal(tc.expected.RoleExternalId, actual.RoleExternalId)
require.Equal(tc.expected.RoleSessionName, actual.RoleSessionName)
require.Equal(tc.expected.RoleTags, actual.RoleTags)
if tc.dualStack {
require.NotNil(actual.IAMEndpointResolver)
require.NotNil(actual.STSEndpointResolver)
} else {
require.Nil(actual.IAMEndpointResolver)
require.Nil(actual.STSEndpointResolver)
}
})
}
}
+35 -1
View File
@@ -11,7 +11,10 @@ import (
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/iam"
iamTypes "github.com/aws/aws-sdk-go-v2/service/iam/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
smithyEndpoints "github.com/aws/smithy-go/endpoints"
"github.com/hashicorp/boundary-plugin-aws/internal/errors"
"github.com/hashicorp/boundary-plugin-aws/internal/values"
"github.com/hashicorp/go-secure-stdlib/awsutil/v2"
@@ -237,7 +240,7 @@ func (s *AwsCredentialPersistedState) ToMap() map[string]any {
// AwsCredentialPersistedStateFromProto parses values out of a protobuf struct input
// and returns a AwsCredentialPersistedState used for configuring an AWS session.
func AwsCredentialPersistedStateFromProto(secrets *structpb.Struct, attrs *CredentialAttributes, opts ...AwsCredentialPersistedStateOption) (*AwsCredentialPersistedState, error) {
func AwsCredentialPersistedStateFromProto(secrets *structpb.Struct, attrs *CredentialAttributes, dualStack bool, opts ...AwsCredentialPersistedStateOption) (*AwsCredentialPersistedState, error) {
// initialize secrets if it is nil
// secrets can be nil because static credentials are optional
if secrets == nil {
@@ -292,6 +295,17 @@ func AwsCredentialPersistedStateFromProto(secrets *structpb.Struct, attrs *Crede
if len(attrs.RoleTags) != 0 {
awsOpts = append(awsOpts, awsutil.WithRoleTags(attrs.RoleTags))
}
if dualStack {
awsOpts = append(awsOpts, []awsutil.Option{
awsutil.WithIamEndpointResolver(&iamEndpointResolver{
dualStack: dualStack,
}),
awsutil.WithStsEndpointResolver(&stsEndpointResolver{
dualStack: dualStack,
}),
}...)
}
credentialsConfig, err := awsutil.NewCredentialsConfig(awsOpts...)
if err != nil {
return nil, err
@@ -321,3 +335,23 @@ func GetCredentialType(cc *awsutil.CredentialsConfig) CredentialType {
return Unknown
}
type iamEndpointResolver struct {
dualStack bool
}
func (e *iamEndpointResolver) ResolveEndpoint(ctx context.Context, params iam.EndpointParameters) (resolver smithyEndpoints.Endpoint, err error) {
params.UseDualStack = aws.Bool(e.dualStack)
resolver, err = iam.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
return
}
type stsEndpointResolver struct {
dualStack bool
}
func (e *stsEndpointResolver) ResolveEndpoint(ctx context.Context, params sts.EndpointParameters) (resolver smithyEndpoints.Endpoint, err error) {
params.UseDualStack = aws.Bool(e.dualStack)
resolver, err = sts.NewDefaultEndpointResolverV2().ResolveEndpoint(ctx, params)
return
}
+29 -1
View File
@@ -112,6 +112,7 @@ func TestAwsCredentialPersistedStateFromProto(t *testing.T) {
name string
secrets *structpb.Struct
attrs *CredentialAttributes
dualStack bool
opts []AwsCredentialPersistedStateOption
expected *AwsCredentialPersistedState
expectedErr string
@@ -174,6 +175,26 @@ func TestAwsCredentialPersistedStateFromProto(t *testing.T) {
},
},
},
{
name: "with dualstack static credentials",
secrets: &structpb.Struct{
Fields: map[string]*structpb.Value{
ConstAccessKeyId: structpb.NewStringValue("foobar"),
ConstSecretAccessKey: structpb.NewStringValue("bazqux"),
},
},
dualStack: true,
attrs: &CredentialAttributes{
Region: "us-west-2",
},
expected: &AwsCredentialPersistedState{
CredentialsConfig: &awsutil.CredentialsConfig{
Region: "us-west-2",
AccessKey: "foobar",
SecretKey: "bazqux",
},
},
},
{
name: "bad last rotated time",
secrets: &structpb.Struct{
@@ -318,7 +339,7 @@ func TestAwsCredentialPersistedStateFromProto(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
require := require.New(t)
actual, err := AwsCredentialPersistedStateFromProto(tc.secrets, tc.attrs, tc.opts...)
actual, err := AwsCredentialPersistedStateFromProto(tc.secrets, tc.attrs, tc.dualStack, tc.opts...)
if tc.expectedErr != "" {
require.EqualError(err, tc.expectedErr)
return
@@ -336,6 +357,13 @@ func TestAwsCredentialPersistedStateFromProto(t *testing.T) {
require.Equal(tc.expected.CredentialsConfig.RoleExternalId, actual.CredentialsConfig.RoleExternalId)
require.Equal(tc.expected.CredentialsConfig.RoleSessionName, actual.CredentialsConfig.RoleSessionName)
require.Equal(tc.expected.CredentialsConfig.RoleTags, actual.CredentialsConfig.RoleTags)
if tc.dualStack {
require.NotNil(actual.CredentialsConfig.STSEndpointResolver)
require.NotNil(actual.CredentialsConfig.IAMEndpointResolver)
} else {
require.Nil(actual.CredentialsConfig.STSEndpointResolver)
require.Nil(actual.CredentialsConfig.IAMEndpointResolver)
}
}
})
}
+7 -2
View File
@@ -51,7 +51,7 @@ func (p *HostPlugin) OnCreateCatalog(ctx context.Context, req *pb.OnCreateCatalo
if err != nil {
return nil, err
}
credConfig, err := credential.GetCredentialsConfig(catalog.GetSecrets(), catalogAttributes.CredentialAttributes)
credConfig, err := credential.GetCredentialsConfig(catalog.GetSecrets(), catalogAttributes.CredentialAttributes, catalogAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -135,6 +135,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
credState, err := credential.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetSecrets(),
oldCatalogAttributes.CredentialAttributes,
oldCatalogAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
@@ -143,7 +144,7 @@ func (p *HostPlugin) OnUpdateCatalog(ctx context.Context, req *pb.OnUpdateCatalo
// Verify the incoming credentials are valid and return any errors to the
// user if they're not. Note this doesn't validate the credentials against
// AWS - it only does logical validation on the fields.
updatedCredentials, err := credential.GetCredentialsConfig(newCatalog.GetSecrets(), newCatalogAttributes.CredentialAttributes)
updatedCredentials, err := credential.GetCredentialsConfig(newCatalog.GetSecrets(), newCatalogAttributes.CredentialAttributes, newCatalogAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -260,6 +261,7 @@ func (p *HostPlugin) OnDeleteCatalog(ctx context.Context, req *pb.OnDeleteCatalo
credState, err := credential.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetSecrets(),
catalogAttributes.CredentialAttributes,
catalogAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
@@ -336,6 +338,7 @@ func (p *HostPlugin) OnCreateSet(ctx context.Context, req *pb.OnCreateSetRequest
credState, err := credential.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetSecrets(),
catalogAttributes.CredentialAttributes,
catalogAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
@@ -398,6 +401,7 @@ func (p *HostPlugin) OnUpdateSet(ctx context.Context, req *pb.OnUpdateSetRequest
credState, err := credential.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetSecrets(),
catalogAttributes.CredentialAttributes,
catalogAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
@@ -470,6 +474,7 @@ func (p *HostPlugin) ListHosts(ctx context.Context, req *pb.ListHostsRequest) (*
credState, err := credential.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetSecrets(),
catalogAttributes.CredentialAttributes,
catalogAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
+9 -7
View File
@@ -64,7 +64,7 @@ func (p *StoragePlugin) OnCreateStorageBucket(ctx context.Context, req *pb.OnCre
return nil, err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes)
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, storageAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -147,6 +147,7 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
credState, err := cred.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetData(),
oldStorageAttributes.CredentialAttributes,
oldStorageAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
@@ -155,7 +156,7 @@ func (p *StoragePlugin) OnUpdateStorageBucket(ctx context.Context, req *pb.OnUpd
// Verify the incoming credentials are valid and return any errors to the
// user if they're not. Note this doesn't validate the credentials against
// AWS - it only does logical validation on the fields.
updatedCredentials, err := cred.GetCredentialsConfig(newBucket.GetSecrets(), newStorageAttributes.CredentialAttributes)
updatedCredentials, err := cred.GetCredentialsConfig(newBucket.GetSecrets(), newStorageAttributes.CredentialAttributes, newStorageAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -264,6 +265,7 @@ func (p *StoragePlugin) OnDeleteStorageBucket(ctx context.Context, req *pb.OnDel
credState, err := cred.AwsCredentialPersistedStateFromProto(
req.GetPersisted().GetData(),
storageAttributes.CredentialAttributes,
storageAttributes.DualStack,
p.testCredStateOpts...)
if err != nil {
return nil, errors.BadRequestStatus("error loading persisted state: %s", err)
@@ -318,7 +320,7 @@ func (p *StoragePlugin) HeadObject(ctx context.Context, req *pb.HeadObjectReques
return nil, err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes)
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, storageAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -389,7 +391,7 @@ func (p *StoragePlugin) ValidatePermissions(ctx context.Context, req *pb.Validat
return nil, err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes)
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, storageAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -447,7 +449,7 @@ func (p *StoragePlugin) GetObject(req *pb.GetObjectRequest, stream pb.StoragePlu
return err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes)
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, storageAttributes.DualStack)
if err != nil {
return err
}
@@ -561,7 +563,7 @@ func (p *StoragePlugin) PutObject(ctx context.Context, req *pb.PutObjectRequest)
return nil, err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes)
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, storageAttributes.DualStack)
if err != nil {
return nil, err
}
@@ -659,7 +661,7 @@ func (p *StoragePlugin) DeleteObjects(ctx context.Context, req *pb.DeleteObjects
return nil, err
}
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes)
credConfig, err := cred.GetCredentialsConfig(bucket.GetSecrets(), storageAttributes.CredentialAttributes, storageAttributes.DualStack)
if err != nil {
return nil, err
}