shared_config.go (48502B)
1 package config 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strings" 13 "time" 14 15 "github.com/aws/aws-sdk-go-v2/aws" 16 "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" 17 "github.com/aws/aws-sdk-go-v2/internal/ini" 18 "github.com/aws/aws-sdk-go-v2/internal/shareddefaults" 19 "github.com/aws/smithy-go/logging" 20 smithyrequestcompression "github.com/aws/smithy-go/private/requestcompression" 21 ) 22 23 const ( 24 // Prefix to use for filtering profiles. The profile prefix should only 25 // exist in the shared config file, not the credentials file. 26 profilePrefix = `profile ` 27 28 // Prefix to be used for SSO sections. These are supposed to only exist in 29 // the shared config file, not the credentials file. 30 ssoSectionPrefix = `sso-session ` 31 32 // Prefix for services section. It is referenced in profile via the services 33 // parameter to configure clients for service-specific parameters. 34 servicesPrefix = `services ` 35 36 // string equivalent for boolean 37 endpointDiscoveryDisabled = `false` 38 endpointDiscoveryEnabled = `true` 39 endpointDiscoveryAuto = `auto` 40 41 // Static Credentials group 42 accessKeyIDKey = `aws_access_key_id` // group required 43 secretAccessKey = `aws_secret_access_key` // group required 44 sessionTokenKey = `aws_session_token` // optional 45 46 // Assume Role Credentials group 47 roleArnKey = `role_arn` // group required 48 sourceProfileKey = `source_profile` // group required 49 credentialSourceKey = `credential_source` // group required (or source_profile) 50 externalIDKey = `external_id` // optional 51 mfaSerialKey = `mfa_serial` // optional 52 roleSessionNameKey = `role_session_name` // optional 53 roleDurationSecondsKey = "duration_seconds" // optional 54 55 // AWS Single Sign-On (AWS SSO) group 56 ssoSessionNameKey = "sso_session" 57 58 ssoRegionKey = "sso_region" 59 ssoStartURLKey = "sso_start_url" 60 61 ssoAccountIDKey = "sso_account_id" 62 ssoRoleNameKey = "sso_role_name" 63 64 // Additional Config fields 65 regionKey = `region` 66 67 // endpoint discovery group 68 enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional 69 70 // External Credential process 71 credentialProcessKey = `credential_process` // optional 72 73 // Web Identity Token File 74 webIdentityTokenFileKey = `web_identity_token_file` // optional 75 76 // S3 ARN Region Usage 77 s3UseARNRegionKey = "s3_use_arn_region" 78 79 ec2MetadataServiceEndpointModeKey = "ec2_metadata_service_endpoint_mode" 80 81 ec2MetadataServiceEndpointKey = "ec2_metadata_service_endpoint" 82 83 ec2MetadataV1DisabledKey = "ec2_metadata_v1_disabled" 84 85 // Use DualStack Endpoint Resolution 86 useDualStackEndpoint = "use_dualstack_endpoint" 87 88 // DefaultSharedConfigProfile is the default profile to be used when 89 // loading configuration from the config files if another profile name 90 // is not provided. 91 DefaultSharedConfigProfile = `default` 92 93 // S3 Disable Multi-Region AccessPoints 94 s3DisableMultiRegionAccessPointsKey = `s3_disable_multiregion_access_points` 95 96 useFIPSEndpointKey = "use_fips_endpoint" 97 98 defaultsModeKey = "defaults_mode" 99 100 // Retry options 101 retryMaxAttemptsKey = "max_attempts" 102 retryModeKey = "retry_mode" 103 104 caBundleKey = "ca_bundle" 105 106 sdkAppID = "sdk_ua_app_id" 107 108 ignoreConfiguredEndpoints = "ignore_configured_endpoint_urls" 109 110 endpointURL = "endpoint_url" 111 112 servicesSectionKey = "services" 113 114 disableRequestCompression = "disable_request_compression" 115 requestMinCompressionSizeBytes = "request_min_compression_size_bytes" 116 117 s3DisableExpressSessionAuthKey = "s3_disable_express_session_auth" 118 ) 119 120 // defaultSharedConfigProfile allows for swapping the default profile for testing 121 var defaultSharedConfigProfile = DefaultSharedConfigProfile 122 123 // DefaultSharedCredentialsFilename returns the SDK's default file path 124 // for the shared credentials file. 125 // 126 // Builds the shared config file path based on the OS's platform. 127 // 128 // - Linux/Unix: $HOME/.aws/credentials 129 // - Windows: %USERPROFILE%\.aws\credentials 130 func DefaultSharedCredentialsFilename() string { 131 return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "credentials") 132 } 133 134 // DefaultSharedConfigFilename returns the SDK's default file path for 135 // the shared config file. 136 // 137 // Builds the shared config file path based on the OS's platform. 138 // 139 // - Linux/Unix: $HOME/.aws/config 140 // - Windows: %USERPROFILE%\.aws\config 141 func DefaultSharedConfigFilename() string { 142 return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "config") 143 } 144 145 // DefaultSharedConfigFiles is a slice of the default shared config files that 146 // the will be used in order to load the SharedConfig. 147 var DefaultSharedConfigFiles = []string{ 148 DefaultSharedConfigFilename(), 149 } 150 151 // DefaultSharedCredentialsFiles is a slice of the default shared credentials 152 // files that the will be used in order to load the SharedConfig. 153 var DefaultSharedCredentialsFiles = []string{ 154 DefaultSharedCredentialsFilename(), 155 } 156 157 // SSOSession provides the shared configuration parameters of the sso-session 158 // section. 159 type SSOSession struct { 160 Name string 161 SSORegion string 162 SSOStartURL string 163 } 164 165 func (s *SSOSession) setFromIniSection(section ini.Section) { 166 updateString(&s.Name, section, ssoSessionNameKey) 167 updateString(&s.SSORegion, section, ssoRegionKey) 168 updateString(&s.SSOStartURL, section, ssoStartURLKey) 169 } 170 171 // Services contains values configured in the services section 172 // of the AWS configuration file. 173 type Services struct { 174 // Services section values 175 // {"serviceId": {"key": "value"}} 176 // e.g. {"s3": {"endpoint_url": "example.com"}} 177 ServiceValues map[string]map[string]string 178 } 179 180 func (s *Services) setFromIniSection(section ini.Section) { 181 if s.ServiceValues == nil { 182 s.ServiceValues = make(map[string]map[string]string) 183 } 184 for _, service := range section.List() { 185 s.ServiceValues[service] = section.Map(service) 186 } 187 } 188 189 // SharedConfig represents the configuration fields of the SDK config files. 190 type SharedConfig struct { 191 Profile string 192 193 // Credentials values from the config file. Both aws_access_key_id 194 // and aws_secret_access_key must be provided together in the same file 195 // to be considered valid. The values will be ignored if not a complete group. 196 // aws_session_token is an optional field that can be provided if both of the 197 // other two fields are also provided. 198 // 199 // aws_access_key_id 200 // aws_secret_access_key 201 // aws_session_token 202 Credentials aws.Credentials 203 204 CredentialSource string 205 CredentialProcess string 206 WebIdentityTokenFile string 207 208 // SSO session options 209 SSOSessionName string 210 SSOSession *SSOSession 211 212 // Legacy SSO session options 213 SSORegion string 214 SSOStartURL string 215 216 // SSO fields not used 217 SSOAccountID string 218 SSORoleName string 219 220 RoleARN string 221 ExternalID string 222 MFASerial string 223 RoleSessionName string 224 RoleDurationSeconds *time.Duration 225 226 SourceProfileName string 227 Source *SharedConfig 228 229 // Region is the region the SDK should use for looking up AWS service endpoints 230 // and signing requests. 231 // 232 // region = us-west-2 233 Region string 234 235 // EnableEndpointDiscovery can be enabled or disabled in the shared config 236 // by setting endpoint_discovery_enabled to true, or false respectively. 237 // 238 // endpoint_discovery_enabled = true 239 EnableEndpointDiscovery aws.EndpointDiscoveryEnableState 240 241 // Specifies if the S3 service should allow ARNs to direct the region 242 // the client's requests are sent to. 243 // 244 // s3_use_arn_region=true 245 S3UseARNRegion *bool 246 247 // Specifies the EC2 Instance Metadata Service default endpoint selection 248 // mode (IPv4 or IPv6) 249 // 250 // ec2_metadata_service_endpoint_mode=IPv6 251 EC2IMDSEndpointMode imds.EndpointModeState 252 253 // Specifies the EC2 Instance Metadata Service endpoint to use. If 254 // specified it overrides EC2IMDSEndpointMode. 255 // 256 // ec2_metadata_service_endpoint=http://fd00:ec2::254 257 EC2IMDSEndpoint string 258 259 // Specifies that IMDS clients should not fallback to IMDSv1 if token 260 // requests fail. 261 // 262 // ec2_metadata_v1_disabled=true 263 EC2IMDSv1Disabled *bool 264 265 // Specifies if the S3 service should disable support for Multi-Region 266 // access-points 267 // 268 // s3_disable_multiregion_access_points=true 269 S3DisableMultiRegionAccessPoints *bool 270 271 // Specifies that SDK clients must resolve a dual-stack endpoint for 272 // services. 273 // 274 // use_dualstack_endpoint=true 275 UseDualStackEndpoint aws.DualStackEndpointState 276 277 // Specifies that SDK clients must resolve a FIPS endpoint for 278 // services. 279 // 280 // use_fips_endpoint=true 281 UseFIPSEndpoint aws.FIPSEndpointState 282 283 // Specifies which defaults mode should be used by services. 284 // 285 // defaults_mode=standard 286 DefaultsMode aws.DefaultsMode 287 288 // Specifies the maximum number attempts an API client will call an 289 // operation that fails with a retryable error. 290 // 291 // max_attempts=3 292 RetryMaxAttempts int 293 294 // Specifies the retry model the API client will be created with. 295 // 296 // retry_mode=standard 297 RetryMode aws.RetryMode 298 299 // Sets the path to a custom Credentials Authority (CA) Bundle PEM file 300 // that the SDK will use instead of the system's root CA bundle. Only use 301 // this if you want to configure the SDK to use a custom set of CAs. 302 // 303 // Enabling this option will attempt to merge the Transport into the SDK's 304 // HTTP client. If the client's Transport is not a http.Transport an error 305 // will be returned. If the Transport's TLS config is set this option will 306 // cause the SDK to overwrite the Transport's TLS config's RootCAs value. 307 // 308 // Setting a custom HTTPClient in the aws.Config options will override this 309 // setting. To use this option and custom HTTP client, the HTTP client 310 // needs to be provided when creating the config. Not the service client. 311 // 312 // ca_bundle=$HOME/my_custom_ca_bundle 313 CustomCABundle string 314 315 // aws sdk app ID that can be added to user agent header string 316 AppID string 317 318 // Flag used to disable configured endpoints. 319 IgnoreConfiguredEndpoints *bool 320 321 // Value to contain configured endpoints to be propagated to 322 // corresponding endpoint resolution field. 323 BaseEndpoint string 324 325 // Services section config. 326 ServicesSectionName string 327 Services Services 328 329 // determine if request compression is allowed, default to false 330 // retrieved from config file's profile field disable_request_compression 331 DisableRequestCompression *bool 332 333 // inclusive threshold request body size to trigger compression, 334 // default to 10240 and must be within 0 and 10485760 bytes inclusive 335 // retrieved from config file's profile field request_min_compression_size_bytes 336 RequestMinCompressSizeBytes *int64 337 338 // Whether S3Express auth is disabled. 339 // 340 // This will NOT prevent requests from being made to S3Express buckets, it 341 // will only bypass the modified endpoint routing and signing behaviors 342 // associated with the feature. 343 S3DisableExpressAuth *bool 344 } 345 346 func (c SharedConfig) getDefaultsMode(ctx context.Context) (value aws.DefaultsMode, ok bool, err error) { 347 if len(c.DefaultsMode) == 0 { 348 return "", false, nil 349 } 350 351 return c.DefaultsMode, true, nil 352 } 353 354 // GetRetryMaxAttempts returns the maximum number of attempts an API client 355 // created Retryer should attempt an operation call before failing. 356 func (c SharedConfig) GetRetryMaxAttempts(ctx context.Context) (value int, ok bool, err error) { 357 if c.RetryMaxAttempts == 0 { 358 return 0, false, nil 359 } 360 361 return c.RetryMaxAttempts, true, nil 362 } 363 364 // GetRetryMode returns the model the API client should create its Retryer in. 365 func (c SharedConfig) GetRetryMode(ctx context.Context) (value aws.RetryMode, ok bool, err error) { 366 if len(c.RetryMode) == 0 { 367 return "", false, nil 368 } 369 370 return c.RetryMode, true, nil 371 } 372 373 // GetS3UseARNRegion returns if the S3 service should allow ARNs to direct the region 374 // the client's requests are sent to. 375 func (c SharedConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) { 376 if c.S3UseARNRegion == nil { 377 return false, false, nil 378 } 379 380 return *c.S3UseARNRegion, true, nil 381 } 382 383 // GetEnableEndpointDiscovery returns if the enable_endpoint_discovery is set. 384 func (c SharedConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, ok bool, err error) { 385 if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset { 386 return aws.EndpointDiscoveryUnset, false, nil 387 } 388 389 return c.EnableEndpointDiscovery, true, nil 390 } 391 392 // GetS3DisableMultiRegionAccessPoints returns if the S3 service should disable support for Multi-Region 393 // access-points. 394 func (c SharedConfig) GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value, ok bool, err error) { 395 if c.S3DisableMultiRegionAccessPoints == nil { 396 return false, false, nil 397 } 398 399 return *c.S3DisableMultiRegionAccessPoints, true, nil 400 } 401 402 // GetRegion returns the region for the profile if a region is set. 403 func (c SharedConfig) getRegion(ctx context.Context) (string, bool, error) { 404 if len(c.Region) == 0 { 405 return "", false, nil 406 } 407 return c.Region, true, nil 408 } 409 410 // GetCredentialsProvider returns the credentials for a profile if they were set. 411 func (c SharedConfig) getCredentialsProvider() (aws.Credentials, bool, error) { 412 return c.Credentials, true, nil 413 } 414 415 // GetEC2IMDSEndpointMode implements a EC2IMDSEndpointMode option resolver interface. 416 func (c SharedConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) { 417 if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset { 418 return imds.EndpointModeStateUnset, false, nil 419 } 420 421 return c.EC2IMDSEndpointMode, true, nil 422 } 423 424 // GetEC2IMDSEndpoint implements a EC2IMDSEndpoint option resolver interface. 425 func (c SharedConfig) GetEC2IMDSEndpoint() (string, bool, error) { 426 if len(c.EC2IMDSEndpoint) == 0 { 427 return "", false, nil 428 } 429 430 return c.EC2IMDSEndpoint, true, nil 431 } 432 433 // GetEC2IMDSV1FallbackDisabled implements an EC2IMDSV1FallbackDisabled option 434 // resolver interface. 435 func (c SharedConfig) GetEC2IMDSV1FallbackDisabled() (bool, bool) { 436 if c.EC2IMDSv1Disabled == nil { 437 return false, false 438 } 439 440 return *c.EC2IMDSv1Disabled, true 441 } 442 443 // GetUseDualStackEndpoint returns whether the service's dual-stack endpoint should be 444 // used for requests. 445 func (c SharedConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) { 446 if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset { 447 return aws.DualStackEndpointStateUnset, false, nil 448 } 449 450 return c.UseDualStackEndpoint, true, nil 451 } 452 453 // GetUseFIPSEndpoint returns whether the service's FIPS endpoint should be 454 // used for requests. 455 func (c SharedConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) { 456 if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset { 457 return aws.FIPSEndpointStateUnset, false, nil 458 } 459 460 return c.UseFIPSEndpoint, true, nil 461 } 462 463 // GetS3DisableExpressAuth returns the configured value for 464 // [SharedConfig.S3DisableExpressAuth]. 465 func (c SharedConfig) GetS3DisableExpressAuth() (value, ok bool) { 466 if c.S3DisableExpressAuth == nil { 467 return false, false 468 } 469 470 return *c.S3DisableExpressAuth, true 471 } 472 473 // GetCustomCABundle returns the custom CA bundle's PEM bytes if the file was 474 func (c SharedConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) { 475 if len(c.CustomCABundle) == 0 { 476 return nil, false, nil 477 } 478 479 b, err := ioutil.ReadFile(c.CustomCABundle) 480 if err != nil { 481 return nil, false, err 482 } 483 return bytes.NewReader(b), true, nil 484 } 485 486 // getAppID returns the sdk app ID if set in shared config profile 487 func (c SharedConfig) getAppID(context.Context) (string, bool, error) { 488 return c.AppID, len(c.AppID) > 0, nil 489 } 490 491 // GetIgnoreConfiguredEndpoints is used in knowing when to disable configured 492 // endpoints feature. 493 func (c SharedConfig) GetIgnoreConfiguredEndpoints(context.Context) (bool, bool, error) { 494 if c.IgnoreConfiguredEndpoints == nil { 495 return false, false, nil 496 } 497 498 return *c.IgnoreConfiguredEndpoints, true, nil 499 } 500 501 func (c SharedConfig) getBaseEndpoint(context.Context) (string, bool, error) { 502 return c.BaseEndpoint, len(c.BaseEndpoint) > 0, nil 503 } 504 505 // GetServiceBaseEndpoint is used to retrieve a normalized SDK ID for use 506 // with configured endpoints. 507 func (c SharedConfig) GetServiceBaseEndpoint(ctx context.Context, sdkID string) (string, bool, error) { 508 if service, ok := c.Services.ServiceValues[normalizeShared(sdkID)]; ok { 509 if endpt, ok := service[endpointURL]; ok { 510 return endpt, true, nil 511 } 512 } 513 return "", false, nil 514 } 515 516 func normalizeShared(sdkID string) string { 517 lower := strings.ToLower(sdkID) 518 return strings.ReplaceAll(lower, " ", "_") 519 } 520 521 func (c SharedConfig) getServicesObject(context.Context) (map[string]map[string]string, bool, error) { 522 return c.Services.ServiceValues, c.Services.ServiceValues != nil, nil 523 } 524 525 // loadSharedConfigIgnoreNotExist is an alias for loadSharedConfig with the 526 // addition of ignoring when none of the files exist or when the profile 527 // is not found in any of the files. 528 func loadSharedConfigIgnoreNotExist(ctx context.Context, configs configs) (Config, error) { 529 cfg, err := loadSharedConfig(ctx, configs) 530 if err != nil { 531 if _, ok := err.(SharedConfigProfileNotExistError); ok { 532 return SharedConfig{}, nil 533 } 534 return nil, err 535 } 536 537 return cfg, nil 538 } 539 540 // loadSharedConfig uses the configs passed in to load the SharedConfig from file 541 // The file names and profile name are sourced from the configs. 542 // 543 // If profile name is not provided DefaultSharedConfigProfile (default) will 544 // be used. 545 // 546 // If shared config filenames are not provided DefaultSharedConfigFiles will 547 // be used. 548 // 549 // Config providers used: 550 // * sharedConfigProfileProvider 551 // * sharedConfigFilesProvider 552 func loadSharedConfig(ctx context.Context, configs configs) (Config, error) { 553 var profile string 554 var configFiles []string 555 var credentialsFiles []string 556 var ok bool 557 var err error 558 559 profile, ok, err = getSharedConfigProfile(ctx, configs) 560 if err != nil { 561 return nil, err 562 } 563 if !ok { 564 profile = defaultSharedConfigProfile 565 } 566 567 configFiles, ok, err = getSharedConfigFiles(ctx, configs) 568 if err != nil { 569 return nil, err 570 } 571 572 credentialsFiles, ok, err = getSharedCredentialsFiles(ctx, configs) 573 if err != nil { 574 return nil, err 575 } 576 577 // setup logger if log configuration warning is seti 578 var logger logging.Logger 579 logWarnings, found, err := getLogConfigurationWarnings(ctx, configs) 580 if err != nil { 581 return SharedConfig{}, err 582 } 583 if found && logWarnings { 584 logger, found, err = getLogger(ctx, configs) 585 if err != nil { 586 return SharedConfig{}, err 587 } 588 if !found { 589 logger = logging.NewStandardLogger(os.Stderr) 590 } 591 } 592 593 return LoadSharedConfigProfile(ctx, profile, 594 func(o *LoadSharedConfigOptions) { 595 o.Logger = logger 596 o.ConfigFiles = configFiles 597 o.CredentialsFiles = credentialsFiles 598 }, 599 ) 600 } 601 602 // LoadSharedConfigOptions struct contains optional values that can be used to load the config. 603 type LoadSharedConfigOptions struct { 604 605 // CredentialsFiles are the shared credentials files 606 CredentialsFiles []string 607 608 // ConfigFiles are the shared config files 609 ConfigFiles []string 610 611 // Logger is the logger used to log shared config behavior 612 Logger logging.Logger 613 } 614 615 // LoadSharedConfigProfile retrieves the configuration from the list of files 616 // using the profile provided. The order the files are listed will determine 617 // precedence. Values in subsequent files will overwrite values defined in 618 // earlier files. 619 // 620 // For example, given two files A and B. Both define credentials. If the order 621 // of the files are A then B, B's credential values will be used instead of A's. 622 // 623 // If config files are not set, SDK will default to using a file at location `.aws/config` if present. 624 // If credentials files are not set, SDK will default to using a file at location `.aws/credentials` if present. 625 // No default files are set, if files set to an empty slice. 626 // 627 // You can read more about shared config and credentials file location at 628 // https://docs.aws.amazon.com/credref/latest/refdocs/file-location.html#file-location 629 func LoadSharedConfigProfile(ctx context.Context, profile string, optFns ...func(*LoadSharedConfigOptions)) (SharedConfig, error) { 630 var option LoadSharedConfigOptions 631 for _, fn := range optFns { 632 fn(&option) 633 } 634 635 if option.ConfigFiles == nil { 636 option.ConfigFiles = DefaultSharedConfigFiles 637 } 638 639 if option.CredentialsFiles == nil { 640 option.CredentialsFiles = DefaultSharedCredentialsFiles 641 } 642 643 // load shared configuration sections from shared configuration INI options 644 configSections, err := loadIniFiles(option.ConfigFiles) 645 if err != nil { 646 return SharedConfig{}, err 647 } 648 649 // check for profile prefix and drop duplicates or invalid profiles 650 err = processConfigSections(ctx, &configSections, option.Logger) 651 if err != nil { 652 return SharedConfig{}, err 653 } 654 655 // load shared credentials sections from shared credentials INI options 656 credentialsSections, err := loadIniFiles(option.CredentialsFiles) 657 if err != nil { 658 return SharedConfig{}, err 659 } 660 661 // check for profile prefix and drop duplicates or invalid profiles 662 err = processCredentialsSections(ctx, &credentialsSections, option.Logger) 663 if err != nil { 664 return SharedConfig{}, err 665 } 666 667 err = mergeSections(&configSections, credentialsSections) 668 if err != nil { 669 return SharedConfig{}, err 670 } 671 672 cfg := SharedConfig{} 673 profiles := map[string]struct{}{} 674 675 if err = cfg.setFromIniSections(profiles, profile, configSections, option.Logger); err != nil { 676 return SharedConfig{}, err 677 } 678 679 return cfg, nil 680 } 681 682 func processConfigSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error { 683 skipSections := map[string]struct{}{} 684 685 for _, section := range sections.List() { 686 if _, ok := skipSections[section]; ok { 687 continue 688 } 689 690 // drop sections from config file that do not have expected prefixes. 691 switch { 692 case strings.HasPrefix(section, profilePrefix): 693 // Rename sections to remove "profile " prefixing to match with 694 // credentials file. If default is already present, it will be 695 // dropped. 696 newName, err := renameProfileSection(section, sections, logger) 697 if err != nil { 698 return fmt.Errorf("failed to rename profile section, %w", err) 699 } 700 skipSections[newName] = struct{}{} 701 702 case strings.HasPrefix(section, ssoSectionPrefix): 703 case strings.HasPrefix(section, servicesPrefix): 704 case strings.EqualFold(section, "default"): 705 default: 706 // drop this section, as invalid profile name 707 sections.DeleteSection(section) 708 709 if logger != nil { 710 logger.Logf(logging.Debug, "A profile defined with name `%v` is ignored. "+ 711 "For use within a shared configuration file, "+ 712 "a non-default profile must have `profile ` "+ 713 "prefixed to the profile name.", 714 section, 715 ) 716 } 717 } 718 } 719 return nil 720 } 721 722 func renameProfileSection(section string, sections *ini.Sections, logger logging.Logger) (string, error) { 723 v, ok := sections.GetSection(section) 724 if !ok { 725 return "", fmt.Errorf("error processing profiles within the shared configuration files") 726 } 727 728 // delete section with profile as prefix 729 sections.DeleteSection(section) 730 731 // set the value to non-prefixed name in sections. 732 section = strings.TrimPrefix(section, profilePrefix) 733 if sections.HasSection(section) { 734 oldSection, _ := sections.GetSection(section) 735 v.Logs = append(v.Logs, 736 fmt.Sprintf("A non-default profile not prefixed with `profile ` found in %s, "+ 737 "overriding non-default profile from %s", 738 v.SourceFile, oldSection.SourceFile)) 739 sections.DeleteSection(section) 740 } 741 742 // assign non-prefixed name to section 743 v.Name = section 744 sections.SetSection(section, v) 745 746 return section, nil 747 } 748 749 func processCredentialsSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error { 750 for _, section := range sections.List() { 751 // drop profiles with prefix for credential files 752 if strings.HasPrefix(section, profilePrefix) { 753 // drop this section, as invalid profile name 754 sections.DeleteSection(section) 755 756 if logger != nil { 757 logger.Logf(logging.Debug, 758 "The profile defined with name `%v` is ignored. A profile with the `profile ` prefix is invalid "+ 759 "for the shared credentials file.\n", 760 section, 761 ) 762 } 763 } 764 } 765 return nil 766 } 767 768 func loadIniFiles(filenames []string) (ini.Sections, error) { 769 mergedSections := ini.NewSections() 770 771 for _, filename := range filenames { 772 sections, err := ini.OpenFile(filename) 773 var v *ini.UnableToReadFile 774 if ok := errors.As(err, &v); ok { 775 // Skip files which can't be opened and read for whatever reason. 776 // We treat such files as empty, and do not fall back to other locations. 777 continue 778 } else if err != nil { 779 return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err} 780 } 781 782 // mergeSections into mergedSections 783 err = mergeSections(&mergedSections, sections) 784 if err != nil { 785 return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err} 786 } 787 } 788 789 return mergedSections, nil 790 } 791 792 // mergeSections merges source section properties into destination section properties 793 func mergeSections(dst *ini.Sections, src ini.Sections) error { 794 for _, sectionName := range src.List() { 795 srcSection, _ := src.GetSection(sectionName) 796 797 if (!srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey)) || 798 (srcSection.Has(accessKeyIDKey) && !srcSection.Has(secretAccessKey)) { 799 srcSection.Errors = append(srcSection.Errors, 800 fmt.Errorf("partial credentials found for profile %v", sectionName)) 801 } 802 803 if !dst.HasSection(sectionName) { 804 dst.SetSection(sectionName, srcSection) 805 continue 806 } 807 808 // merge with destination srcSection 809 dstSection, _ := dst.GetSection(sectionName) 810 811 // errors should be overriden if any 812 dstSection.Errors = srcSection.Errors 813 814 // Access key id update 815 if srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey) { 816 accessKey := srcSection.String(accessKeyIDKey) 817 secretKey := srcSection.String(secretAccessKey) 818 819 if dstSection.Has(accessKeyIDKey) { 820 dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, accessKeyIDKey, 821 dstSection.SourceFile[accessKeyIDKey], srcSection.SourceFile[accessKeyIDKey])) 822 } 823 824 // update access key 825 v, err := ini.NewStringValue(accessKey) 826 if err != nil { 827 return fmt.Errorf("error merging access key, %w", err) 828 } 829 dstSection.UpdateValue(accessKeyIDKey, v) 830 831 // update secret key 832 v, err = ini.NewStringValue(secretKey) 833 if err != nil { 834 return fmt.Errorf("error merging secret key, %w", err) 835 } 836 dstSection.UpdateValue(secretAccessKey, v) 837 838 // update session token 839 if err = mergeStringKey(&srcSection, &dstSection, sectionName, sessionTokenKey); err != nil { 840 return err 841 } 842 843 // update source file to reflect where the static creds came from 844 dstSection.UpdateSourceFile(accessKeyIDKey, srcSection.SourceFile[accessKeyIDKey]) 845 dstSection.UpdateSourceFile(secretAccessKey, srcSection.SourceFile[secretAccessKey]) 846 } 847 848 stringKeys := []string{ 849 roleArnKey, 850 sourceProfileKey, 851 credentialSourceKey, 852 externalIDKey, 853 mfaSerialKey, 854 roleSessionNameKey, 855 regionKey, 856 enableEndpointDiscoveryKey, 857 credentialProcessKey, 858 webIdentityTokenFileKey, 859 s3UseARNRegionKey, 860 s3DisableMultiRegionAccessPointsKey, 861 ec2MetadataServiceEndpointModeKey, 862 ec2MetadataServiceEndpointKey, 863 ec2MetadataV1DisabledKey, 864 useDualStackEndpoint, 865 useFIPSEndpointKey, 866 defaultsModeKey, 867 retryModeKey, 868 caBundleKey, 869 roleDurationSecondsKey, 870 retryMaxAttemptsKey, 871 872 ssoSessionNameKey, 873 ssoAccountIDKey, 874 ssoRegionKey, 875 ssoRoleNameKey, 876 ssoStartURLKey, 877 } 878 for i := range stringKeys { 879 if err := mergeStringKey(&srcSection, &dstSection, sectionName, stringKeys[i]); err != nil { 880 return err 881 } 882 } 883 884 // set srcSection on dst srcSection 885 *dst = dst.SetSection(sectionName, dstSection) 886 } 887 888 return nil 889 } 890 891 func mergeStringKey(srcSection *ini.Section, dstSection *ini.Section, sectionName, key string) error { 892 if srcSection.Has(key) { 893 srcValue := srcSection.String(key) 894 val, err := ini.NewStringValue(srcValue) 895 if err != nil { 896 return fmt.Errorf("error merging %s, %w", key, err) 897 } 898 899 if dstSection.Has(key) { 900 dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, key, 901 dstSection.SourceFile[key], srcSection.SourceFile[key])) 902 } 903 904 dstSection.UpdateValue(key, val) 905 dstSection.UpdateSourceFile(key, srcSection.SourceFile[key]) 906 } 907 return nil 908 } 909 910 func newMergeKeyLogMessage(sectionName, key, dstSourceFile, srcSourceFile string) string { 911 return fmt.Sprintf("For profile: %v, overriding %v value, defined in %v "+ 912 "with a %v value found in a duplicate profile defined at file %v. \n", 913 sectionName, key, dstSourceFile, key, srcSourceFile) 914 } 915 916 // Returns an error if all of the files fail to load. If at least one file is 917 // successfully loaded and contains the profile, no error will be returned. 918 func (c *SharedConfig) setFromIniSections(profiles map[string]struct{}, profile string, 919 sections ini.Sections, logger logging.Logger) error { 920 c.Profile = profile 921 922 section, ok := sections.GetSection(profile) 923 if !ok { 924 return SharedConfigProfileNotExistError{ 925 Profile: profile, 926 } 927 } 928 929 // if logs are appended to the section, log them 930 if section.Logs != nil && logger != nil { 931 for _, log := range section.Logs { 932 logger.Logf(logging.Debug, log) 933 } 934 } 935 936 // set config from the provided INI section 937 err := c.setFromIniSection(profile, section) 938 if err != nil { 939 return fmt.Errorf("error fetching config from profile, %v, %w", profile, err) 940 } 941 942 if _, ok := profiles[profile]; ok { 943 // if this is the second instance of the profile the Assume Role 944 // options must be cleared because they are only valid for the 945 // first reference of a profile. The self linked instance of the 946 // profile only have credential provider options. 947 c.clearAssumeRoleOptions() 948 } else { 949 // First time a profile has been seen. Assert if the credential type 950 // requires a role ARN, the ARN is also set 951 if err := c.validateCredentialsConfig(profile); err != nil { 952 return err 953 } 954 } 955 956 // if not top level profile and has credentials, return with credentials. 957 if len(profiles) != 0 && c.Credentials.HasKeys() { 958 return nil 959 } 960 961 profiles[profile] = struct{}{} 962 963 // validate no colliding credentials type are present 964 if err := c.validateCredentialType(); err != nil { 965 return err 966 } 967 968 // Link source profiles for assume roles 969 if len(c.SourceProfileName) != 0 { 970 // Linked profile via source_profile ignore credential provider 971 // options, the source profile must provide the credentials. 972 c.clearCredentialOptions() 973 974 srcCfg := &SharedConfig{} 975 err := srcCfg.setFromIniSections(profiles, c.SourceProfileName, sections, logger) 976 if err != nil { 977 // SourceProfileName that doesn't exist is an error in configuration. 978 if _, ok := err.(SharedConfigProfileNotExistError); ok { 979 err = SharedConfigAssumeRoleError{ 980 RoleARN: c.RoleARN, 981 Profile: c.SourceProfileName, 982 Err: err, 983 } 984 } 985 return err 986 } 987 988 if !srcCfg.hasCredentials() { 989 return SharedConfigAssumeRoleError{ 990 RoleARN: c.RoleARN, 991 Profile: c.SourceProfileName, 992 } 993 } 994 995 c.Source = srcCfg 996 } 997 998 // If the profile contains an SSO session parameter, the session MUST exist 999 // as a section in the config file. Load the SSO session using the name 1000 // provided. If the session section is not found or incomplete an error 1001 // will be returned. 1002 if c.hasSSOTokenProviderConfiguration() { 1003 section, ok := sections.GetSection(ssoSectionPrefix + strings.TrimSpace(c.SSOSessionName)) 1004 if !ok { 1005 return fmt.Errorf("failed to find SSO session section, %v", c.SSOSessionName) 1006 } 1007 var ssoSession SSOSession 1008 ssoSession.setFromIniSection(section) 1009 ssoSession.Name = c.SSOSessionName 1010 c.SSOSession = &ssoSession 1011 } 1012 1013 if len(c.ServicesSectionName) > 0 { 1014 if section, ok := sections.GetSection(servicesPrefix + c.ServicesSectionName); ok { 1015 var svcs Services 1016 svcs.setFromIniSection(section) 1017 c.Services = svcs 1018 } 1019 } 1020 1021 return nil 1022 } 1023 1024 // setFromIniSection loads the configuration from the profile section defined in 1025 // the provided INI file. A SharedConfig pointer type value is used so that 1026 // multiple config file loadings can be chained. 1027 // 1028 // Only loads complete logically grouped values, and will not set fields in cfg 1029 // for incomplete grouped values in the config. Such as credentials. For example 1030 // if a config file only includes aws_access_key_id but no aws_secret_access_key 1031 // the aws_access_key_id will be ignored. 1032 func (c *SharedConfig) setFromIniSection(profile string, section ini.Section) error { 1033 if len(section.Name) == 0 { 1034 sources := make([]string, 0) 1035 for _, v := range section.SourceFile { 1036 sources = append(sources, v) 1037 } 1038 1039 return fmt.Errorf("parsing error : could not find profile section name after processing files: %v", sources) 1040 } 1041 1042 if len(section.Errors) != 0 { 1043 var errStatement string 1044 for i, e := range section.Errors { 1045 errStatement = fmt.Sprintf("%d, %v\n", i+1, e.Error()) 1046 } 1047 return fmt.Errorf("Error using profile: \n %v", errStatement) 1048 } 1049 1050 // Assume Role 1051 updateString(&c.RoleARN, section, roleArnKey) 1052 updateString(&c.ExternalID, section, externalIDKey) 1053 updateString(&c.MFASerial, section, mfaSerialKey) 1054 updateString(&c.RoleSessionName, section, roleSessionNameKey) 1055 updateString(&c.SourceProfileName, section, sourceProfileKey) 1056 updateString(&c.CredentialSource, section, credentialSourceKey) 1057 updateString(&c.Region, section, regionKey) 1058 1059 // AWS Single Sign-On (AWS SSO) 1060 // SSO session options 1061 updateString(&c.SSOSessionName, section, ssoSessionNameKey) 1062 1063 // Legacy SSO session options 1064 updateString(&c.SSORegion, section, ssoRegionKey) 1065 updateString(&c.SSOStartURL, section, ssoStartURLKey) 1066 1067 // SSO fields not used 1068 updateString(&c.SSOAccountID, section, ssoAccountIDKey) 1069 updateString(&c.SSORoleName, section, ssoRoleNameKey) 1070 1071 // we're retaining a behavioral quirk with this field that existed before 1072 // the removal of literal parsing for #2276: 1073 // - if the key is missing, the config field will not be set 1074 // - if the key is set to a non-numeric, the config field will be set to 0 1075 if section.Has(roleDurationSecondsKey) { 1076 if v, ok := section.Int(roleDurationSecondsKey); ok { 1077 c.RoleDurationSeconds = aws.Duration(time.Duration(v) * time.Second) 1078 } else { 1079 c.RoleDurationSeconds = aws.Duration(time.Duration(0)) 1080 } 1081 } 1082 1083 updateString(&c.CredentialProcess, section, credentialProcessKey) 1084 updateString(&c.WebIdentityTokenFile, section, webIdentityTokenFileKey) 1085 1086 updateEndpointDiscoveryType(&c.EnableEndpointDiscovery, section, enableEndpointDiscoveryKey) 1087 updateBoolPtr(&c.S3UseARNRegion, section, s3UseARNRegionKey) 1088 updateBoolPtr(&c.S3DisableMultiRegionAccessPoints, section, s3DisableMultiRegionAccessPointsKey) 1089 updateBoolPtr(&c.S3DisableExpressAuth, section, s3DisableExpressSessionAuthKey) 1090 1091 if err := updateEC2MetadataServiceEndpointMode(&c.EC2IMDSEndpointMode, section, ec2MetadataServiceEndpointModeKey); err != nil { 1092 return fmt.Errorf("failed to load %s from shared config, %v", ec2MetadataServiceEndpointModeKey, err) 1093 } 1094 updateString(&c.EC2IMDSEndpoint, section, ec2MetadataServiceEndpointKey) 1095 updateBoolPtr(&c.EC2IMDSv1Disabled, section, ec2MetadataV1DisabledKey) 1096 1097 updateUseDualStackEndpoint(&c.UseDualStackEndpoint, section, useDualStackEndpoint) 1098 updateUseFIPSEndpoint(&c.UseFIPSEndpoint, section, useFIPSEndpointKey) 1099 1100 if err := updateDefaultsMode(&c.DefaultsMode, section, defaultsModeKey); err != nil { 1101 return fmt.Errorf("failed to load %s from shared config, %w", defaultsModeKey, err) 1102 } 1103 1104 if err := updateInt(&c.RetryMaxAttempts, section, retryMaxAttemptsKey); err != nil { 1105 return fmt.Errorf("failed to load %s from shared config, %w", retryMaxAttemptsKey, err) 1106 } 1107 if err := updateRetryMode(&c.RetryMode, section, retryModeKey); err != nil { 1108 return fmt.Errorf("failed to load %s from shared config, %w", retryModeKey, err) 1109 } 1110 1111 updateString(&c.CustomCABundle, section, caBundleKey) 1112 1113 // user agent app ID added to request User-Agent header 1114 updateString(&c.AppID, section, sdkAppID) 1115 1116 updateBoolPtr(&c.IgnoreConfiguredEndpoints, section, ignoreConfiguredEndpoints) 1117 1118 updateString(&c.BaseEndpoint, section, endpointURL) 1119 1120 if err := updateDisableRequestCompression(&c.DisableRequestCompression, section, disableRequestCompression); err != nil { 1121 return fmt.Errorf("failed to load %s from shared config, %w", disableRequestCompression, err) 1122 } 1123 if err := updateRequestMinCompressSizeBytes(&c.RequestMinCompressSizeBytes, section, requestMinCompressionSizeBytes); err != nil { 1124 return fmt.Errorf("failed to load %s from shared config, %w", requestMinCompressionSizeBytes, err) 1125 } 1126 1127 // Shared Credentials 1128 creds := aws.Credentials{ 1129 AccessKeyID: section.String(accessKeyIDKey), 1130 SecretAccessKey: section.String(secretAccessKey), 1131 SessionToken: section.String(sessionTokenKey), 1132 Source: fmt.Sprintf("SharedConfigCredentials: %s", section.SourceFile[accessKeyIDKey]), 1133 } 1134 1135 if creds.HasKeys() { 1136 c.Credentials = creds 1137 } 1138 1139 updateString(&c.ServicesSectionName, section, servicesSectionKey) 1140 1141 return nil 1142 } 1143 1144 func updateRequestMinCompressSizeBytes(bytes **int64, sec ini.Section, key string) error { 1145 if !sec.Has(key) { 1146 return nil 1147 } 1148 1149 v, ok := sec.Int(key) 1150 if !ok { 1151 return fmt.Errorf("invalid value for min request compression size bytes %s, need int64", sec.String(key)) 1152 } 1153 if v < 0 || v > smithyrequestcompression.MaxRequestMinCompressSizeBytes { 1154 return fmt.Errorf("invalid range for min request compression size bytes %d, must be within 0 and 10485760 inclusively", v) 1155 } 1156 *bytes = new(int64) 1157 **bytes = v 1158 return nil 1159 } 1160 1161 func updateDisableRequestCompression(disable **bool, sec ini.Section, key string) error { 1162 if !sec.Has(key) { 1163 return nil 1164 } 1165 1166 v := sec.String(key) 1167 switch { 1168 case v == "true": 1169 *disable = new(bool) 1170 **disable = true 1171 case v == "false": 1172 *disable = new(bool) 1173 **disable = false 1174 default: 1175 return fmt.Errorf("invalid value for shared config profile field, %s=%s, need true or false", key, v) 1176 } 1177 return nil 1178 } 1179 1180 func (c SharedConfig) getRequestMinCompressSizeBytes(ctx context.Context) (int64, bool, error) { 1181 if c.RequestMinCompressSizeBytes == nil { 1182 return 0, false, nil 1183 } 1184 return *c.RequestMinCompressSizeBytes, true, nil 1185 } 1186 1187 func (c SharedConfig) getDisableRequestCompression(ctx context.Context) (bool, bool, error) { 1188 if c.DisableRequestCompression == nil { 1189 return false, false, nil 1190 } 1191 return *c.DisableRequestCompression, true, nil 1192 } 1193 1194 func updateDefaultsMode(mode *aws.DefaultsMode, section ini.Section, key string) error { 1195 if !section.Has(key) { 1196 return nil 1197 } 1198 value := section.String(key) 1199 if ok := mode.SetFromString(value); !ok { 1200 return fmt.Errorf("invalid value: %s", value) 1201 } 1202 return nil 1203 } 1204 1205 func updateRetryMode(mode *aws.RetryMode, section ini.Section, key string) (err error) { 1206 if !section.Has(key) { 1207 return nil 1208 } 1209 value := section.String(key) 1210 if *mode, err = aws.ParseRetryMode(value); err != nil { 1211 return err 1212 } 1213 return nil 1214 } 1215 1216 func updateEC2MetadataServiceEndpointMode(endpointMode *imds.EndpointModeState, section ini.Section, key string) error { 1217 if !section.Has(key) { 1218 return nil 1219 } 1220 value := section.String(key) 1221 return endpointMode.SetFromString(value) 1222 } 1223 1224 func (c *SharedConfig) validateCredentialsConfig(profile string) error { 1225 if err := c.validateCredentialsRequireARN(profile); err != nil { 1226 return err 1227 } 1228 1229 return nil 1230 } 1231 1232 func (c *SharedConfig) validateCredentialsRequireARN(profile string) error { 1233 var credSource string 1234 1235 switch { 1236 case len(c.SourceProfileName) != 0: 1237 credSource = sourceProfileKey 1238 case len(c.CredentialSource) != 0: 1239 credSource = credentialSourceKey 1240 case len(c.WebIdentityTokenFile) != 0: 1241 credSource = webIdentityTokenFileKey 1242 } 1243 1244 if len(credSource) != 0 && len(c.RoleARN) == 0 { 1245 return CredentialRequiresARNError{ 1246 Type: credSource, 1247 Profile: profile, 1248 } 1249 } 1250 1251 return nil 1252 } 1253 1254 func (c *SharedConfig) validateCredentialType() error { 1255 // Only one or no credential type can be defined. 1256 if !oneOrNone( 1257 len(c.SourceProfileName) != 0, 1258 len(c.CredentialSource) != 0, 1259 len(c.CredentialProcess) != 0, 1260 len(c.WebIdentityTokenFile) != 0, 1261 ) { 1262 return fmt.Errorf("only one credential type may be specified per profile: source profile, credential source, credential process, web identity token") 1263 } 1264 1265 return nil 1266 } 1267 1268 func (c *SharedConfig) validateSSOConfiguration() error { 1269 if c.hasSSOTokenProviderConfiguration() { 1270 err := c.validateSSOTokenProviderConfiguration() 1271 if err != nil { 1272 return err 1273 } 1274 return nil 1275 } 1276 1277 if c.hasLegacySSOConfiguration() { 1278 err := c.validateLegacySSOConfiguration() 1279 if err != nil { 1280 return err 1281 } 1282 } 1283 return nil 1284 } 1285 1286 func (c *SharedConfig) validateSSOTokenProviderConfiguration() error { 1287 var missing []string 1288 1289 if len(c.SSOSessionName) == 0 { 1290 missing = append(missing, ssoSessionNameKey) 1291 } 1292 1293 if c.SSOSession == nil { 1294 missing = append(missing, ssoSectionPrefix) 1295 } else { 1296 if len(c.SSOSession.SSORegion) == 0 { 1297 missing = append(missing, ssoRegionKey) 1298 } 1299 1300 if len(c.SSOSession.SSOStartURL) == 0 { 1301 missing = append(missing, ssoStartURLKey) 1302 } 1303 } 1304 1305 if len(missing) > 0 { 1306 return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s", 1307 c.Profile, strings.Join(missing, ", ")) 1308 } 1309 1310 if len(c.SSORegion) > 0 && c.SSORegion != c.SSOSession.SSORegion { 1311 return fmt.Errorf("%s in profile %q must match %s in %s", ssoRegionKey, c.Profile, ssoRegionKey, ssoSectionPrefix) 1312 } 1313 1314 if len(c.SSOStartURL) > 0 && c.SSOStartURL != c.SSOSession.SSOStartURL { 1315 return fmt.Errorf("%s in profile %q must match %s in %s", ssoStartURLKey, c.Profile, ssoStartURLKey, ssoSectionPrefix) 1316 } 1317 1318 return nil 1319 } 1320 1321 func (c *SharedConfig) validateLegacySSOConfiguration() error { 1322 var missing []string 1323 1324 if len(c.SSORegion) == 0 { 1325 missing = append(missing, ssoRegionKey) 1326 } 1327 1328 if len(c.SSOStartURL) == 0 { 1329 missing = append(missing, ssoStartURLKey) 1330 } 1331 1332 if len(c.SSOAccountID) == 0 { 1333 missing = append(missing, ssoAccountIDKey) 1334 } 1335 1336 if len(c.SSORoleName) == 0 { 1337 missing = append(missing, ssoRoleNameKey) 1338 } 1339 1340 if len(missing) > 0 { 1341 return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s", 1342 c.Profile, strings.Join(missing, ", ")) 1343 } 1344 return nil 1345 } 1346 1347 func (c *SharedConfig) hasCredentials() bool { 1348 switch { 1349 case len(c.SourceProfileName) != 0: 1350 case len(c.CredentialSource) != 0: 1351 case len(c.CredentialProcess) != 0: 1352 case len(c.WebIdentityTokenFile) != 0: 1353 case c.hasSSOConfiguration(): 1354 case c.Credentials.HasKeys(): 1355 default: 1356 return false 1357 } 1358 1359 return true 1360 } 1361 1362 func (c *SharedConfig) hasSSOConfiguration() bool { 1363 return c.hasSSOTokenProviderConfiguration() || c.hasLegacySSOConfiguration() 1364 } 1365 1366 func (c *SharedConfig) hasSSOTokenProviderConfiguration() bool { 1367 return len(c.SSOSessionName) > 0 1368 } 1369 1370 func (c *SharedConfig) hasLegacySSOConfiguration() bool { 1371 return len(c.SSORegion) > 0 || len(c.SSOAccountID) > 0 || len(c.SSOStartURL) > 0 || len(c.SSORoleName) > 0 1372 } 1373 1374 func (c *SharedConfig) clearAssumeRoleOptions() { 1375 c.RoleARN = "" 1376 c.ExternalID = "" 1377 c.MFASerial = "" 1378 c.RoleSessionName = "" 1379 c.SourceProfileName = "" 1380 } 1381 1382 func (c *SharedConfig) clearCredentialOptions() { 1383 c.CredentialSource = "" 1384 c.CredentialProcess = "" 1385 c.WebIdentityTokenFile = "" 1386 c.Credentials = aws.Credentials{} 1387 c.SSOAccountID = "" 1388 c.SSORegion = "" 1389 c.SSORoleName = "" 1390 c.SSOStartURL = "" 1391 } 1392 1393 // SharedConfigLoadError is an error for the shared config file failed to load. 1394 type SharedConfigLoadError struct { 1395 Filename string 1396 Err error 1397 } 1398 1399 // Unwrap returns the underlying error that caused the failure. 1400 func (e SharedConfigLoadError) Unwrap() error { 1401 return e.Err 1402 } 1403 1404 func (e SharedConfigLoadError) Error() string { 1405 return fmt.Sprintf("failed to load shared config file, %s, %v", e.Filename, e.Err) 1406 } 1407 1408 // SharedConfigProfileNotExistError is an error for the shared config when 1409 // the profile was not find in the config file. 1410 type SharedConfigProfileNotExistError struct { 1411 Filename []string 1412 Profile string 1413 Err error 1414 } 1415 1416 // Unwrap returns the underlying error that caused the failure. 1417 func (e SharedConfigProfileNotExistError) Unwrap() error { 1418 return e.Err 1419 } 1420 1421 func (e SharedConfigProfileNotExistError) Error() string { 1422 return fmt.Sprintf("failed to get shared config profile, %s", e.Profile) 1423 } 1424 1425 // SharedConfigAssumeRoleError is an error for the shared config when the 1426 // profile contains assume role information, but that information is invalid 1427 // or not complete. 1428 type SharedConfigAssumeRoleError struct { 1429 Profile string 1430 RoleARN string 1431 Err error 1432 } 1433 1434 // Unwrap returns the underlying error that caused the failure. 1435 func (e SharedConfigAssumeRoleError) Unwrap() error { 1436 return e.Err 1437 } 1438 1439 func (e SharedConfigAssumeRoleError) Error() string { 1440 return fmt.Sprintf("failed to load assume role %s, of profile %s, %v", 1441 e.RoleARN, e.Profile, e.Err) 1442 } 1443 1444 // CredentialRequiresARNError provides the error for shared config credentials 1445 // that are incorrectly configured in the shared config or credentials file. 1446 type CredentialRequiresARNError struct { 1447 // type of credentials that were configured. 1448 Type string 1449 1450 // Profile name the credentials were in. 1451 Profile string 1452 } 1453 1454 // Error satisfies the error interface. 1455 func (e CredentialRequiresARNError) Error() string { 1456 return fmt.Sprintf( 1457 "credential type %s requires role_arn, profile %s", 1458 e.Type, e.Profile, 1459 ) 1460 } 1461 1462 func oneOrNone(bs ...bool) bool { 1463 var count int 1464 1465 for _, b := range bs { 1466 if b { 1467 count++ 1468 if count > 1 { 1469 return false 1470 } 1471 } 1472 } 1473 1474 return true 1475 } 1476 1477 // updateString will only update the dst with the value in the section key, key 1478 // is present in the section. 1479 func updateString(dst *string, section ini.Section, key string) { 1480 if !section.Has(key) { 1481 return 1482 } 1483 *dst = section.String(key) 1484 } 1485 1486 // updateInt will only update the dst with the value in the section key, key 1487 // is present in the section. 1488 // 1489 // Down casts the INI integer value from a int64 to an int, which could be 1490 // different bit size depending on platform. 1491 func updateInt(dst *int, section ini.Section, key string) error { 1492 if !section.Has(key) { 1493 return nil 1494 } 1495 1496 v, ok := section.Int(key) 1497 if !ok { 1498 return fmt.Errorf("invalid value %s=%s, expect integer", key, section.String(key)) 1499 } 1500 1501 *dst = int(v) 1502 return nil 1503 } 1504 1505 // updateBool will only update the dst with the value in the section key, key 1506 // is present in the section. 1507 func updateBool(dst *bool, section ini.Section, key string) { 1508 if !section.Has(key) { 1509 return 1510 } 1511 1512 // retains pre-#2276 behavior where non-bool value would resolve to false 1513 v, _ := section.Bool(key) 1514 *dst = v 1515 } 1516 1517 // updateBoolPtr will only update the dst with the value in the section key, 1518 // key is present in the section. 1519 func updateBoolPtr(dst **bool, section ini.Section, key string) { 1520 if !section.Has(key) { 1521 return 1522 } 1523 1524 // retains pre-#2276 behavior where non-bool value would resolve to false 1525 v, _ := section.Bool(key) 1526 *dst = new(bool) 1527 **dst = v 1528 } 1529 1530 // updateEndpointDiscoveryType will only update the dst with the value in the section, if 1531 // a valid key and corresponding EndpointDiscoveryType is found. 1532 func updateEndpointDiscoveryType(dst *aws.EndpointDiscoveryEnableState, section ini.Section, key string) { 1533 if !section.Has(key) { 1534 return 1535 } 1536 1537 value := section.String(key) 1538 if len(value) == 0 { 1539 return 1540 } 1541 1542 switch { 1543 case strings.EqualFold(value, endpointDiscoveryDisabled): 1544 *dst = aws.EndpointDiscoveryDisabled 1545 case strings.EqualFold(value, endpointDiscoveryEnabled): 1546 *dst = aws.EndpointDiscoveryEnabled 1547 case strings.EqualFold(value, endpointDiscoveryAuto): 1548 *dst = aws.EndpointDiscoveryAuto 1549 } 1550 } 1551 1552 // updateEndpointDiscoveryType will only update the dst with the value in the section, if 1553 // a valid key and corresponding EndpointDiscoveryType is found. 1554 func updateUseDualStackEndpoint(dst *aws.DualStackEndpointState, section ini.Section, key string) { 1555 if !section.Has(key) { 1556 return 1557 } 1558 1559 // retains pre-#2276 behavior where non-bool value would resolve to false 1560 if v, _ := section.Bool(key); v { 1561 *dst = aws.DualStackEndpointStateEnabled 1562 } else { 1563 *dst = aws.DualStackEndpointStateDisabled 1564 } 1565 1566 return 1567 } 1568 1569 // updateEndpointDiscoveryType will only update the dst with the value in the section, if 1570 // a valid key and corresponding EndpointDiscoveryType is found. 1571 func updateUseFIPSEndpoint(dst *aws.FIPSEndpointState, section ini.Section, key string) { 1572 if !section.Has(key) { 1573 return 1574 } 1575 1576 // retains pre-#2276 behavior where non-bool value would resolve to false 1577 if v, _ := section.Bool(key); v { 1578 *dst = aws.FIPSEndpointStateEnabled 1579 } else { 1580 *dst = aws.FIPSEndpointStateDisabled 1581 } 1582 1583 return 1584 }