src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

resolve_credentials.go (19777B)


      1 package config
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 	"io/ioutil"
      7 	"net"
      8 	"net/url"
      9 	"os"
     10 	"time"
     11 
     12 	"github.com/aws/aws-sdk-go-v2/aws"
     13 	"github.com/aws/aws-sdk-go-v2/credentials"
     14 	"github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds"
     15 	"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds"
     16 	"github.com/aws/aws-sdk-go-v2/credentials/processcreds"
     17 	"github.com/aws/aws-sdk-go-v2/credentials/ssocreds"
     18 	"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
     19 	"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
     20 	"github.com/aws/aws-sdk-go-v2/service/sso"
     21 	"github.com/aws/aws-sdk-go-v2/service/ssooidc"
     22 	"github.com/aws/aws-sdk-go-v2/service/sts"
     23 )
     24 
     25 const (
     26 	// valid credential source values
     27 	credSourceEc2Metadata      = "Ec2InstanceMetadata"
     28 	credSourceEnvironment      = "Environment"
     29 	credSourceECSContainer     = "EcsContainer"
     30 	httpProviderAuthFileEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"
     31 )
     32 
     33 // direct representation of the IPv4 address for the ECS container
     34 // "169.254.170.2"
     35 var ecsContainerIPv4 net.IP = []byte{
     36 	169, 254, 170, 2,
     37 }
     38 
     39 // direct representation of the IPv4 address for the EKS container
     40 // "169.254.170.23"
     41 var eksContainerIPv4 net.IP = []byte{
     42 	169, 254, 170, 23,
     43 }
     44 
     45 // direct representation of the IPv6 address for the EKS container
     46 // "fd00:ec2::23"
     47 var eksContainerIPv6 net.IP = []byte{
     48 	0xFD, 0, 0xE, 0xC2,
     49 	0, 0, 0, 0,
     50 	0, 0, 0, 0,
     51 	0, 0, 0, 0x23,
     52 }
     53 
     54 var (
     55 	ecsContainerEndpoint = "http://169.254.170.2" // not constant to allow for swapping during unit-testing
     56 )
     57 
     58 // resolveCredentials extracts a credential provider from slice of config
     59 // sources.
     60 //
     61 // If an explicit credential provider is not found the resolver will fallback
     62 // to resolving credentials by extracting a credential provider from EnvConfig
     63 // and SharedConfig.
     64 func resolveCredentials(ctx context.Context, cfg *aws.Config, configs configs) error {
     65 	found, err := resolveCredentialProvider(ctx, cfg, configs)
     66 	if found || err != nil {
     67 		return err
     68 	}
     69 
     70 	return resolveCredentialChain(ctx, cfg, configs)
     71 }
     72 
     73 // resolveCredentialProvider extracts the first instance of Credentials from the
     74 // config slices.
     75 //
     76 // The resolved CredentialProvider will be wrapped in a cache to ensure the
     77 // credentials are only refreshed when needed. This also protects the
     78 // credential provider to be used concurrently.
     79 //
     80 // Config providers used:
     81 // * credentialsProviderProvider
     82 func resolveCredentialProvider(ctx context.Context, cfg *aws.Config, configs configs) (bool, error) {
     83 	credProvider, found, err := getCredentialsProvider(ctx, configs)
     84 	if !found || err != nil {
     85 		return false, err
     86 	}
     87 
     88 	cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, credProvider)
     89 	if err != nil {
     90 		return false, err
     91 	}
     92 
     93 	return true, nil
     94 }
     95 
     96 // resolveCredentialChain resolves a credential provider chain using EnvConfig
     97 // and SharedConfig if present in the slice of provided configs.
     98 //
     99 // The resolved CredentialProvider will be wrapped in a cache to ensure the
    100 // credentials are only refreshed when needed. This also protects the
    101 // credential provider to be used concurrently.
    102 func resolveCredentialChain(ctx context.Context, cfg *aws.Config, configs configs) (err error) {
    103 	envConfig, sharedConfig, other := getAWSConfigSources(configs)
    104 
    105 	// When checking if a profile was specified programmatically we should only consider the "other"
    106 	// configuration sources that have been provided. This ensures we correctly honor the expected credential
    107 	// hierarchy.
    108 	_, sharedProfileSet, err := getSharedConfigProfile(ctx, other)
    109 	if err != nil {
    110 		return err
    111 	}
    112 
    113 	switch {
    114 	case sharedProfileSet:
    115 		ctx, err = resolveCredsFromProfile(ctx, cfg, envConfig, sharedConfig, other)
    116 	case envConfig.Credentials.HasKeys():
    117 		ctx = addCredentialSource(ctx, aws.CredentialSourceEnvVars)
    118 		cfg.Credentials = credentials.StaticCredentialsProvider{Value: envConfig.Credentials, Source: getCredentialSources(ctx)}
    119 	case len(envConfig.WebIdentityTokenFilePath) > 0:
    120 		ctx = addCredentialSource(ctx, aws.CredentialSourceEnvVarsSTSWebIDToken)
    121 		err = assumeWebIdentity(ctx, cfg, envConfig.WebIdentityTokenFilePath, envConfig.RoleARN, envConfig.RoleSessionName, configs)
    122 	default:
    123 		ctx, err = resolveCredsFromProfile(ctx, cfg, envConfig, sharedConfig, other)
    124 	}
    125 	if err != nil {
    126 		return err
    127 	}
    128 
    129 	// Wrap the resolved provider in a cache so the SDK will cache credentials.
    130 	cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, cfg.Credentials)
    131 	if err != nil {
    132 		return err
    133 	}
    134 
    135 	return nil
    136 }
    137 
    138 func resolveCredsFromProfile(ctx context.Context, cfg *aws.Config, envConfig *EnvConfig, sharedConfig *SharedConfig, configs configs) (ctx2 context.Context, err error) {
    139 	switch {
    140 	case sharedConfig.Source != nil:
    141 		ctx = addCredentialSource(ctx, aws.CredentialSourceProfileSourceProfile)
    142 		// Assume IAM role with credentials source from a different profile.
    143 		ctx, err = resolveCredsFromProfile(ctx, cfg, envConfig, sharedConfig.Source, configs)
    144 
    145 	case sharedConfig.Credentials.HasKeys():
    146 		// Static Credentials from Shared Config/Credentials file.
    147 		ctx = addCredentialSource(ctx, aws.CredentialSourceProfile)
    148 		cfg.Credentials = credentials.StaticCredentialsProvider{
    149 			Value:  sharedConfig.Credentials,
    150 			Source: getCredentialSources(ctx),
    151 		}
    152 
    153 	case len(sharedConfig.CredentialSource) != 0:
    154 		ctx = addCredentialSource(ctx, aws.CredentialSourceProfileNamedProvider)
    155 		ctx, err = resolveCredsFromSource(ctx, cfg, envConfig, sharedConfig, configs)
    156 
    157 	case len(sharedConfig.WebIdentityTokenFile) != 0:
    158 		// Credentials from Assume Web Identity token require an IAM Role, and
    159 		// that roll will be assumed. May be wrapped with another assume role
    160 		// via SourceProfile.
    161 		ctx = addCredentialSource(ctx, aws.CredentialSourceProfileSTSWebIDToken)
    162 		return ctx, assumeWebIdentity(ctx, cfg, sharedConfig.WebIdentityTokenFile, sharedConfig.RoleARN, sharedConfig.RoleSessionName, configs)
    163 
    164 	case sharedConfig.hasSSOConfiguration():
    165 		if sharedConfig.hasLegacySSOConfiguration() {
    166 			ctx = addCredentialSource(ctx, aws.CredentialSourceProfileSSOLegacy)
    167 			ctx = addCredentialSource(ctx, aws.CredentialSourceSSOLegacy)
    168 		} else {
    169 			ctx = addCredentialSource(ctx, aws.CredentialSourceSSO)
    170 		}
    171 		if sharedConfig.SSOSession != nil {
    172 			ctx = addCredentialSource(ctx, aws.CredentialSourceProfileSSO)
    173 		}
    174 		err = resolveSSOCredentials(ctx, cfg, sharedConfig, configs)
    175 
    176 	case len(sharedConfig.CredentialProcess) != 0:
    177 		// Get credentials from CredentialProcess
    178 		ctx = addCredentialSource(ctx, aws.CredentialSourceProfileProcess)
    179 		ctx = addCredentialSource(ctx, aws.CredentialSourceProcess)
    180 		err = processCredentials(ctx, cfg, sharedConfig, configs)
    181 
    182 	case len(envConfig.ContainerCredentialsRelativePath) != 0:
    183 		ctx = addCredentialSource(ctx, aws.CredentialSourceHTTP)
    184 		err = resolveHTTPCredProvider(ctx, cfg, ecsContainerURI(envConfig.ContainerCredentialsRelativePath), envConfig.ContainerAuthorizationToken, configs)
    185 
    186 	case len(envConfig.ContainerCredentialsEndpoint) != 0:
    187 		ctx = addCredentialSource(ctx, aws.CredentialSourceHTTP)
    188 		err = resolveLocalHTTPCredProvider(ctx, cfg, envConfig.ContainerCredentialsEndpoint, envConfig.ContainerAuthorizationToken, configs)
    189 
    190 	default:
    191 		ctx = addCredentialSource(ctx, aws.CredentialSourceIMDS)
    192 		err = resolveEC2RoleCredentials(ctx, cfg, configs)
    193 	}
    194 	if err != nil {
    195 		return ctx, err
    196 	}
    197 
    198 	if len(sharedConfig.RoleARN) > 0 {
    199 		return ctx, credsFromAssumeRole(ctx, cfg, sharedConfig, configs)
    200 	}
    201 
    202 	return ctx, nil
    203 }
    204 
    205 func resolveSSOCredentials(ctx context.Context, cfg *aws.Config, sharedConfig *SharedConfig, configs configs) error {
    206 	if err := sharedConfig.validateSSOConfiguration(); err != nil {
    207 		return err
    208 	}
    209 
    210 	var options []func(*ssocreds.Options)
    211 	v, found, err := getSSOProviderOptions(ctx, configs)
    212 	if err != nil {
    213 		return err
    214 	}
    215 	if found {
    216 		options = append(options, v)
    217 	}
    218 
    219 	cfgCopy := cfg.Copy()
    220 
    221 	options = append(options, func(o *ssocreds.Options) {
    222 		o.CredentialSources = getCredentialSources(ctx)
    223 	})
    224 
    225 	if sharedConfig.SSOSession != nil {
    226 		ssoTokenProviderOptionsFn, found, err := getSSOTokenProviderOptions(ctx, configs)
    227 		if err != nil {
    228 			return fmt.Errorf("failed to get SSOTokenProviderOptions from config sources, %w", err)
    229 		}
    230 		var optFns []func(*ssocreds.SSOTokenProviderOptions)
    231 		if found {
    232 			optFns = append(optFns, ssoTokenProviderOptionsFn)
    233 		}
    234 		cfgCopy.Region = sharedConfig.SSOSession.SSORegion
    235 		cachedPath, err := ssocreds.StandardCachedTokenFilepath(sharedConfig.SSOSession.Name)
    236 		if err != nil {
    237 			return err
    238 		}
    239 		oidcClient := ssooidc.NewFromConfig(cfgCopy)
    240 		tokenProvider := ssocreds.NewSSOTokenProvider(oidcClient, cachedPath, optFns...)
    241 		options = append(options, func(o *ssocreds.Options) {
    242 			o.SSOTokenProvider = tokenProvider
    243 			o.CachedTokenFilepath = cachedPath
    244 		})
    245 	} else {
    246 		cfgCopy.Region = sharedConfig.SSORegion
    247 	}
    248 
    249 	cfg.Credentials = ssocreds.New(sso.NewFromConfig(cfgCopy), sharedConfig.SSOAccountID, sharedConfig.SSORoleName, sharedConfig.SSOStartURL, options...)
    250 
    251 	return nil
    252 }
    253 
    254 func ecsContainerURI(path string) string {
    255 	return fmt.Sprintf("%s%s", ecsContainerEndpoint, path)
    256 }
    257 
    258 func processCredentials(ctx context.Context, cfg *aws.Config, sharedConfig *SharedConfig, configs configs) error {
    259 	var opts []func(*processcreds.Options)
    260 
    261 	options, found, err := getProcessCredentialOptions(ctx, configs)
    262 	if err != nil {
    263 		return err
    264 	}
    265 	if found {
    266 		opts = append(opts, options)
    267 	}
    268 
    269 	opts = append(opts, func(o *processcreds.Options) {
    270 		o.CredentialSources = getCredentialSources(ctx)
    271 	})
    272 
    273 	cfg.Credentials = processcreds.NewProvider(sharedConfig.CredentialProcess, opts...)
    274 
    275 	return nil
    276 }
    277 
    278 // isAllowedHost allows host to be loopback or known ECS/EKS container IPs
    279 //
    280 // host can either be an IP address OR an unresolved hostname - resolution will
    281 // be automatically performed in the latter case
    282 func isAllowedHost(host string) (bool, error) {
    283 	if ip := net.ParseIP(host); ip != nil {
    284 		return isIPAllowed(ip), nil
    285 	}
    286 
    287 	addrs, err := lookupHostFn(host)
    288 	if err != nil {
    289 		return false, err
    290 	}
    291 
    292 	for _, addr := range addrs {
    293 		if ip := net.ParseIP(addr); ip == nil || !isIPAllowed(ip) {
    294 			return false, nil
    295 		}
    296 	}
    297 
    298 	return true, nil
    299 }
    300 
    301 func isIPAllowed(ip net.IP) bool {
    302 	return ip.IsLoopback() ||
    303 		ip.Equal(ecsContainerIPv4) ||
    304 		ip.Equal(eksContainerIPv4) ||
    305 		ip.Equal(eksContainerIPv6)
    306 }
    307 
    308 func resolveLocalHTTPCredProvider(ctx context.Context, cfg *aws.Config, endpointURL, authToken string, configs configs) error {
    309 	var resolveErr error
    310 
    311 	parsed, err := url.Parse(endpointURL)
    312 	if err != nil {
    313 		resolveErr = fmt.Errorf("invalid URL, %w", err)
    314 	} else {
    315 		host := parsed.Hostname()
    316 		if len(host) == 0 {
    317 			resolveErr = fmt.Errorf("unable to parse host from local HTTP cred provider URL")
    318 		} else if parsed.Scheme == "http" {
    319 			if isAllowedHost, allowHostErr := isAllowedHost(host); allowHostErr != nil {
    320 				resolveErr = fmt.Errorf("failed to resolve host %q, %v", host, allowHostErr)
    321 			} else if !isAllowedHost {
    322 				resolveErr = fmt.Errorf("invalid endpoint host, %q, only loopback/ecs/eks hosts are allowed", host)
    323 			}
    324 		}
    325 	}
    326 
    327 	if resolveErr != nil {
    328 		return resolveErr
    329 	}
    330 
    331 	return resolveHTTPCredProvider(ctx, cfg, endpointURL, authToken, configs)
    332 }
    333 
    334 func resolveHTTPCredProvider(ctx context.Context, cfg *aws.Config, url, authToken string, configs configs) error {
    335 	optFns := []func(*endpointcreds.Options){
    336 		func(options *endpointcreds.Options) {
    337 			if len(authToken) != 0 {
    338 				options.AuthorizationToken = authToken
    339 			}
    340 			if authFilePath := os.Getenv(httpProviderAuthFileEnvVar); authFilePath != "" {
    341 				options.AuthorizationTokenProvider = endpointcreds.TokenProviderFunc(func() (string, error) {
    342 					var contents []byte
    343 					var err error
    344 					if contents, err = ioutil.ReadFile(authFilePath); err != nil {
    345 						return "", fmt.Errorf("failed to read authorization token from %v: %v", authFilePath, err)
    346 					}
    347 					return string(contents), nil
    348 				})
    349 			}
    350 			options.APIOptions = cfg.APIOptions
    351 			if cfg.Retryer != nil {
    352 				options.Retryer = cfg.Retryer()
    353 			}
    354 			options.CredentialSources = getCredentialSources(ctx)
    355 		},
    356 	}
    357 
    358 	optFn, found, err := getEndpointCredentialProviderOptions(ctx, configs)
    359 	if err != nil {
    360 		return err
    361 	}
    362 	if found {
    363 		optFns = append(optFns, optFn)
    364 	}
    365 
    366 	provider := endpointcreds.New(url, optFns...)
    367 
    368 	cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider, func(options *aws.CredentialsCacheOptions) {
    369 		options.ExpiryWindow = 5 * time.Minute
    370 	})
    371 	if err != nil {
    372 		return err
    373 	}
    374 
    375 	return nil
    376 }
    377 
    378 func resolveCredsFromSource(ctx context.Context, cfg *aws.Config, envConfig *EnvConfig, sharedCfg *SharedConfig, configs configs) (context.Context, error) {
    379 	switch sharedCfg.CredentialSource {
    380 	case credSourceEc2Metadata:
    381 		ctx = addCredentialSource(ctx, aws.CredentialSourceIMDS)
    382 		return ctx, resolveEC2RoleCredentials(ctx, cfg, configs)
    383 
    384 	case credSourceEnvironment:
    385 		ctx = addCredentialSource(ctx, aws.CredentialSourceHTTP)
    386 		cfg.Credentials = credentials.StaticCredentialsProvider{Value: envConfig.Credentials, Source: getCredentialSources(ctx)}
    387 
    388 	case credSourceECSContainer:
    389 		ctx = addCredentialSource(ctx, aws.CredentialSourceHTTP)
    390 		if len(envConfig.ContainerCredentialsRelativePath) != 0 {
    391 			return ctx, resolveHTTPCredProvider(ctx, cfg, ecsContainerURI(envConfig.ContainerCredentialsRelativePath), envConfig.ContainerAuthorizationToken, configs)
    392 		}
    393 		if len(envConfig.ContainerCredentialsEndpoint) != 0 {
    394 			return ctx, resolveLocalHTTPCredProvider(ctx, cfg, envConfig.ContainerCredentialsEndpoint, envConfig.ContainerAuthorizationToken, configs)
    395 		}
    396 		return ctx, fmt.Errorf("EcsContainer was specified as the credential_source, but neither 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' or AWS_CONTAINER_CREDENTIALS_FULL_URI' was set")
    397 
    398 	default:
    399 		return ctx, fmt.Errorf("credential_source values must be EcsContainer, Ec2InstanceMetadata, or Environment")
    400 	}
    401 
    402 	return ctx, nil
    403 }
    404 
    405 func resolveEC2RoleCredentials(ctx context.Context, cfg *aws.Config, configs configs) error {
    406 	optFns := make([]func(*ec2rolecreds.Options), 0, 2)
    407 
    408 	optFn, found, err := getEC2RoleCredentialProviderOptions(ctx, configs)
    409 	if err != nil {
    410 		return err
    411 	}
    412 	if found {
    413 		optFns = append(optFns, optFn)
    414 	}
    415 
    416 	optFns = append(optFns, func(o *ec2rolecreds.Options) {
    417 		// Only define a client from config if not already defined.
    418 		if o.Client == nil {
    419 			o.Client = imds.NewFromConfig(*cfg)
    420 		}
    421 		o.CredentialSources = getCredentialSources(ctx)
    422 	})
    423 
    424 	provider := ec2rolecreds.New(optFns...)
    425 
    426 	cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider)
    427 	if err != nil {
    428 		return err
    429 	}
    430 	return nil
    431 }
    432 
    433 func getAWSConfigSources(cfgs configs) (*EnvConfig, *SharedConfig, configs) {
    434 	var (
    435 		envConfig    *EnvConfig
    436 		sharedConfig *SharedConfig
    437 		other        configs
    438 	)
    439 
    440 	for i := range cfgs {
    441 		switch c := cfgs[i].(type) {
    442 		case EnvConfig:
    443 			if envConfig == nil {
    444 				envConfig = &c
    445 			}
    446 		case *EnvConfig:
    447 			if envConfig == nil {
    448 				envConfig = c
    449 			}
    450 		case SharedConfig:
    451 			if sharedConfig == nil {
    452 				sharedConfig = &c
    453 			}
    454 		case *SharedConfig:
    455 			if envConfig == nil {
    456 				sharedConfig = c
    457 			}
    458 		default:
    459 			other = append(other, c)
    460 		}
    461 	}
    462 
    463 	if envConfig == nil {
    464 		envConfig = &EnvConfig{}
    465 	}
    466 
    467 	if sharedConfig == nil {
    468 		sharedConfig = &SharedConfig{}
    469 	}
    470 
    471 	return envConfig, sharedConfig, other
    472 }
    473 
    474 // AssumeRoleTokenProviderNotSetError is an error returned when creating a
    475 // session when the MFAToken option is not set when shared config is configured
    476 // load assume a role with an MFA token.
    477 type AssumeRoleTokenProviderNotSetError struct{}
    478 
    479 // Error is the error message
    480 func (e AssumeRoleTokenProviderNotSetError) Error() string {
    481 	return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.")
    482 }
    483 
    484 func assumeWebIdentity(ctx context.Context, cfg *aws.Config, filepath string, roleARN, sessionName string, configs configs) error {
    485 	if len(filepath) == 0 {
    486 		return fmt.Errorf("token file path is not set")
    487 	}
    488 
    489 	optFns := []func(*stscreds.WebIdentityRoleOptions){
    490 		func(options *stscreds.WebIdentityRoleOptions) {
    491 			options.RoleSessionName = sessionName
    492 		},
    493 	}
    494 
    495 	optFn, found, err := getWebIdentityCredentialProviderOptions(ctx, configs)
    496 	if err != nil {
    497 		return err
    498 	}
    499 
    500 	if found {
    501 		optFns = append(optFns, optFn)
    502 	}
    503 
    504 	opts := stscreds.WebIdentityRoleOptions{
    505 		RoleARN: roleARN,
    506 	}
    507 
    508 	optFns = append(optFns, func(options *stscreds.WebIdentityRoleOptions) {
    509 		options.CredentialSources = getCredentialSources(ctx)
    510 	})
    511 
    512 	for _, fn := range optFns {
    513 		fn(&opts)
    514 	}
    515 
    516 	if len(opts.RoleARN) == 0 {
    517 		return fmt.Errorf("role ARN is not set")
    518 	}
    519 
    520 	client := opts.Client
    521 	if client == nil {
    522 		client = sts.NewFromConfig(*cfg)
    523 	}
    524 
    525 	provider := stscreds.NewWebIdentityRoleProvider(client, roleARN, stscreds.IdentityTokenFile(filepath), optFns...)
    526 
    527 	cfg.Credentials = provider
    528 
    529 	return nil
    530 }
    531 
    532 func credsFromAssumeRole(ctx context.Context, cfg *aws.Config, sharedCfg *SharedConfig, configs configs) (err error) {
    533 	// resolve credentials early
    534 	credentialSources := getCredentialSources(ctx)
    535 	optFns := []func(*stscreds.AssumeRoleOptions){
    536 		func(options *stscreds.AssumeRoleOptions) {
    537 			options.RoleSessionName = sharedCfg.RoleSessionName
    538 			if sharedCfg.RoleDurationSeconds != nil {
    539 				if *sharedCfg.RoleDurationSeconds/time.Minute > 15 {
    540 					options.Duration = *sharedCfg.RoleDurationSeconds
    541 				}
    542 			}
    543 			// Assume role with external ID
    544 			if len(sharedCfg.ExternalID) > 0 {
    545 				options.ExternalID = aws.String(sharedCfg.ExternalID)
    546 			}
    547 
    548 			// Assume role with MFA
    549 			if len(sharedCfg.MFASerial) != 0 {
    550 				options.SerialNumber = aws.String(sharedCfg.MFASerial)
    551 			}
    552 
    553 			// add existing credential chain
    554 			options.CredentialSources = credentialSources
    555 		},
    556 	}
    557 
    558 	optFn, found, err := getAssumeRoleCredentialProviderOptions(ctx, configs)
    559 	if err != nil {
    560 		return err
    561 	}
    562 	if found {
    563 		optFns = append(optFns, optFn)
    564 	}
    565 
    566 	{
    567 		// Synthesize options early to validate configuration errors sooner to ensure a token provider
    568 		// is present if the SerialNumber was set.
    569 		var o stscreds.AssumeRoleOptions
    570 		for _, fn := range optFns {
    571 			fn(&o)
    572 		}
    573 		if o.TokenProvider == nil && o.SerialNumber != nil {
    574 			return AssumeRoleTokenProviderNotSetError{}
    575 		}
    576 	}
    577 	cfg.Credentials = stscreds.NewAssumeRoleProvider(sts.NewFromConfig(*cfg), sharedCfg.RoleARN, optFns...)
    578 
    579 	return nil
    580 }
    581 
    582 // wrapWithCredentialsCache will wrap provider with an aws.CredentialsCache
    583 // with the provided options if the provider is not already a
    584 // aws.CredentialsCache.
    585 func wrapWithCredentialsCache(
    586 	ctx context.Context,
    587 	cfgs configs,
    588 	provider aws.CredentialsProvider,
    589 	optFns ...func(options *aws.CredentialsCacheOptions),
    590 ) (aws.CredentialsProvider, error) {
    591 	_, ok := provider.(*aws.CredentialsCache)
    592 	if ok {
    593 		return provider, nil
    594 	}
    595 
    596 	credCacheOptions, optionsFound, err := getCredentialsCacheOptionsProvider(ctx, cfgs)
    597 	if err != nil {
    598 		return nil, err
    599 	}
    600 
    601 	// force allocation of a new slice if the additional options are
    602 	// needed, to prevent overwriting the passed in slice of options.
    603 	optFns = optFns[:len(optFns):len(optFns)]
    604 	if optionsFound {
    605 		optFns = append(optFns, credCacheOptions)
    606 	}
    607 
    608 	return aws.NewCredentialsCache(provider, optFns...), nil
    609 }
    610 
    611 // credentialSource stores the chain of providers that was used to create an instance of
    612 // a credentials provider on the context
    613 type credentialSource struct{}
    614 
    615 func addCredentialSource(ctx context.Context, source aws.CredentialSource) context.Context {
    616 	existing, ok := ctx.Value(credentialSource{}).([]aws.CredentialSource)
    617 	if !ok {
    618 		existing = []aws.CredentialSource{source}
    619 	} else {
    620 		existing = append(existing, source)
    621 	}
    622 	return context.WithValue(ctx, credentialSource{}, existing)
    623 }
    624 
    625 func getCredentialSources(ctx context.Context) []aws.CredentialSource {
    626 	return ctx.Value(credentialSource{}).([]aws.CredentialSource)
    627 }