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