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