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