diff --git a/builtin/credential/aws/backend.go b/builtin/credential/aws/backend.go index 352bb14812ef..ce7c39bb6a8e 100644 --- a/builtin/credential/aws/backend.go +++ b/builtin/credential/aws/backend.go @@ -65,6 +65,11 @@ type backend struct { // will be flushed. The empty STS role signifies the master account IAMClientsMap map[string]map[string]*iam.IAM + // Map to associate a partition to a random region in that partition. Users of + // this don't care what region in the partition they use, but there is some client + // cache efficiency gain if we keep the mapping stable, hence caching a single copy. + partitionToRegionMap map[string]*endpoints.Region + // Map of AWS unique IDs to the full ARN corresponding to that unique ID // This avoids the overhead of an AWS API hit for every login request // using the IAM auth method when bound_iam_principal_arn contains a wildcard @@ -144,6 +149,8 @@ func Backend(_ *logical.BackendConfig) (*backend, error) { Clean: b.cleanup, } + b.partitionToRegionMap = generatePartitionToRegionMap() + return b, nil } @@ -249,7 +256,7 @@ func (b *backend) resolveArnToRealUniqueId(ctx context.Context, s logical.Storag // partition, and passing that region back back to the SDK, so that the SDK can figure out the // proper partition from the arbitrary region we passed in to look up the endpoint. // Sigh - region := getAnyRegionForAwsPartition(entity.Partition) + region := b.partitionToRegionMap[entity.Partition] if region == nil { return "", fmt.Errorf("unable to resolve partition %q to a region", entity.Partition) } @@ -293,18 +300,21 @@ func (b *backend) resolveArnToRealUniqueId(ctx context.Context, s logical.Storag // Adapted from https://docs.aws.amazon.com/sdk-for-go/api/aws/endpoints/ // the "Enumerating Regions and Endpoint Metadata" section -func getAnyRegionForAwsPartition(partitionId string) *endpoints.Region { +func generatePartitionToRegionMap() map[string]*endpoints.Region { + partitionToRegion := make(map[string]*endpoints.Region) + resolver := endpoints.DefaultResolver() partitions := resolver.(endpoints.EnumPartitions).Partitions() for _, p := range partitions { - if p.ID() == partitionId { - for _, r := range p.Regions() { - return &r - } + // Choose a single region randomly from the partition + for _, r := range p.Regions() { + partitionToRegion[p.ID()] = &r + break } } - return nil + + return partitionToRegion } const backendHelp = ` diff --git a/builtin/credential/aws/path_login.go b/builtin/credential/aws/path_login.go index 8cf7552f31a0..6b8fe95d6a0e 100644 --- a/builtin/credential/aws/path_login.go +++ b/builtin/credential/aws/path_login.go @@ -1650,7 +1650,13 @@ func (e *iamEntity) canonicalArn() string { // This returns the "full" ARN of an iamEntity, how it would be referred to in AWS proper func (b *backend) fullArn(ctx context.Context, e *iamEntity, s logical.Storage) (string, error) { // Not assuming path is reliable for any entity types - client, err := b.clientIAM(ctx, s, getAnyRegionForAwsPartition(e.Partition).ID(), e.AccountNumber) + + region := b.partitionToRegionMap[e.Partition] + if region == nil { + return "", fmt.Errorf("unable to resolve partition %q to a region", e.Partition) + } + + client, err := b.clientIAM(ctx, s, region.ID(), e.AccountNumber) if err != nil { return "", errwrap.Wrapf("error creating IAM client: {{err}}", err) }