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