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